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