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

[MTD] [NAND] GPIO NAND flash driver

The patch adds support for NAND flashes connected to GPIOs.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Mike Rapoport <mike@compulab.co.il>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>

authored by

Mike Rapoport and committed by
David Woodhouse
aaf7ea20 a0ee24a0

+401
+6
drivers/mtd/nand/Kconfig
··· 56 56 help 57 57 This enables the driver for the iPAQ h1900 flash. 58 58 59 + config MTD_NAND_GPIO 60 + tristate "GPIO NAND Flash driver" 61 + depends on GENERIC_GPIO 62 + help 63 + This enables a GPIO based NAND flash driver. 64 + 59 65 config MTD_NAND_SPIA 60 66 tristate "NAND Flash device on SPIA board" 61 67 depends on ARCH_P720T
+1
drivers/mtd/nand/Makefile
··· 23 23 obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o 24 24 obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o 25 25 obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o 26 + obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o 26 27 obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o 27 28 obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o 28 29 obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o
+375
drivers/mtd/nand/gpio.c
··· 1 + /* 2 + * drivers/mtd/nand/gpio.c 3 + * 4 + * Updated, and converted to generic GPIO based driver by Russell King. 5 + * 6 + * Written by Ben Dooks <ben@simtec.co.uk> 7 + * Based on 2.4 version by Mark Whittaker 8 + * 9 + * © 2004 Simtec Electronics 10 + * 11 + * Device driver for NAND connected via GPIO 12 + * 13 + * This program is free software; you can redistribute it and/or modify 14 + * it under the terms of the GNU General Public License version 2 as 15 + * published by the Free Software Foundation. 16 + * 17 + */ 18 + 19 + #include <linux/kernel.h> 20 + #include <linux/init.h> 21 + #include <linux/slab.h> 22 + #include <linux/module.h> 23 + #include <linux/platform_device.h> 24 + #include <linux/gpio.h> 25 + #include <linux/io.h> 26 + #include <linux/mtd/mtd.h> 27 + #include <linux/mtd/nand.h> 28 + #include <linux/mtd/partitions.h> 29 + #include <linux/mtd/nand-gpio.h> 30 + 31 + struct gpiomtd { 32 + void __iomem *io_sync; 33 + struct mtd_info mtd_info; 34 + struct nand_chip nand_chip; 35 + struct gpio_nand_platdata plat; 36 + }; 37 + 38 + #define gpio_nand_getpriv(x) container_of(x, struct gpiomtd, mtd_info) 39 + 40 + 41 + #ifdef CONFIG_ARM 42 + /* gpio_nand_dosync() 43 + * 44 + * Make sure the GPIO state changes occur in-order with writes to NAND 45 + * memory region. 46 + * Needed on PXA due to bus-reordering within the SoC itself (see section on 47 + * I/O ordering in PXA manual (section 2.3, p35) 48 + */ 49 + static void gpio_nand_dosync(struct gpiomtd *gpiomtd) 50 + { 51 + unsigned long tmp; 52 + 53 + if (gpiomtd->io_sync) { 54 + /* 55 + * Linux memory barriers don't cater for what's required here. 56 + * What's required is what's here - a read from a separate 57 + * region with a dependency on that read. 58 + */ 59 + tmp = readl(gpiomtd->io_sync); 60 + asm volatile("mov %1, %0\n" : "=r" (tmp) : "r" (tmp)); 61 + } 62 + } 63 + #else 64 + static inline void gpio_nand_dosync(struct gpiomtd *gpiomtd) {} 65 + #endif 66 + 67 + static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) 68 + { 69 + struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); 70 + 71 + gpio_nand_dosync(gpiomtd); 72 + 73 + if (ctrl & NAND_CTRL_CHANGE) { 74 + gpio_set_value(gpiomtd->plat.gpio_nce, !(ctrl & NAND_NCE)); 75 + gpio_set_value(gpiomtd->plat.gpio_cle, !!(ctrl & NAND_CLE)); 76 + gpio_set_value(gpiomtd->plat.gpio_ale, !!(ctrl & NAND_ALE)); 77 + gpio_nand_dosync(gpiomtd); 78 + } 79 + if (cmd == NAND_CMD_NONE) 80 + return; 81 + 82 + writeb(cmd, gpiomtd->nand_chip.IO_ADDR_W); 83 + gpio_nand_dosync(gpiomtd); 84 + } 85 + 86 + static void gpio_nand_writebuf(struct mtd_info *mtd, const u_char *buf, int len) 87 + { 88 + struct nand_chip *this = mtd->priv; 89 + 90 + writesb(this->IO_ADDR_W, buf, len); 91 + } 92 + 93 + static void gpio_nand_readbuf(struct mtd_info *mtd, u_char *buf, int len) 94 + { 95 + struct nand_chip *this = mtd->priv; 96 + 97 + readsb(this->IO_ADDR_R, buf, len); 98 + } 99 + 100 + static int gpio_nand_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) 101 + { 102 + struct nand_chip *this = mtd->priv; 103 + unsigned char read, *p = (unsigned char *) buf; 104 + int i, err = 0; 105 + 106 + for (i = 0; i < len; i++) { 107 + read = readb(this->IO_ADDR_R); 108 + if (read != p[i]) { 109 + pr_debug("%s: err at %d (read %04x vs %04x)\n", 110 + __func__, i, read, p[i]); 111 + err = -EFAULT; 112 + } 113 + } 114 + return err; 115 + } 116 + 117 + static void gpio_nand_writebuf16(struct mtd_info *mtd, const u_char *buf, 118 + int len) 119 + { 120 + struct nand_chip *this = mtd->priv; 121 + 122 + if (IS_ALIGNED((unsigned long)buf, 2)) { 123 + writesw(this->IO_ADDR_W, buf, len>>1); 124 + } else { 125 + int i; 126 + unsigned short *ptr = (unsigned short *)buf; 127 + 128 + for (i = 0; i < len; i += 2, ptr++) 129 + writew(*ptr, this->IO_ADDR_W); 130 + } 131 + } 132 + 133 + static void gpio_nand_readbuf16(struct mtd_info *mtd, u_char *buf, int len) 134 + { 135 + struct nand_chip *this = mtd->priv; 136 + 137 + if (IS_ALIGNED((unsigned long)buf, 2)) { 138 + readsw(this->IO_ADDR_R, buf, len>>1); 139 + } else { 140 + int i; 141 + unsigned short *ptr = (unsigned short *)buf; 142 + 143 + for (i = 0; i < len; i += 2, ptr++) 144 + *ptr = readw(this->IO_ADDR_R); 145 + } 146 + } 147 + 148 + static int gpio_nand_verifybuf16(struct mtd_info *mtd, const u_char *buf, 149 + int len) 150 + { 151 + struct nand_chip *this = mtd->priv; 152 + unsigned short read, *p = (unsigned short *) buf; 153 + int i, err = 0; 154 + len >>= 1; 155 + 156 + for (i = 0; i < len; i++) { 157 + read = readw(this->IO_ADDR_R); 158 + if (read != p[i]) { 159 + pr_debug("%s: err at %d (read %04x vs %04x)\n", 160 + __func__, i, read, p[i]); 161 + err = -EFAULT; 162 + } 163 + } 164 + return err; 165 + } 166 + 167 + 168 + static int gpio_nand_devready(struct mtd_info *mtd) 169 + { 170 + struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); 171 + return gpio_get_value(gpiomtd->plat.gpio_rdy); 172 + } 173 + 174 + static int __devexit gpio_nand_remove(struct platform_device *dev) 175 + { 176 + struct gpiomtd *gpiomtd = platform_get_drvdata(dev); 177 + struct resource *res; 178 + 179 + nand_release(&gpiomtd->mtd_info); 180 + 181 + res = platform_get_resource(dev, IORESOURCE_MEM, 1); 182 + iounmap(gpiomtd->io_sync); 183 + if (res) 184 + release_mem_region(res->start, res->end - res->start + 1); 185 + 186 + res = platform_get_resource(dev, IORESOURCE_MEM, 0); 187 + iounmap(gpiomtd->nand_chip.IO_ADDR_R); 188 + release_mem_region(res->start, res->end - res->start + 1); 189 + 190 + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) 191 + gpio_set_value(gpiomtd->plat.gpio_nwp, 0); 192 + gpio_set_value(gpiomtd->plat.gpio_nce, 1); 193 + 194 + gpio_free(gpiomtd->plat.gpio_cle); 195 + gpio_free(gpiomtd->plat.gpio_ale); 196 + gpio_free(gpiomtd->plat.gpio_nce); 197 + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) 198 + gpio_free(gpiomtd->plat.gpio_nwp); 199 + gpio_free(gpiomtd->plat.gpio_rdy); 200 + 201 + kfree(gpiomtd); 202 + 203 + return 0; 204 + } 205 + 206 + static void __iomem *request_and_remap(struct resource *res, size_t size, 207 + const char *name, int *err) 208 + { 209 + void __iomem *ptr; 210 + 211 + if (!request_mem_region(res->start, res->end - res->start + 1, name)) { 212 + *err = -EBUSY; 213 + return NULL; 214 + } 215 + 216 + ptr = ioremap(res->start, size); 217 + if (!ptr) { 218 + release_mem_region(res->start, res->end - res->start + 1); 219 + *err = -ENOMEM; 220 + } 221 + return ptr; 222 + } 223 + 224 + static int __devinit gpio_nand_probe(struct platform_device *dev) 225 + { 226 + struct gpiomtd *gpiomtd; 227 + struct nand_chip *this; 228 + struct resource *res0, *res1; 229 + int ret; 230 + 231 + if (!dev->dev.platform_data) 232 + return -EINVAL; 233 + 234 + res0 = platform_get_resource(dev, IORESOURCE_MEM, 0); 235 + if (!res0) 236 + return -EINVAL; 237 + 238 + gpiomtd = kzalloc(sizeof(*gpiomtd), GFP_KERNEL); 239 + if (gpiomtd == NULL) { 240 + dev_err(&dev->dev, "failed to create NAND MTD\n"); 241 + return -ENOMEM; 242 + } 243 + 244 + this = &gpiomtd->nand_chip; 245 + this->IO_ADDR_R = request_and_remap(res0, 2, "NAND", &ret); 246 + if (!this->IO_ADDR_R) { 247 + dev_err(&dev->dev, "unable to map NAND\n"); 248 + goto err_map; 249 + } 250 + 251 + res1 = platform_get_resource(dev, IORESOURCE_MEM, 1); 252 + if (res1) { 253 + gpiomtd->io_sync = request_and_remap(res1, 4, "NAND sync", &ret); 254 + if (!gpiomtd->io_sync) { 255 + dev_err(&dev->dev, "unable to map sync NAND\n"); 256 + goto err_sync; 257 + } 258 + } 259 + 260 + memcpy(&gpiomtd->plat, dev->dev.platform_data, sizeof(gpiomtd->plat)); 261 + 262 + ret = gpio_request(gpiomtd->plat.gpio_nce, "NAND NCE"); 263 + if (ret) 264 + goto err_nce; 265 + gpio_direction_output(gpiomtd->plat.gpio_nce, 1); 266 + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) { 267 + ret = gpio_request(gpiomtd->plat.gpio_nwp, "NAND NWP"); 268 + if (ret) 269 + goto err_nwp; 270 + gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); 271 + } 272 + ret = gpio_request(gpiomtd->plat.gpio_ale, "NAND ALE"); 273 + if (ret) 274 + goto err_ale; 275 + gpio_direction_output(gpiomtd->plat.gpio_ale, 0); 276 + ret = gpio_request(gpiomtd->plat.gpio_cle, "NAND CLE"); 277 + if (ret) 278 + goto err_cle; 279 + gpio_direction_output(gpiomtd->plat.gpio_cle, 0); 280 + ret = gpio_request(gpiomtd->plat.gpio_rdy, "NAND RDY"); 281 + if (ret) 282 + goto err_rdy; 283 + gpio_direction_input(gpiomtd->plat.gpio_rdy); 284 + 285 + 286 + this->IO_ADDR_W = this->IO_ADDR_R; 287 + this->ecc.mode = NAND_ECC_SOFT; 288 + this->options = gpiomtd->plat.options; 289 + this->chip_delay = gpiomtd->plat.chip_delay; 290 + 291 + /* install our routines */ 292 + this->cmd_ctrl = gpio_nand_cmd_ctrl; 293 + this->dev_ready = gpio_nand_devready; 294 + 295 + if (this->options & NAND_BUSWIDTH_16) { 296 + this->read_buf = gpio_nand_readbuf16; 297 + this->write_buf = gpio_nand_writebuf16; 298 + this->verify_buf = gpio_nand_verifybuf16; 299 + } else { 300 + this->read_buf = gpio_nand_readbuf; 301 + this->write_buf = gpio_nand_writebuf; 302 + this->verify_buf = gpio_nand_verifybuf; 303 + } 304 + 305 + /* set the mtd private data for the nand driver */ 306 + gpiomtd->mtd_info.priv = this; 307 + gpiomtd->mtd_info.owner = THIS_MODULE; 308 + 309 + if (nand_scan(&gpiomtd->mtd_info, 1)) { 310 + dev_err(&dev->dev, "no nand chips found?\n"); 311 + ret = -ENXIO; 312 + goto err_wp; 313 + } 314 + 315 + if (gpiomtd->plat.adjust_parts) 316 + gpiomtd->plat.adjust_parts(&gpiomtd->plat, 317 + gpiomtd->mtd_info.size); 318 + 319 + add_mtd_partitions(&gpiomtd->mtd_info, gpiomtd->plat.parts, 320 + gpiomtd->plat.num_parts); 321 + platform_set_drvdata(dev, gpiomtd); 322 + 323 + return 0; 324 + 325 + err_wp: 326 + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) 327 + gpio_set_value(gpiomtd->plat.gpio_nwp, 0); 328 + gpio_free(gpiomtd->plat.gpio_rdy); 329 + err_rdy: 330 + gpio_free(gpiomtd->plat.gpio_cle); 331 + err_cle: 332 + gpio_free(gpiomtd->plat.gpio_ale); 333 + err_ale: 334 + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) 335 + gpio_free(gpiomtd->plat.gpio_nwp); 336 + err_nwp: 337 + gpio_free(gpiomtd->plat.gpio_nce); 338 + err_nce: 339 + iounmap(gpiomtd->io_sync); 340 + if (res1) 341 + release_mem_region(res1->start, res1->end - res1->start + 1); 342 + err_sync: 343 + iounmap(gpiomtd->nand_chip.IO_ADDR_R); 344 + release_mem_region(res0->start, res0->end - res0->start + 1); 345 + err_map: 346 + kfree(gpiomtd); 347 + return ret; 348 + } 349 + 350 + static struct platform_driver gpio_nand_driver = { 351 + .probe = gpio_nand_probe, 352 + .remove = gpio_nand_remove, 353 + .driver = { 354 + .name = "gpio-nand", 355 + }, 356 + }; 357 + 358 + static int __init gpio_nand_init(void) 359 + { 360 + printk(KERN_INFO "GPIO NAND driver, © 2004 Simtec Electronics\n"); 361 + 362 + return platform_driver_register(&gpio_nand_driver); 363 + } 364 + 365 + static void __exit gpio_nand_exit(void) 366 + { 367 + platform_driver_unregister(&gpio_nand_driver); 368 + } 369 + 370 + module_init(gpio_nand_init); 371 + module_exit(gpio_nand_exit); 372 + 373 + MODULE_LICENSE("GPL"); 374 + MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 375 + MODULE_DESCRIPTION("GPIO NAND Driver");
+19
include/linux/mtd/nand-gpio.h
··· 1 + #ifndef __LINUX_MTD_NAND_GPIO_H 2 + #define __LINUX_MTD_NAND_GPIO_H 3 + 4 + #include <linux/mtd/nand.h> 5 + 6 + struct gpio_nand_platdata { 7 + int gpio_nce; 8 + int gpio_nwp; 9 + int gpio_cle; 10 + int gpio_ale; 11 + int gpio_rdy; 12 + void (*adjust_parts)(struct gpio_nand_platdata *, size_t); 13 + struct mtd_partition *parts; 14 + unsigned int num_parts; 15 + unsigned int options; 16 + int chip_delay; 17 + }; 18 + 19 + #endif