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 v3.14-rc5 321 lines 7.9 kB view raw
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/err.h> 21#include <linux/init.h> 22#include <linux/slab.h> 23#include <linux/module.h> 24#include <linux/platform_device.h> 25#include <linux/gpio.h> 26#include <linux/io.h> 27#include <linux/mtd/mtd.h> 28#include <linux/mtd/nand.h> 29#include <linux/mtd/partitions.h> 30#include <linux/mtd/nand-gpio.h> 31#include <linux/of.h> 32#include <linux/of_address.h> 33#include <linux/of_gpio.h> 34 35struct gpiomtd { 36 void __iomem *io_sync; 37 struct mtd_info mtd_info; 38 struct nand_chip nand_chip; 39 struct gpio_nand_platdata plat; 40}; 41 42#define gpio_nand_getpriv(x) container_of(x, struct gpiomtd, mtd_info) 43 44 45#ifdef CONFIG_ARM 46/* gpio_nand_dosync() 47 * 48 * Make sure the GPIO state changes occur in-order with writes to NAND 49 * memory region. 50 * Needed on PXA due to bus-reordering within the SoC itself (see section on 51 * I/O ordering in PXA manual (section 2.3, p35) 52 */ 53static void gpio_nand_dosync(struct gpiomtd *gpiomtd) 54{ 55 unsigned long tmp; 56 57 if (gpiomtd->io_sync) { 58 /* 59 * Linux memory barriers don't cater for what's required here. 60 * What's required is what's here - a read from a separate 61 * region with a dependency on that read. 62 */ 63 tmp = readl(gpiomtd->io_sync); 64 asm volatile("mov %1, %0\n" : "=r" (tmp) : "r" (tmp)); 65 } 66} 67#else 68static inline void gpio_nand_dosync(struct gpiomtd *gpiomtd) {} 69#endif 70 71static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) 72{ 73 struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); 74 75 gpio_nand_dosync(gpiomtd); 76 77 if (ctrl & NAND_CTRL_CHANGE) { 78 gpio_set_value(gpiomtd->plat.gpio_nce, !(ctrl & NAND_NCE)); 79 gpio_set_value(gpiomtd->plat.gpio_cle, !!(ctrl & NAND_CLE)); 80 gpio_set_value(gpiomtd->plat.gpio_ale, !!(ctrl & NAND_ALE)); 81 gpio_nand_dosync(gpiomtd); 82 } 83 if (cmd == NAND_CMD_NONE) 84 return; 85 86 writeb(cmd, gpiomtd->nand_chip.IO_ADDR_W); 87 gpio_nand_dosync(gpiomtd); 88} 89 90static int gpio_nand_devready(struct mtd_info *mtd) 91{ 92 struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); 93 94 return gpio_get_value(gpiomtd->plat.gpio_rdy); 95} 96 97#ifdef CONFIG_OF 98static const struct of_device_id gpio_nand_id_table[] = { 99 { .compatible = "gpio-control-nand" }, 100 {} 101}; 102MODULE_DEVICE_TABLE(of, gpio_nand_id_table); 103 104static int gpio_nand_get_config_of(const struct device *dev, 105 struct gpio_nand_platdata *plat) 106{ 107 u32 val; 108 109 if (!dev->of_node) 110 return -ENODEV; 111 112 if (!of_property_read_u32(dev->of_node, "bank-width", &val)) { 113 if (val == 2) { 114 plat->options |= NAND_BUSWIDTH_16; 115 } else if (val != 1) { 116 dev_err(dev, "invalid bank-width %u\n", val); 117 return -EINVAL; 118 } 119 } 120 121 plat->gpio_rdy = of_get_gpio(dev->of_node, 0); 122 plat->gpio_nce = of_get_gpio(dev->of_node, 1); 123 plat->gpio_ale = of_get_gpio(dev->of_node, 2); 124 plat->gpio_cle = of_get_gpio(dev->of_node, 3); 125 plat->gpio_nwp = of_get_gpio(dev->of_node, 4); 126 127 if (!of_property_read_u32(dev->of_node, "chip-delay", &val)) 128 plat->chip_delay = val; 129 130 return 0; 131} 132 133static struct resource *gpio_nand_get_io_sync_of(struct platform_device *pdev) 134{ 135 struct resource *r; 136 u64 addr; 137 138 if (of_property_read_u64(pdev->dev.of_node, 139 "gpio-control-nand,io-sync-reg", &addr)) 140 return NULL; 141 142 r = devm_kzalloc(&pdev->dev, sizeof(*r), GFP_KERNEL); 143 if (!r) 144 return NULL; 145 146 r->start = addr; 147 r->end = r->start + 0x3; 148 r->flags = IORESOURCE_MEM; 149 150 return r; 151} 152#else /* CONFIG_OF */ 153static inline int gpio_nand_get_config_of(const struct device *dev, 154 struct gpio_nand_platdata *plat) 155{ 156 return -ENOSYS; 157} 158 159static inline struct resource * 160gpio_nand_get_io_sync_of(struct platform_device *pdev) 161{ 162 return NULL; 163} 164#endif /* CONFIG_OF */ 165 166static inline int gpio_nand_get_config(const struct device *dev, 167 struct gpio_nand_platdata *plat) 168{ 169 int ret = gpio_nand_get_config_of(dev, plat); 170 171 if (!ret) 172 return ret; 173 174 if (dev_get_platdata(dev)) { 175 memcpy(plat, dev_get_platdata(dev), sizeof(*plat)); 176 return 0; 177 } 178 179 return -EINVAL; 180} 181 182static inline struct resource * 183gpio_nand_get_io_sync(struct platform_device *pdev) 184{ 185 struct resource *r = gpio_nand_get_io_sync_of(pdev); 186 187 if (r) 188 return r; 189 190 return platform_get_resource(pdev, IORESOURCE_MEM, 1); 191} 192 193static int gpio_nand_remove(struct platform_device *pdev) 194{ 195 struct gpiomtd *gpiomtd = platform_get_drvdata(pdev); 196 197 nand_release(&gpiomtd->mtd_info); 198 199 if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) 200 gpio_set_value(gpiomtd->plat.gpio_nwp, 0); 201 gpio_set_value(gpiomtd->plat.gpio_nce, 1); 202 203 return 0; 204} 205 206static int gpio_nand_probe(struct platform_device *pdev) 207{ 208 struct gpiomtd *gpiomtd; 209 struct nand_chip *chip; 210 struct resource *res; 211 struct mtd_part_parser_data ppdata = {}; 212 int ret = 0; 213 214 if (!pdev->dev.of_node && !dev_get_platdata(&pdev->dev)) 215 return -EINVAL; 216 217 gpiomtd = devm_kzalloc(&pdev->dev, sizeof(*gpiomtd), GFP_KERNEL); 218 if (!gpiomtd) 219 return -ENOMEM; 220 221 chip = &gpiomtd->nand_chip; 222 223 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 224 chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); 225 if (IS_ERR(chip->IO_ADDR_R)) 226 return PTR_ERR(chip->IO_ADDR_R); 227 228 res = gpio_nand_get_io_sync(pdev); 229 if (res) { 230 gpiomtd->io_sync = devm_ioremap_resource(&pdev->dev, res); 231 if (IS_ERR(gpiomtd->io_sync)) 232 return PTR_ERR(gpiomtd->io_sync); 233 } 234 235 ret = gpio_nand_get_config(&pdev->dev, &gpiomtd->plat); 236 if (ret) 237 return ret; 238 239 ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nce, "NAND NCE"); 240 if (ret) 241 return ret; 242 gpio_direction_output(gpiomtd->plat.gpio_nce, 1); 243 244 if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) { 245 ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nwp, 246 "NAND NWP"); 247 if (ret) 248 return ret; 249 } 250 251 ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_ale, "NAND ALE"); 252 if (ret) 253 return ret; 254 gpio_direction_output(gpiomtd->plat.gpio_ale, 0); 255 256 ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_cle, "NAND CLE"); 257 if (ret) 258 return ret; 259 gpio_direction_output(gpiomtd->plat.gpio_cle, 0); 260 261 if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) { 262 ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_rdy, 263 "NAND RDY"); 264 if (ret) 265 return ret; 266 gpio_direction_input(gpiomtd->plat.gpio_rdy); 267 chip->dev_ready = gpio_nand_devready; 268 } 269 270 chip->IO_ADDR_W = chip->IO_ADDR_R; 271 chip->ecc.mode = NAND_ECC_SOFT; 272 chip->options = gpiomtd->plat.options; 273 chip->chip_delay = gpiomtd->plat.chip_delay; 274 chip->cmd_ctrl = gpio_nand_cmd_ctrl; 275 276 gpiomtd->mtd_info.priv = chip; 277 gpiomtd->mtd_info.owner = THIS_MODULE; 278 279 platform_set_drvdata(pdev, gpiomtd); 280 281 if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) 282 gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); 283 284 if (nand_scan(&gpiomtd->mtd_info, 1)) { 285 ret = -ENXIO; 286 goto err_wp; 287 } 288 289 if (gpiomtd->plat.adjust_parts) 290 gpiomtd->plat.adjust_parts(&gpiomtd->plat, 291 gpiomtd->mtd_info.size); 292 293 ppdata.of_node = pdev->dev.of_node; 294 ret = mtd_device_parse_register(&gpiomtd->mtd_info, NULL, &ppdata, 295 gpiomtd->plat.parts, 296 gpiomtd->plat.num_parts); 297 if (!ret) 298 return 0; 299 300err_wp: 301 if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) 302 gpio_set_value(gpiomtd->plat.gpio_nwp, 0); 303 304 return ret; 305} 306 307static struct platform_driver gpio_nand_driver = { 308 .probe = gpio_nand_probe, 309 .remove = gpio_nand_remove, 310 .driver = { 311 .name = "gpio-nand", 312 .owner = THIS_MODULE, 313 .of_match_table = of_match_ptr(gpio_nand_id_table), 314 }, 315}; 316 317module_platform_driver(gpio_nand_driver); 318 319MODULE_LICENSE("GPL"); 320MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 321MODULE_DESCRIPTION("GPIO NAND Driver");