Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.20-rc7 281 lines 7.4 kB view raw
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 16#include <linux/gpio.h> 17#include <linux/gpio/consumer.h> 18#include <linux/io.h> 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/slab.h> 27#include <linux/types.h> 28 29#define win_mask(x) ((BIT(x)) - 1) 30 31#define DRIVER_NAME "gpio-addr-flash" 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 * @gpios: Struct containing the array of GPIO descriptors 38 * @gpio_values: cached GPIO values 39 * @win_order: dedicated memory size (if no GPIOs) 40 */ 41struct async_state { 42 struct mtd_info *mtd; 43 struct map_info map; 44 struct gpio_descs *gpios; 45 unsigned int gpio_values; 46 unsigned int win_order; 47}; 48#define gf_map_info_to_state(mi) ((struct async_state *)(mi)->map_priv_1) 49 50/** 51 * gf_set_gpios() - set GPIO address lines to access specified flash offset 52 * @state: GPIO flash state 53 * @ofs: desired offset to access 54 * 55 * Rather than call the GPIO framework every time, cache the last-programmed 56 * value. This speeds up sequential accesses (which are by far the most common 57 * type). 58 */ 59static void gf_set_gpios(struct async_state *state, unsigned long ofs) 60{ 61 int i; 62 63 ofs >>= state->win_order; 64 65 if (ofs == state->gpio_values) 66 return; 67 68 for (i = 0; i < state->gpios->ndescs; i++) { 69 if ((ofs & BIT(i)) == (state->gpio_values & BIT(i))) 70 continue; 71 72 gpiod_set_value(state->gpios->desc[i], !!(ofs & BIT(i))); 73 } 74 75 state->gpio_values = ofs; 76} 77 78/** 79 * gf_read() - read a word at the specified offset 80 * @map: MTD map state 81 * @ofs: desired offset to read 82 */ 83static map_word gf_read(struct map_info *map, unsigned long ofs) 84{ 85 struct async_state *state = gf_map_info_to_state(map); 86 uint16_t word; 87 map_word test; 88 89 gf_set_gpios(state, ofs); 90 91 word = readw(map->virt + (ofs & win_mask(state->win_order))); 92 test.x[0] = word; 93 return test; 94} 95 96/** 97 * gf_copy_from() - copy a chunk of data from the flash 98 * @map: MTD map state 99 * @to: memory to copy to 100 * @from: flash offset to copy from 101 * @len: how much to copy 102 * 103 * The "from" region may straddle more than one window, so toggle the GPIOs for 104 * each window region before reading its data. 105 */ 106static 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 int this_len; 111 112 while (len) { 113 this_len = from & win_mask(state->win_order); 114 this_len = BIT(state->win_order) - this_len; 115 this_len = min_t(int, len, this_len); 116 117 gf_set_gpios(state, from); 118 memcpy_fromio(to, 119 map->virt + (from & win_mask(state->win_order)), 120 this_len); 121 len -= this_len; 122 from += this_len; 123 to += this_len; 124 } 125} 126 127/** 128 * gf_write() - write a word at the specified offset 129 * @map: MTD map state 130 * @ofs: desired offset to write 131 */ 132static void gf_write(struct map_info *map, map_word d1, unsigned long ofs) 133{ 134 struct async_state *state = gf_map_info_to_state(map); 135 uint16_t d; 136 137 gf_set_gpios(state, ofs); 138 139 d = d1.x[0]; 140 writew(d, map->virt + (ofs & win_mask(state->win_order))); 141} 142 143/** 144 * gf_copy_to() - copy a chunk of data to the flash 145 * @map: MTD map state 146 * @to: flash offset to copy to 147 * @from: memory to copy from 148 * @len: how much to copy 149 * 150 * See gf_copy_from() caveat. 151 */ 152static void gf_copy_to(struct map_info *map, unsigned long to, 153 const void *from, ssize_t len) 154{ 155 struct async_state *state = gf_map_info_to_state(map); 156 157 int this_len; 158 159 while (len) { 160 this_len = to & win_mask(state->win_order); 161 this_len = BIT(state->win_order) - this_len; 162 this_len = min_t(int, len, this_len); 163 164 gf_set_gpios(state, to); 165 memcpy_toio(map->virt + (to & win_mask(state->win_order)), 166 from, len); 167 168 len -= this_len; 169 to += this_len; 170 from += this_len; 171 } 172} 173 174static const char * const part_probe_types[] = { 175 "cmdlinepart", "RedBoot", NULL }; 176 177/** 178 * gpio_flash_probe() - setup a mapping for a GPIO assisted flash 179 * @pdev: platform device 180 * 181 * The platform resource layout expected looks something like: 182 * struct mtd_partition partitions[] = { ... }; 183 * struct physmap_flash_data flash_data = { ... }; 184 * static struct gpiod_lookup_table addr_flash_gpios = { 185 * .dev_id = "gpio-addr-flash.0", 186 * .table = { 187 * GPIO_LOOKUP_IDX("gpio.0", 15, "addr", 0, GPIO_ACTIVE_HIGH), 188 * GPIO_LOOKUP_IDX("gpio.0", 16, "addr", 1, GPIO_ACTIVE_HIGH), 189 * ); 190 * }; 191 * gpiod_add_lookup_table(&addr_flash_gpios); 192 * 193 * struct resource flash_resource[] = { 194 * { 195 * .name = "cfi_probe", 196 * .start = 0x20000000, 197 * .end = 0x201fffff, 198 * .flags = IORESOURCE_MEM, 199 * }, 200 * }; 201 * struct platform_device flash_device = { 202 * .name = "gpio-addr-flash", 203 * .dev = { .platform_data = &flash_data, }, 204 * .num_resources = ARRAY_SIZE(flash_resource), 205 * .resource = flash_resource, 206 * ... 207 * }; 208 */ 209static int gpio_flash_probe(struct platform_device *pdev) 210{ 211 struct physmap_flash_data *pdata; 212 struct resource *memory; 213 struct async_state *state; 214 215 pdata = dev_get_platdata(&pdev->dev); 216 memory = platform_get_resource(pdev, IORESOURCE_MEM, 0); 217 218 if (!memory) 219 return -EINVAL; 220 221 state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL); 222 if (!state) 223 return -ENOMEM; 224 225 state->gpios = devm_gpiod_get_array(&pdev->dev, "addr", GPIOD_OUT_LOW); 226 if (IS_ERR(state->gpios)) 227 return PTR_ERR(state->gpios); 228 229 state->win_order = get_bitmask_order(resource_size(memory)) - 1; 230 231 state->map.name = DRIVER_NAME; 232 state->map.read = gf_read; 233 state->map.copy_from = gf_copy_from; 234 state->map.write = gf_write; 235 state->map.copy_to = gf_copy_to; 236 state->map.bankwidth = pdata->width; 237 state->map.size = BIT(state->win_order + state->gpios->ndescs); 238 state->map.virt = devm_ioremap_resource(&pdev->dev, memory); 239 if (IS_ERR(state->map.virt)) 240 return PTR_ERR(state->map.virt); 241 242 state->map.phys = NO_XIP; 243 state->map.map_priv_1 = (unsigned long)state; 244 245 platform_set_drvdata(pdev, state); 246 247 dev_notice(&pdev->dev, "probing %d-bit flash bus\n", 248 state->map.bankwidth * 8); 249 state->mtd = do_map_probe(memory->name, &state->map); 250 if (!state->mtd) 251 return -ENXIO; 252 state->mtd->dev.parent = &pdev->dev; 253 254 mtd_device_parse_register(state->mtd, part_probe_types, NULL, 255 pdata->parts, pdata->nr_parts); 256 257 return 0; 258} 259 260static int gpio_flash_remove(struct platform_device *pdev) 261{ 262 struct async_state *state = platform_get_drvdata(pdev); 263 264 mtd_device_unregister(state->mtd); 265 map_destroy(state->mtd); 266 return 0; 267} 268 269static struct platform_driver gpio_flash_driver = { 270 .probe = gpio_flash_probe, 271 .remove = gpio_flash_remove, 272 .driver = { 273 .name = DRIVER_NAME, 274 }, 275}; 276 277module_platform_driver(gpio_flash_driver); 278 279MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>"); 280MODULE_DESCRIPTION("MTD map driver for flashes addressed physically and with gpios"); 281MODULE_LICENSE("GPL");