| Mike Frysinger | d79c326 | 2009-05-20 12:04:09 -0400 | [diff] [blame] | 1 | /* | 
|  | 2 | * drivers/mtd/maps/gpio-addr-flash.c | 
|  | 3 | * | 
|  | 4 | * Handle the case where a flash device is mostly addressed using physical | 
|  | 5 | * line and supplemented by GPIOs.  This way you can hook up say a 8MiB flash | 
|  | 6 | * to a 2MiB memory range and use the GPIOs to select a particular range. | 
|  | 7 | * | 
|  | 8 | * Copyright © 2000 Nicolas Pitre <nico@cam.org> | 
|  | 9 | * Copyright © 2005-2009 Analog Devices Inc. | 
|  | 10 | * | 
|  | 11 | * Enter bugs at http://blackfin.uclinux.org/ | 
|  | 12 | * | 
|  | 13 | * Licensed under the GPL-2 or later. | 
|  | 14 | */ | 
|  | 15 |  | 
| Mike Frysinger | f5bae56 | 2009-09-24 15:11:37 -0400 | [diff] [blame] | 16 | #include <linux/gpio.h> | 
| Mike Frysinger | d79c326 | 2009-05-20 12:04:09 -0400 | [diff] [blame] | 17 | #include <linux/init.h> | 
| Mike Frysinger | f5bae56 | 2009-09-24 15:11:37 -0400 | [diff] [blame] | 18 | #include <linux/io.h> | 
| Mike Frysinger | d79c326 | 2009-05-20 12:04:09 -0400 | [diff] [blame] | 19 | #include <linux/kernel.h> | 
|  | 20 | #include <linux/module.h> | 
|  | 21 | #include <linux/mtd/mtd.h> | 
|  | 22 | #include <linux/mtd/map.h> | 
|  | 23 | #include <linux/mtd/partitions.h> | 
|  | 24 | #include <linux/mtd/physmap.h> | 
|  | 25 | #include <linux/platform_device.h> | 
|  | 26 | #include <linux/types.h> | 
|  | 27 |  | 
| Mike Frysinger | d79c326 | 2009-05-20 12:04:09 -0400 | [diff] [blame] | 28 | #define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); }) | 
|  | 29 |  | 
|  | 30 | #define DRIVER_NAME "gpio-addr-flash" | 
|  | 31 | #define PFX DRIVER_NAME ": " | 
|  | 32 |  | 
|  | 33 | /** | 
|  | 34 | * struct async_state - keep GPIO flash state | 
|  | 35 | *	@mtd:         MTD state for this mapping | 
|  | 36 | *	@map:         MTD map state for this flash | 
|  | 37 | *	@gpio_count:  number of GPIOs used to address | 
|  | 38 | *	@gpio_addrs:  array of GPIOs to twiddle | 
|  | 39 | *	@gpio_values: cached GPIO values | 
|  | 40 | *	@win_size:    dedicated memory size (if no GPIOs) | 
|  | 41 | */ | 
|  | 42 | struct async_state { | 
|  | 43 | struct mtd_info *mtd; | 
|  | 44 | struct map_info map; | 
|  | 45 | size_t gpio_count; | 
|  | 46 | unsigned *gpio_addrs; | 
|  | 47 | int *gpio_values; | 
|  | 48 | unsigned long win_size; | 
|  | 49 | }; | 
|  | 50 | #define gf_map_info_to_state(mi) ((struct async_state *)(mi)->map_priv_1) | 
|  | 51 |  | 
|  | 52 | /** | 
|  | 53 | * gf_set_gpios() - set GPIO address lines to access specified flash offset | 
|  | 54 | *	@state: GPIO flash state | 
|  | 55 | *	@ofs:   desired offset to access | 
|  | 56 | * | 
|  | 57 | * Rather than call the GPIO framework every time, cache the last-programmed | 
|  | 58 | * value.  This speeds up sequential accesses (which are by far the most common | 
|  | 59 | * type).  We rely on the GPIO framework to treat non-zero value as high so | 
|  | 60 | * that we don't have to normalize the bits. | 
|  | 61 | */ | 
|  | 62 | static void gf_set_gpios(struct async_state *state, unsigned long ofs) | 
|  | 63 | { | 
|  | 64 | size_t i = 0; | 
|  | 65 | int value; | 
|  | 66 | ofs /= state->win_size; | 
|  | 67 | do { | 
|  | 68 | value = ofs & (1 << i); | 
|  | 69 | if (state->gpio_values[i] != value) { | 
|  | 70 | gpio_set_value(state->gpio_addrs[i], value); | 
|  | 71 | state->gpio_values[i] = value; | 
|  | 72 | } | 
|  | 73 | } while (++i < state->gpio_count); | 
|  | 74 | } | 
|  | 75 |  | 
|  | 76 | /** | 
|  | 77 | * gf_read() - read a word at the specified offset | 
|  | 78 | *	@map: MTD map state | 
|  | 79 | *	@ofs: desired offset to read | 
|  | 80 | */ | 
|  | 81 | static map_word gf_read(struct map_info *map, unsigned long ofs) | 
|  | 82 | { | 
|  | 83 | struct async_state *state = gf_map_info_to_state(map); | 
|  | 84 | uint16_t word; | 
|  | 85 | map_word test; | 
|  | 86 |  | 
|  | 87 | gf_set_gpios(state, ofs); | 
|  | 88 |  | 
|  | 89 | word = readw(map->virt + (ofs % state->win_size)); | 
|  | 90 | test.x[0] = word; | 
|  | 91 | return test; | 
|  | 92 | } | 
|  | 93 |  | 
|  | 94 | /** | 
|  | 95 | * gf_copy_from() - copy a chunk of data from the flash | 
|  | 96 | *	@map:  MTD map state | 
|  | 97 | *	@to:   memory to copy to | 
|  | 98 | *	@from: flash offset to copy from | 
|  | 99 | *	@len:  how much to copy | 
|  | 100 | * | 
|  | 101 | * We rely on the MTD layer to chunk up copies such that a single request here | 
|  | 102 | * will not cross a window size.  This allows us to only wiggle the GPIOs once | 
|  | 103 | * before falling back to a normal memcpy.  Reading the higher layer code shows | 
|  | 104 | * that this is indeed the case, but add a BUG_ON() to future proof. | 
|  | 105 | */ | 
|  | 106 | static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) | 
|  | 107 | { | 
|  | 108 | struct async_state *state = gf_map_info_to_state(map); | 
|  | 109 |  | 
|  | 110 | gf_set_gpios(state, from); | 
|  | 111 |  | 
|  | 112 | /* BUG if operation crosses the win_size */ | 
|  | 113 | BUG_ON(!((from + len) % state->win_size <= (from + len))); | 
|  | 114 |  | 
|  | 115 | /* operation does not cross the win_size, so one shot it */ | 
|  | 116 | memcpy_fromio(to, map->virt + (from % state->win_size), len); | 
|  | 117 | } | 
|  | 118 |  | 
|  | 119 | /** | 
|  | 120 | * gf_write() - write a word at the specified offset | 
|  | 121 | *	@map: MTD map state | 
|  | 122 | *	@ofs: desired offset to write | 
|  | 123 | */ | 
|  | 124 | static void gf_write(struct map_info *map, map_word d1, unsigned long ofs) | 
|  | 125 | { | 
|  | 126 | struct async_state *state = gf_map_info_to_state(map); | 
|  | 127 | uint16_t d; | 
|  | 128 |  | 
|  | 129 | gf_set_gpios(state, ofs); | 
|  | 130 |  | 
|  | 131 | d = d1.x[0]; | 
|  | 132 | writew(d, map->virt + (ofs % state->win_size)); | 
|  | 133 | } | 
|  | 134 |  | 
|  | 135 | /** | 
|  | 136 | * gf_copy_to() - copy a chunk of data to the flash | 
|  | 137 | *	@map:  MTD map state | 
|  | 138 | *	@to:   flash offset to copy to | 
|  | 139 | *	@from: memory to copy from | 
|  | 140 | *	@len:  how much to copy | 
|  | 141 | * | 
|  | 142 | * See gf_copy_from() caveat. | 
|  | 143 | */ | 
|  | 144 | static void gf_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) | 
|  | 145 | { | 
|  | 146 | struct async_state *state = gf_map_info_to_state(map); | 
|  | 147 |  | 
|  | 148 | gf_set_gpios(state, to); | 
|  | 149 |  | 
|  | 150 | /* BUG if operation crosses the win_size */ | 
|  | 151 | BUG_ON(!((to + len) % state->win_size <= (to + len))); | 
|  | 152 |  | 
|  | 153 | /* operation does not cross the win_size, so one shot it */ | 
|  | 154 | memcpy_toio(map->virt + (to % state->win_size), from, len); | 
|  | 155 | } | 
|  | 156 |  | 
|  | 157 | #ifdef CONFIG_MTD_PARTITIONS | 
|  | 158 | static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; | 
|  | 159 | #endif | 
|  | 160 |  | 
|  | 161 | /** | 
|  | 162 | * gpio_flash_probe() - setup a mapping for a GPIO assisted flash | 
|  | 163 | *	@pdev: platform device | 
|  | 164 | * | 
|  | 165 | * The platform resource layout expected looks something like: | 
|  | 166 | * struct mtd_partition partitions[] = { ... }; | 
|  | 167 | * struct physmap_flash_data flash_data = { ... }; | 
|  | 168 | * unsigned flash_gpios[] = { GPIO_XX, GPIO_XX, ... }; | 
|  | 169 | * struct resource flash_resource[] = { | 
|  | 170 | *	{ | 
|  | 171 | *		.name  = "cfi_probe", | 
|  | 172 | *		.start = 0x20000000, | 
|  | 173 | *		.end   = 0x201fffff, | 
|  | 174 | *		.flags = IORESOURCE_MEM, | 
|  | 175 | *	}, { | 
|  | 176 | *		.start = (unsigned long)flash_gpios, | 
|  | 177 | *		.end   = ARRAY_SIZE(flash_gpios), | 
|  | 178 | *		.flags = IORESOURCE_IRQ, | 
|  | 179 | *	} | 
|  | 180 | * }; | 
|  | 181 | * struct platform_device flash_device = { | 
|  | 182 | *	.name          = "gpio-addr-flash", | 
|  | 183 | *	.dev           = { .platform_data = &flash_data, }, | 
|  | 184 | *	.num_resources = ARRAY_SIZE(flash_resource), | 
|  | 185 | *	.resource      = flash_resource, | 
|  | 186 | *	... | 
|  | 187 | * }; | 
|  | 188 | */ | 
|  | 189 | static int __devinit gpio_flash_probe(struct platform_device *pdev) | 
|  | 190 | { | 
|  | 191 | int ret; | 
|  | 192 | size_t i, arr_size; | 
|  | 193 | struct physmap_flash_data *pdata; | 
|  | 194 | struct resource *memory; | 
|  | 195 | struct resource *gpios; | 
|  | 196 | struct async_state *state; | 
|  | 197 |  | 
|  | 198 | pdata = pdev->dev.platform_data; | 
|  | 199 | memory = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | 200 | gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | 
|  | 201 |  | 
|  | 202 | if (!memory || !gpios || !gpios->end) | 
|  | 203 | return -EINVAL; | 
|  | 204 |  | 
|  | 205 | arr_size = sizeof(int) * gpios->end; | 
|  | 206 | state = kzalloc(sizeof(*state) + arr_size, GFP_KERNEL); | 
|  | 207 | if (!state) | 
|  | 208 | return -ENOMEM; | 
|  | 209 |  | 
|  | 210 | state->gpio_count     = gpios->end; | 
|  | 211 | state->gpio_addrs     = (void *)gpios->start; | 
|  | 212 | state->gpio_values    = (void *)(state + 1); | 
|  | 213 | state->win_size       = memory->end - memory->start + 1; | 
|  | 214 | memset(state->gpio_values, 0xff, arr_size); | 
|  | 215 |  | 
|  | 216 | state->map.name       = DRIVER_NAME; | 
|  | 217 | state->map.read       = gf_read; | 
|  | 218 | state->map.copy_from  = gf_copy_from; | 
|  | 219 | state->map.write      = gf_write; | 
|  | 220 | state->map.copy_to    = gf_copy_to; | 
|  | 221 | state->map.bankwidth  = pdata->width; | 
|  | 222 | state->map.size       = state->win_size * (1 << state->gpio_count); | 
|  | 223 | state->map.virt       = (void __iomem *)memory->start; | 
|  | 224 | state->map.phys       = NO_XIP; | 
|  | 225 | state->map.map_priv_1 = (unsigned long)state; | 
|  | 226 |  | 
|  | 227 | platform_set_drvdata(pdev, state); | 
|  | 228 |  | 
|  | 229 | i = 0; | 
|  | 230 | do { | 
|  | 231 | if (gpio_request(state->gpio_addrs[i], DRIVER_NAME)) { | 
|  | 232 | pr_devinit(KERN_ERR PFX "failed to request gpio %d\n", | 
|  | 233 | state->gpio_addrs[i]); | 
|  | 234 | while (i--) | 
|  | 235 | gpio_free(state->gpio_addrs[i]); | 
|  | 236 | kfree(state); | 
|  | 237 | return -EBUSY; | 
|  | 238 | } | 
|  | 239 | gpio_direction_output(state->gpio_addrs[i], 0); | 
|  | 240 | } while (++i < state->gpio_count); | 
|  | 241 |  | 
|  | 242 | pr_devinit(KERN_NOTICE PFX "probing %d-bit flash bus\n", | 
|  | 243 | state->map.bankwidth * 8); | 
|  | 244 | state->mtd = do_map_probe(memory->name, &state->map); | 
|  | 245 | if (!state->mtd) { | 
|  | 246 | for (i = 0; i < state->gpio_count; ++i) | 
|  | 247 | gpio_free(state->gpio_addrs[i]); | 
|  | 248 | kfree(state); | 
|  | 249 | return -ENXIO; | 
|  | 250 | } | 
|  | 251 |  | 
|  | 252 | #ifdef CONFIG_MTD_PARTITIONS | 
|  | 253 | ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0); | 
|  | 254 | if (ret > 0) { | 
|  | 255 | pr_devinit(KERN_NOTICE PFX "Using commandline partition definition\n"); | 
|  | 256 | add_mtd_partitions(state->mtd, pdata->parts, ret); | 
|  | 257 | kfree(pdata->parts); | 
|  | 258 |  | 
|  | 259 | } else if (pdata->nr_parts) { | 
|  | 260 | pr_devinit(KERN_NOTICE PFX "Using board partition definition\n"); | 
|  | 261 | add_mtd_partitions(state->mtd, pdata->parts, pdata->nr_parts); | 
|  | 262 |  | 
|  | 263 | } else | 
|  | 264 | #endif | 
|  | 265 | { | 
|  | 266 | pr_devinit(KERN_NOTICE PFX "no partition info available, registering whole flash at once\n"); | 
|  | 267 | add_mtd_device(state->mtd); | 
|  | 268 | } | 
|  | 269 |  | 
|  | 270 | return 0; | 
|  | 271 | } | 
|  | 272 |  | 
|  | 273 | static int __devexit gpio_flash_remove(struct platform_device *pdev) | 
|  | 274 | { | 
|  | 275 | struct async_state *state = platform_get_drvdata(pdev); | 
|  | 276 | size_t i = 0; | 
|  | 277 | do { | 
|  | 278 | gpio_free(state->gpio_addrs[i]); | 
|  | 279 | } while (++i < state->gpio_count); | 
|  | 280 | #ifdef CONFIG_MTD_PARTITIONS | 
|  | 281 | del_mtd_partitions(state->mtd); | 
|  | 282 | #endif | 
|  | 283 | map_destroy(state->mtd); | 
|  | 284 | kfree(state); | 
|  | 285 | return 0; | 
|  | 286 | } | 
|  | 287 |  | 
|  | 288 | static struct platform_driver gpio_flash_driver = { | 
|  | 289 | .probe		= gpio_flash_probe, | 
|  | 290 | .remove		= __devexit_p(gpio_flash_remove), | 
|  | 291 | .driver		= { | 
|  | 292 | .name	= DRIVER_NAME, | 
|  | 293 | }, | 
|  | 294 | }; | 
|  | 295 |  | 
|  | 296 | static int __init gpio_flash_init(void) | 
|  | 297 | { | 
|  | 298 | return platform_driver_register(&gpio_flash_driver); | 
|  | 299 | } | 
|  | 300 | module_init(gpio_flash_init); | 
|  | 301 |  | 
|  | 302 | static void __exit gpio_flash_exit(void) | 
|  | 303 | { | 
|  | 304 | platform_driver_unregister(&gpio_flash_driver); | 
|  | 305 | } | 
|  | 306 | module_exit(gpio_flash_exit); | 
|  | 307 |  | 
|  | 308 | MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>"); | 
|  | 309 | MODULE_DESCRIPTION("MTD map driver for flashes addressed physically and with gpios"); | 
|  | 310 | MODULE_LICENSE("GPL"); |