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