1/* 2 * Copyright © 2009 Nuvoton technology corporation. 3 * 4 * Wan ZongShun <mcuos.com@gmail.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation;version 2 of the License. 9 * 10 */ 11 12#include <linux/slab.h> 13#include <linux/init.h> 14#include <linux/module.h> 15#include <linux/interrupt.h> 16#include <linux/io.h> 17#include <linux/platform_device.h> 18#include <linux/delay.h> 19#include <linux/clk.h> 20#include <linux/err.h> 21 22#include <linux/mtd/mtd.h> 23#include <linux/mtd/nand.h> 24#include <linux/mtd/partitions.h> 25 26#define REG_FMICSR 0x00 27#define REG_SMCSR 0xa0 28#define REG_SMISR 0xac 29#define REG_SMCMD 0xb0 30#define REG_SMADDR 0xb4 31#define REG_SMDATA 0xb8 32 33#define RESET_FMI 0x01 34#define NAND_EN 0x08 35#define READYBUSY (0x01 << 18) 36 37#define SWRST 0x01 38#define PSIZE (0x01 << 3) 39#define DMARWEN (0x03 << 1) 40#define BUSWID (0x01 << 4) 41#define ECC4EN (0x01 << 5) 42#define WP (0x01 << 24) 43#define NANDCS (0x01 << 25) 44#define ENDADDR (0x01 << 31) 45 46#define read_data_reg(dev) \ 47 __raw_readl((dev)->reg + REG_SMDATA) 48 49#define write_data_reg(dev, val) \ 50 __raw_writel((val), (dev)->reg + REG_SMDATA) 51 52#define write_cmd_reg(dev, val) \ 53 __raw_writel((val), (dev)->reg + REG_SMCMD) 54 55#define write_addr_reg(dev, val) \ 56 __raw_writel((val), (dev)->reg + REG_SMADDR) 57 58struct nuc900_nand { 59 struct mtd_info mtd; 60 struct nand_chip chip; 61 void __iomem *reg; 62 struct clk *clk; 63 spinlock_t lock; 64}; 65 66static const struct mtd_partition partitions[] = { 67 { 68 .name = "NAND FS 0", 69 .offset = 0, 70 .size = 8 * 1024 * 1024 71 }, 72 { 73 .name = "NAND FS 1", 74 .offset = MTDPART_OFS_APPEND, 75 .size = MTDPART_SIZ_FULL 76 } 77}; 78 79static unsigned char nuc900_nand_read_byte(struct mtd_info *mtd) 80{ 81 unsigned char ret; 82 struct nuc900_nand *nand; 83 84 nand = container_of(mtd, struct nuc900_nand, mtd); 85 86 ret = (unsigned char)read_data_reg(nand); 87 88 return ret; 89} 90 91static void nuc900_nand_read_buf(struct mtd_info *mtd, 92 unsigned char *buf, int len) 93{ 94 int i; 95 struct nuc900_nand *nand; 96 97 nand = container_of(mtd, struct nuc900_nand, mtd); 98 99 for (i = 0; i < len; i++) 100 buf[i] = (unsigned char)read_data_reg(nand); 101} 102 103static void nuc900_nand_write_buf(struct mtd_info *mtd, 104 const unsigned char *buf, int len) 105{ 106 int i; 107 struct nuc900_nand *nand; 108 109 nand = container_of(mtd, struct nuc900_nand, mtd); 110 111 for (i = 0; i < len; i++) 112 write_data_reg(nand, buf[i]); 113} 114 115static int nuc900_verify_buf(struct mtd_info *mtd, 116 const unsigned char *buf, int len) 117{ 118 int i; 119 struct nuc900_nand *nand; 120 121 nand = container_of(mtd, struct nuc900_nand, mtd); 122 123 for (i = 0; i < len; i++) { 124 if (buf[i] != (unsigned char)read_data_reg(nand)) 125 return -EFAULT; 126 } 127 128 return 0; 129} 130 131static int nuc900_check_rb(struct nuc900_nand *nand) 132{ 133 unsigned int val; 134 spin_lock(&nand->lock); 135 val = __raw_readl(REG_SMISR); 136 val &= READYBUSY; 137 spin_unlock(&nand->lock); 138 139 return val; 140} 141 142static int nuc900_nand_devready(struct mtd_info *mtd) 143{ 144 struct nuc900_nand *nand; 145 int ready; 146 147 nand = container_of(mtd, struct nuc900_nand, mtd); 148 149 ready = (nuc900_check_rb(nand)) ? 1 : 0; 150 return ready; 151} 152 153static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command, 154 int column, int page_addr) 155{ 156 register struct nand_chip *chip = mtd->priv; 157 struct nuc900_nand *nand; 158 159 nand = container_of(mtd, struct nuc900_nand, mtd); 160 161 if (command == NAND_CMD_READOOB) { 162 column += mtd->writesize; 163 command = NAND_CMD_READ0; 164 } 165 166 write_cmd_reg(nand, command & 0xff); 167 168 if (column != -1 || page_addr != -1) { 169 170 if (column != -1) { 171 if (chip->options & NAND_BUSWIDTH_16) 172 column >>= 1; 173 write_addr_reg(nand, column); 174 write_addr_reg(nand, column >> 8 | ENDADDR); 175 } 176 if (page_addr != -1) { 177 write_addr_reg(nand, page_addr); 178 179 if (chip->chipsize > (128 << 20)) { 180 write_addr_reg(nand, page_addr >> 8); 181 write_addr_reg(nand, page_addr >> 16 | ENDADDR); 182 } else { 183 write_addr_reg(nand, page_addr >> 8 | ENDADDR); 184 } 185 } 186 } 187 188 switch (command) { 189 case NAND_CMD_CACHEDPROG: 190 case NAND_CMD_PAGEPROG: 191 case NAND_CMD_ERASE1: 192 case NAND_CMD_ERASE2: 193 case NAND_CMD_SEQIN: 194 case NAND_CMD_RNDIN: 195 case NAND_CMD_STATUS: 196 case NAND_CMD_DEPLETE1: 197 return; 198 199 case NAND_CMD_STATUS_ERROR: 200 case NAND_CMD_STATUS_ERROR0: 201 case NAND_CMD_STATUS_ERROR1: 202 case NAND_CMD_STATUS_ERROR2: 203 case NAND_CMD_STATUS_ERROR3: 204 udelay(chip->chip_delay); 205 return; 206 207 case NAND_CMD_RESET: 208 if (chip->dev_ready) 209 break; 210 udelay(chip->chip_delay); 211 212 write_cmd_reg(nand, NAND_CMD_STATUS); 213 write_cmd_reg(nand, command); 214 215 while (!nuc900_check_rb(nand)) 216 ; 217 218 return; 219 220 case NAND_CMD_RNDOUT: 221 write_cmd_reg(nand, NAND_CMD_RNDOUTSTART); 222 return; 223 224 case NAND_CMD_READ0: 225 226 write_cmd_reg(nand, NAND_CMD_READSTART); 227 default: 228 229 if (!chip->dev_ready) { 230 udelay(chip->chip_delay); 231 return; 232 } 233 } 234 235 /* Apply this short delay always to ensure that we do wait tWB in 236 * any case on any machine. */ 237 ndelay(100); 238 239 while (!chip->dev_ready(mtd)) 240 ; 241} 242 243 244static void nuc900_nand_enable(struct nuc900_nand *nand) 245{ 246 unsigned int val; 247 spin_lock(&nand->lock); 248 __raw_writel(RESET_FMI, (nand->reg + REG_FMICSR)); 249 250 val = __raw_readl(nand->reg + REG_FMICSR); 251 252 if (!(val & NAND_EN)) 253 __raw_writel(val | NAND_EN, REG_FMICSR); 254 255 val = __raw_readl(nand->reg + REG_SMCSR); 256 257 val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS); 258 val |= WP; 259 260 __raw_writel(val, nand->reg + REG_SMCSR); 261 262 spin_unlock(&nand->lock); 263} 264 265static int __devinit nuc900_nand_probe(struct platform_device *pdev) 266{ 267 struct nuc900_nand *nuc900_nand; 268 struct nand_chip *chip; 269 int retval; 270 struct resource *res; 271 272 retval = 0; 273 274 nuc900_nand = kzalloc(sizeof(struct nuc900_nand), GFP_KERNEL); 275 if (!nuc900_nand) 276 return -ENOMEM; 277 chip = &(nuc900_nand->chip); 278 279 nuc900_nand->mtd.priv = chip; 280 nuc900_nand->mtd.owner = THIS_MODULE; 281 spin_lock_init(&nuc900_nand->lock); 282 283 nuc900_nand->clk = clk_get(&pdev->dev, NULL); 284 if (IS_ERR(nuc900_nand->clk)) { 285 retval = -ENOENT; 286 goto fail1; 287 } 288 clk_enable(nuc900_nand->clk); 289 290 chip->cmdfunc = nuc900_nand_command_lp; 291 chip->dev_ready = nuc900_nand_devready; 292 chip->read_byte = nuc900_nand_read_byte; 293 chip->write_buf = nuc900_nand_write_buf; 294 chip->read_buf = nuc900_nand_read_buf; 295 chip->verify_buf = nuc900_verify_buf; 296 chip->chip_delay = 50; 297 chip->options = 0; 298 chip->ecc.mode = NAND_ECC_SOFT; 299 300 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 301 if (!res) { 302 retval = -ENXIO; 303 goto fail1; 304 } 305 306 if (!request_mem_region(res->start, resource_size(res), pdev->name)) { 307 retval = -EBUSY; 308 goto fail1; 309 } 310 311 nuc900_nand->reg = ioremap(res->start, resource_size(res)); 312 if (!nuc900_nand->reg) { 313 retval = -ENOMEM; 314 goto fail2; 315 } 316 317 nuc900_nand_enable(nuc900_nand); 318 319 if (nand_scan(&(nuc900_nand->mtd), 1)) { 320 retval = -ENXIO; 321 goto fail3; 322 } 323 324 mtd_device_register(&(nuc900_nand->mtd), partitions, 325 ARRAY_SIZE(partitions)); 326 327 platform_set_drvdata(pdev, nuc900_nand); 328 329 return retval; 330 331fail3: iounmap(nuc900_nand->reg); 332fail2: release_mem_region(res->start, resource_size(res)); 333fail1: kfree(nuc900_nand); 334 return retval; 335} 336 337static int __devexit nuc900_nand_remove(struct platform_device *pdev) 338{ 339 struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev); 340 struct resource *res; 341 342 nand_release(&nuc900_nand->mtd); 343 iounmap(nuc900_nand->reg); 344 345 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 346 release_mem_region(res->start, resource_size(res)); 347 348 clk_disable(nuc900_nand->clk); 349 clk_put(nuc900_nand->clk); 350 351 kfree(nuc900_nand); 352 353 platform_set_drvdata(pdev, NULL); 354 355 return 0; 356} 357 358static struct platform_driver nuc900_nand_driver = { 359 .probe = nuc900_nand_probe, 360 .remove = __devexit_p(nuc900_nand_remove), 361 .driver = { 362 .name = "nuc900-fmi", 363 .owner = THIS_MODULE, 364 }, 365}; 366 367module_platform_driver(nuc900_nand_driver); 368 369MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); 370MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!"); 371MODULE_LICENSE("GPL"); 372MODULE_ALIAS("platform:nuc900-fmi"); 373