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/module.h> 14#include <linux/interrupt.h> 15#include <linux/io.h> 16#include <linux/platform_device.h> 17#include <linux/delay.h> 18#include <linux/clk.h> 19#include <linux/err.h> 20 21#include <linux/mtd/mtd.h> 22#include <linux/mtd/nand.h> 23#include <linux/mtd/partitions.h> 24 25#define REG_FMICSR 0x00 26#define REG_SMCSR 0xa0 27#define REG_SMISR 0xac 28#define REG_SMCMD 0xb0 29#define REG_SMADDR 0xb4 30#define REG_SMDATA 0xb8 31 32#define RESET_FMI 0x01 33#define NAND_EN 0x08 34#define READYBUSY (0x01 << 18) 35 36#define SWRST 0x01 37#define PSIZE (0x01 << 3) 38#define DMARWEN (0x03 << 1) 39#define BUSWID (0x01 << 4) 40#define ECC4EN (0x01 << 5) 41#define WP (0x01 << 24) 42#define NANDCS (0x01 << 25) 43#define ENDADDR (0x01 << 31) 44 45#define read_data_reg(dev) \ 46 __raw_readl((dev)->reg + REG_SMDATA) 47 48#define write_data_reg(dev, val) \ 49 __raw_writel((val), (dev)->reg + REG_SMDATA) 50 51#define write_cmd_reg(dev, val) \ 52 __raw_writel((val), (dev)->reg + REG_SMCMD) 53 54#define write_addr_reg(dev, val) \ 55 __raw_writel((val), (dev)->reg + REG_SMADDR) 56 57struct nuc900_nand { 58 struct mtd_info mtd; 59 struct nand_chip chip; 60 void __iomem *reg; 61 struct clk *clk; 62 spinlock_t lock; 63}; 64 65static const struct mtd_partition partitions[] = { 66 { 67 .name = "NAND FS 0", 68 .offset = 0, 69 .size = 8 * 1024 * 1024 70 }, 71 { 72 .name = "NAND FS 1", 73 .offset = MTDPART_OFS_APPEND, 74 .size = MTDPART_SIZ_FULL 75 } 76}; 77 78static unsigned char nuc900_nand_read_byte(struct mtd_info *mtd) 79{ 80 unsigned char ret; 81 struct nuc900_nand *nand; 82 83 nand = container_of(mtd, struct nuc900_nand, mtd); 84 85 ret = (unsigned char)read_data_reg(nand); 86 87 return ret; 88} 89 90static void nuc900_nand_read_buf(struct mtd_info *mtd, 91 unsigned char *buf, int len) 92{ 93 int i; 94 struct nuc900_nand *nand; 95 96 nand = container_of(mtd, struct nuc900_nand, mtd); 97 98 for (i = 0; i < len; i++) 99 buf[i] = (unsigned char)read_data_reg(nand); 100} 101 102static void nuc900_nand_write_buf(struct mtd_info *mtd, 103 const unsigned char *buf, int len) 104{ 105 int i; 106 struct nuc900_nand *nand; 107 108 nand = container_of(mtd, struct nuc900_nand, mtd); 109 110 for (i = 0; i < len; i++) 111 write_data_reg(nand, buf[i]); 112} 113 114static int nuc900_check_rb(struct nuc900_nand *nand) 115{ 116 unsigned int val; 117 spin_lock(&nand->lock); 118 val = __raw_readl(REG_SMISR); 119 val &= READYBUSY; 120 spin_unlock(&nand->lock); 121 122 return val; 123} 124 125static int nuc900_nand_devready(struct mtd_info *mtd) 126{ 127 struct nuc900_nand *nand; 128 int ready; 129 130 nand = container_of(mtd, struct nuc900_nand, mtd); 131 132 ready = (nuc900_check_rb(nand)) ? 1 : 0; 133 return ready; 134} 135 136static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command, 137 int column, int page_addr) 138{ 139 register struct nand_chip *chip = mtd->priv; 140 struct nuc900_nand *nand; 141 142 nand = container_of(mtd, struct nuc900_nand, mtd); 143 144 if (command == NAND_CMD_READOOB) { 145 column += mtd->writesize; 146 command = NAND_CMD_READ0; 147 } 148 149 write_cmd_reg(nand, command & 0xff); 150 151 if (column != -1 || page_addr != -1) { 152 153 if (column != -1) { 154 if (chip->options & NAND_BUSWIDTH_16 && 155 !nand_opcode_8bits(command)) 156 column >>= 1; 157 write_addr_reg(nand, column); 158 write_addr_reg(nand, column >> 8 | ENDADDR); 159 } 160 if (page_addr != -1) { 161 write_addr_reg(nand, page_addr); 162 163 if (chip->chipsize > (128 << 20)) { 164 write_addr_reg(nand, page_addr >> 8); 165 write_addr_reg(nand, page_addr >> 16 | ENDADDR); 166 } else { 167 write_addr_reg(nand, page_addr >> 8 | ENDADDR); 168 } 169 } 170 } 171 172 switch (command) { 173 case NAND_CMD_CACHEDPROG: 174 case NAND_CMD_PAGEPROG: 175 case NAND_CMD_ERASE1: 176 case NAND_CMD_ERASE2: 177 case NAND_CMD_SEQIN: 178 case NAND_CMD_RNDIN: 179 case NAND_CMD_STATUS: 180 return; 181 182 case NAND_CMD_RESET: 183 if (chip->dev_ready) 184 break; 185 udelay(chip->chip_delay); 186 187 write_cmd_reg(nand, NAND_CMD_STATUS); 188 write_cmd_reg(nand, command); 189 190 while (!nuc900_check_rb(nand)) 191 ; 192 193 return; 194 195 case NAND_CMD_RNDOUT: 196 write_cmd_reg(nand, NAND_CMD_RNDOUTSTART); 197 return; 198 199 case NAND_CMD_READ0: 200 201 write_cmd_reg(nand, NAND_CMD_READSTART); 202 default: 203 204 if (!chip->dev_ready) { 205 udelay(chip->chip_delay); 206 return; 207 } 208 } 209 210 /* Apply this short delay always to ensure that we do wait tWB in 211 * any case on any machine. */ 212 ndelay(100); 213 214 while (!chip->dev_ready(mtd)) 215 ; 216} 217 218 219static void nuc900_nand_enable(struct nuc900_nand *nand) 220{ 221 unsigned int val; 222 spin_lock(&nand->lock); 223 __raw_writel(RESET_FMI, (nand->reg + REG_FMICSR)); 224 225 val = __raw_readl(nand->reg + REG_FMICSR); 226 227 if (!(val & NAND_EN)) 228 __raw_writel(val | NAND_EN, nand->reg + REG_FMICSR); 229 230 val = __raw_readl(nand->reg + REG_SMCSR); 231 232 val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS); 233 val |= WP; 234 235 __raw_writel(val, nand->reg + REG_SMCSR); 236 237 spin_unlock(&nand->lock); 238} 239 240static int nuc900_nand_probe(struct platform_device *pdev) 241{ 242 struct nuc900_nand *nuc900_nand; 243 struct nand_chip *chip; 244 struct resource *res; 245 246 nuc900_nand = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_nand), 247 GFP_KERNEL); 248 if (!nuc900_nand) 249 return -ENOMEM; 250 chip = &(nuc900_nand->chip); 251 252 nuc900_nand->mtd.priv = chip; 253 nuc900_nand->mtd.dev.parent = &pdev->dev; 254 spin_lock_init(&nuc900_nand->lock); 255 256 nuc900_nand->clk = devm_clk_get(&pdev->dev, NULL); 257 if (IS_ERR(nuc900_nand->clk)) 258 return -ENOENT; 259 clk_enable(nuc900_nand->clk); 260 261 chip->cmdfunc = nuc900_nand_command_lp; 262 chip->dev_ready = nuc900_nand_devready; 263 chip->read_byte = nuc900_nand_read_byte; 264 chip->write_buf = nuc900_nand_write_buf; 265 chip->read_buf = nuc900_nand_read_buf; 266 chip->chip_delay = 50; 267 chip->options = 0; 268 chip->ecc.mode = NAND_ECC_SOFT; 269 270 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 271 nuc900_nand->reg = devm_ioremap_resource(&pdev->dev, res); 272 if (IS_ERR(nuc900_nand->reg)) 273 return PTR_ERR(nuc900_nand->reg); 274 275 nuc900_nand_enable(nuc900_nand); 276 277 if (nand_scan(&(nuc900_nand->mtd), 1)) 278 return -ENXIO; 279 280 mtd_device_register(&(nuc900_nand->mtd), partitions, 281 ARRAY_SIZE(partitions)); 282 283 platform_set_drvdata(pdev, nuc900_nand); 284 285 return 0; 286} 287 288static int nuc900_nand_remove(struct platform_device *pdev) 289{ 290 struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev); 291 292 nand_release(&nuc900_nand->mtd); 293 clk_disable(nuc900_nand->clk); 294 295 return 0; 296} 297 298static struct platform_driver nuc900_nand_driver = { 299 .probe = nuc900_nand_probe, 300 .remove = nuc900_nand_remove, 301 .driver = { 302 .name = "nuc900-fmi", 303 }, 304}; 305 306module_platform_driver(nuc900_nand_driver); 307 308MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); 309MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!"); 310MODULE_LICENSE("GPL"); 311MODULE_ALIAS("platform:nuc900-fmi"); 312