Blame view

drivers/mtd/maps/gpio-addr-flash.c 8.02 KB
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   * drivers/mtd/maps/gpio-addr-flash.c
   *
   * Handle the case where a flash device is mostly addressed using physical
   * line and supplemented by GPIOs.  This way you can hook up say a 8MiB flash
   * to a 2MiB memory range and use the GPIOs to select a particular range.
   *
   * Copyright © 2000 Nicolas Pitre <nico@cam.org>
   * Copyright © 2005-2009 Analog Devices Inc.
   *
   * Enter bugs at http://blackfin.uclinux.org/
   *
   * Licensed under the GPL-2 or later.
   */
f5bae56a5   Mike Frysinger   mtd/maps: gpio-ad...
15
  #include <linux/gpio.h>
f5bae56a5   Mike Frysinger   mtd/maps: gpio-ad...
16
  #include <linux/io.h>
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
17
18
19
20
21
22
23
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/mtd/mtd.h>
  #include <linux/mtd/map.h>
  #include <linux/mtd/partitions.h>
  #include <linux/mtd/physmap.h>
  #include <linux/platform_device.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
24
  #include <linux/slab.h>
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
25
  #include <linux/types.h>
7bf350b72   Artem Bityutskiy   mtd: fix a number...
26
27
  #define pr_devinit(fmt, args...) \
  	({ static const char __fmt[] = fmt; printk(__fmt, ## args); })
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  
  #define DRIVER_NAME "gpio-addr-flash"
  #define PFX DRIVER_NAME ": "
  
  /**
   * struct async_state - keep GPIO flash state
   *	@mtd:         MTD state for this mapping
   *	@map:         MTD map state for this flash
   *	@gpio_count:  number of GPIOs used to address
   *	@gpio_addrs:  array of GPIOs to twiddle
   *	@gpio_values: cached GPIO values
   *	@win_size:    dedicated memory size (if no GPIOs)
   */
  struct async_state {
  	struct mtd_info *mtd;
  	struct map_info map;
  	size_t gpio_count;
  	unsigned *gpio_addrs;
  	int *gpio_values;
  	unsigned long win_size;
  };
  #define gf_map_info_to_state(mi) ((struct async_state *)(mi)->map_priv_1)
  
  /**
   * gf_set_gpios() - set GPIO address lines to access specified flash offset
   *	@state: GPIO flash state
   *	@ofs:   desired offset to access
   *
   * Rather than call the GPIO framework every time, cache the last-programmed
   * value.  This speeds up sequential accesses (which are by far the most common
   * type).  We rely on the GPIO framework to treat non-zero value as high so
   * that we don't have to normalize the bits.
   */
  static void gf_set_gpios(struct async_state *state, unsigned long ofs)
  {
  	size_t i = 0;
  	int value;
  	ofs /= state->win_size;
  	do {
  		value = ofs & (1 << i);
  		if (state->gpio_values[i] != value) {
  			gpio_set_value(state->gpio_addrs[i], value);
  			state->gpio_values[i] = value;
  		}
  	} while (++i < state->gpio_count);
  }
  
  /**
   * gf_read() - read a word at the specified offset
   *	@map: MTD map state
   *	@ofs: desired offset to read
   */
  static map_word gf_read(struct map_info *map, unsigned long ofs)
  {
  	struct async_state *state = gf_map_info_to_state(map);
  	uint16_t word;
  	map_word test;
  
  	gf_set_gpios(state, ofs);
  
  	word = readw(map->virt + (ofs % state->win_size));
  	test.x[0] = word;
  	return test;
  }
  
  /**
   * gf_copy_from() - copy a chunk of data from the flash
   *	@map:  MTD map state
   *	@to:   memory to copy to
   *	@from: flash offset to copy from
   *	@len:  how much to copy
   *
02f8a24e7   Aaron Wu   mtd: gpio_flash: ...
100
101
   * The "from" region may straddle more than one window, so toggle the GPIOs for
   * each window region before reading its data.
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
102
103
104
105
   */
  static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
  {
  	struct async_state *state = gf_map_info_to_state(map);
02f8a24e7   Aaron Wu   mtd: gpio_flash: ...
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
  	int this_len;
  
  	while (len) {
  		if ((from % state->win_size) + len > state->win_size)
  			this_len = state->win_size - (from % state->win_size);
  		else
  			this_len = len;
  
  		gf_set_gpios(state, from);
  		memcpy_fromio(to, map->virt + (from % state->win_size),
  			 this_len);
  		len -= this_len;
  		from += this_len;
  		to += this_len;
  	}
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
  }
  
  /**
   * gf_write() - write a word at the specified offset
   *	@map: MTD map state
   *	@ofs: desired offset to write
   */
  static void gf_write(struct map_info *map, map_word d1, unsigned long ofs)
  {
  	struct async_state *state = gf_map_info_to_state(map);
  	uint16_t d;
  
  	gf_set_gpios(state, ofs);
  
  	d = d1.x[0];
  	writew(d, map->virt + (ofs % state->win_size));
  }
  
  /**
   * gf_copy_to() - copy a chunk of data to the flash
   *	@map:  MTD map state
   *	@to:   flash offset to copy to
   *	@from: memory to copy from
   *	@len:  how much to copy
   *
   * See gf_copy_from() caveat.
   */
7bf350b72   Artem Bityutskiy   mtd: fix a number...
148
149
  static void gf_copy_to(struct map_info *map, unsigned long to,
  		       const void *from, ssize_t len)
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
150
151
  {
  	struct async_state *state = gf_map_info_to_state(map);
02f8a24e7   Aaron Wu   mtd: gpio_flash: ...
152
153
154
155
156
157
158
  	int this_len;
  
  	while (len) {
  		if ((to % state->win_size) + len > state->win_size)
  			this_len = state->win_size - (to % state->win_size);
  		else
  			this_len = len;
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
159

02f8a24e7   Aaron Wu   mtd: gpio_flash: ...
160
161
  		gf_set_gpios(state, to);
  		memcpy_toio(map->virt + (to % state->win_size), from, len);
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
162

02f8a24e7   Aaron Wu   mtd: gpio_flash: ...
163
164
165
166
  		len -= this_len;
  		to += this_len;
  		from += this_len;
  	}
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
167
  }
0984c8910   Artem Bityutskiy   mtd: maps: add co...
168
169
  static const char * const part_probe_types[] = {
  	"cmdlinepart", "RedBoot", NULL };
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
  
  /**
   * gpio_flash_probe() - setup a mapping for a GPIO assisted flash
   *	@pdev: platform device
   *
   * The platform resource layout expected looks something like:
   * struct mtd_partition partitions[] = { ... };
   * struct physmap_flash_data flash_data = { ... };
   * unsigned flash_gpios[] = { GPIO_XX, GPIO_XX, ... };
   * struct resource flash_resource[] = {
   *	{
   *		.name  = "cfi_probe",
   *		.start = 0x20000000,
   *		.end   = 0x201fffff,
   *		.flags = IORESOURCE_MEM,
   *	}, {
   *		.start = (unsigned long)flash_gpios,
   *		.end   = ARRAY_SIZE(flash_gpios),
   *		.flags = IORESOURCE_IRQ,
   *	}
   * };
   * struct platform_device flash_device = {
   *	.name          = "gpio-addr-flash",
   *	.dev           = { .platform_data = &flash_data, },
   *	.num_resources = ARRAY_SIZE(flash_resource),
   *	.resource      = flash_resource,
   *	...
   * };
   */
06f255106   Bill Pemberton   mtd: remove use o...
199
  static int gpio_flash_probe(struct platform_device *pdev)
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
200
  {
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
201
202
203
204
205
  	size_t i, arr_size;
  	struct physmap_flash_data *pdata;
  	struct resource *memory;
  	struct resource *gpios;
  	struct async_state *state;
d20d5a578   Jingoo Han   mtd: maps: use de...
206
  	pdata = dev_get_platdata(&pdev->dev);
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
207
208
209
210
211
212
213
214
215
216
  	memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  
  	if (!memory || !gpios || !gpios->end)
  		return -EINVAL;
  
  	arr_size = sizeof(int) * gpios->end;
  	state = kzalloc(sizeof(*state) + arr_size, GFP_KERNEL);
  	if (!state)
  		return -ENOMEM;
ebd71e3a4   Mike Frysinger   mtd: maps: gpio-a...
217
218
219
220
  	/*
  	 * We cast start/end to known types in the boards file, so cast
  	 * away their pointer types here to the known types (gpios->xxx).
  	 */
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
221
  	state->gpio_count     = gpios->end;
ebd71e3a4   Mike Frysinger   mtd: maps: gpio-a...
222
  	state->gpio_addrs     = (void *)(unsigned long)gpios->start;
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
223
  	state->gpio_values    = (void *)(state + 1);
ebd71e3a4   Mike Frysinger   mtd: maps: gpio-a...
224
  	state->win_size       = resource_size(memory);
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
225
226
227
228
229
230
231
232
233
  	memset(state->gpio_values, 0xff, arr_size);
  
  	state->map.name       = DRIVER_NAME;
  	state->map.read       = gf_read;
  	state->map.copy_from  = gf_copy_from;
  	state->map.write      = gf_write;
  	state->map.copy_to    = gf_copy_to;
  	state->map.bankwidth  = pdata->width;
  	state->map.size       = state->win_size * (1 << state->gpio_count);
ebd71e3a4   Mike Frysinger   mtd: maps: gpio-a...
234
  	state->map.virt       = ioremap_nocache(memory->start, state->map.size);
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
  	state->map.phys       = NO_XIP;
  	state->map.map_priv_1 = (unsigned long)state;
  
  	platform_set_drvdata(pdev, state);
  
  	i = 0;
  	do {
  		if (gpio_request(state->gpio_addrs[i], DRIVER_NAME)) {
  			pr_devinit(KERN_ERR PFX "failed to request gpio %d
  ",
  				state->gpio_addrs[i]);
  			while (i--)
  				gpio_free(state->gpio_addrs[i]);
  			kfree(state);
  			return -EBUSY;
  		}
  		gpio_direction_output(state->gpio_addrs[i], 0);
  	} while (++i < state->gpio_count);
  
  	pr_devinit(KERN_NOTICE PFX "probing %d-bit flash bus
  ",
  		state->map.bankwidth * 8);
  	state->mtd = do_map_probe(memory->name, &state->map);
  	if (!state->mtd) {
  		for (i = 0; i < state->gpio_count; ++i)
  			gpio_free(state->gpio_addrs[i]);
  		kfree(state);
  		return -ENXIO;
  	}
28bc7406b   Frans Klaver   mtd: maps: gpio-a...
264
  	state->mtd->dev.parent = &pdev->dev;
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
265

42d7fbe22   Artem Bityutskiy   mtd: do not use p...
266
267
  	mtd_device_parse_register(state->mtd, part_probe_types, NULL,
  				  pdata->parts, pdata->nr_parts);
fdbf3bf6b   Jamie Iles   mtd: gpio-addr-fl...
268

d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
269
270
  	return 0;
  }
810b7e060   Bill Pemberton   mtd: remove use o...
271
  static int gpio_flash_remove(struct platform_device *pdev)
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
272
273
274
275
276
277
  {
  	struct async_state *state = platform_get_drvdata(pdev);
  	size_t i = 0;
  	do {
  		gpio_free(state->gpio_addrs[i]);
  	} while (++i < state->gpio_count);
fdbf3bf6b   Jamie Iles   mtd: gpio-addr-fl...
278
  	mtd_device_unregister(state->mtd);
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
279
280
281
282
283
284
285
  	map_destroy(state->mtd);
  	kfree(state);
  	return 0;
  }
  
  static struct platform_driver gpio_flash_driver = {
  	.probe		= gpio_flash_probe,
5153b88ca   Bill Pemberton   mtd: remove use o...
286
  	.remove		= gpio_flash_remove,
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
287
288
289
290
  	.driver		= {
  		.name	= DRIVER_NAME,
  	},
  };
f99640dee   Axel Lin   mtd: convert driv...
291
  module_platform_driver(gpio_flash_driver);
d79c326c0   Mike Frysinger   mtd/maps: gpio-ad...
292
293
294
295
  
  MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
  MODULE_DESCRIPTION("MTD map driver for flashes addressed physically and with gpios");
  MODULE_LICENSE("GPL");