aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/nand/Kconfig6
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/txx9ndfmc.c428
3 files changed, 435 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 30d6fbc30c35..68ae144ce3be 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -434,4 +434,10 @@ config MTD_NAND_DAVINCI
434 Enable the driver for NAND flash chips on Texas Instruments 434 Enable the driver for NAND flash chips on Texas Instruments
435 DaVinci processors. 435 DaVinci processors.
436 436
437config MTD_NAND_TXX9NDFMC
438 tristate "NAND Flash support for TXx9 SoC"
439 depends on SOC_TX4938 || SOC_TX4939
440 help
441 This enables the NAND flash controller on the TXx9 SoCs.
442
437endif # MTD_NAND 443endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 26dc0bef68ae..d228622d5dc0 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -37,5 +37,6 @@ obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
37obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o 37obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
38obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o 38obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
39obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o 39obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
40obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
40 41
41nand-objs := nand_base.o nand_bbt.o 42nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c
new file mode 100644
index 000000000000..812479264896
--- /dev/null
+++ b/drivers/mtd/nand/txx9ndfmc.c
@@ -0,0 +1,428 @@
1/*
2 * TXx9 NAND flash memory controller driver
3 * Based on RBTX49xx patch from CELF patch archive.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * (C) Copyright TOSHIBA CORPORATION 2004-2007
10 * All Rights Reserved.
11 */
12#include <linux/init.h>
13#include <linux/slab.h>
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/delay.h>
17#include <linux/mtd/mtd.h>
18#include <linux/mtd/nand.h>
19#include <linux/mtd/nand_ecc.h>
20#include <linux/mtd/partitions.h>
21#include <linux/io.h>
22#include <asm/txx9/ndfmc.h>
23
24/* TXX9 NDFMC Registers */
25#define TXX9_NDFDTR 0x00
26#define TXX9_NDFMCR 0x04
27#define TXX9_NDFSR 0x08
28#define TXX9_NDFISR 0x0c
29#define TXX9_NDFIMR 0x10
30#define TXX9_NDFSPR 0x14
31#define TXX9_NDFRSTR 0x18 /* not TX4939 */
32
33/* NDFMCR : NDFMC Mode Control */
34#define TXX9_NDFMCR_WE 0x80
35#define TXX9_NDFMCR_ECC_ALL 0x60
36#define TXX9_NDFMCR_ECC_RESET 0x60
37#define TXX9_NDFMCR_ECC_READ 0x40
38#define TXX9_NDFMCR_ECC_ON 0x20
39#define TXX9_NDFMCR_ECC_OFF 0x00
40#define TXX9_NDFMCR_CE 0x10
41#define TXX9_NDFMCR_BSPRT 0x04 /* TX4925/TX4926 only */
42#define TXX9_NDFMCR_ALE 0x02
43#define TXX9_NDFMCR_CLE 0x01
44/* TX4939 only */
45#define TXX9_NDFMCR_X16 0x0400
46#define TXX9_NDFMCR_DMAREQ_MASK 0x0300
47#define TXX9_NDFMCR_DMAREQ_NODMA 0x0000
48#define TXX9_NDFMCR_DMAREQ_128 0x0100
49#define TXX9_NDFMCR_DMAREQ_256 0x0200
50#define TXX9_NDFMCR_DMAREQ_512 0x0300
51#define TXX9_NDFMCR_CS_MASK 0x0c
52#define TXX9_NDFMCR_CS(ch) ((ch) << 2)
53
54/* NDFMCR : NDFMC Status */
55#define TXX9_NDFSR_BUSY 0x80
56/* TX4939 only */
57#define TXX9_NDFSR_DMARUN 0x40
58
59/* NDFMCR : NDFMC Reset */
60#define TXX9_NDFRSTR_RST 0x01
61
62struct txx9ndfmc_priv {
63 struct platform_device *dev;
64 struct nand_chip chip;
65 struct mtd_info mtd;
66 int cs;
67 char mtdname[BUS_ID_SIZE + 2];
68};
69
70#define MAX_TXX9NDFMC_DEV 4
71struct txx9ndfmc_drvdata {
72 struct mtd_info *mtds[MAX_TXX9NDFMC_DEV];
73 void __iomem *base;
74 unsigned char hold; /* in gbusclock */
75 unsigned char spw; /* in gbusclock */
76 struct nand_hw_control hw_control;
77#ifdef CONFIG_MTD_PARTITIONS
78 struct mtd_partition *parts[MAX_TXX9NDFMC_DEV];
79#endif
80};
81
82static struct platform_device *mtd_to_platdev(struct mtd_info *mtd)
83{
84 struct nand_chip *chip = mtd->priv;
85 struct txx9ndfmc_priv *txx9_priv = chip->priv;
86 return txx9_priv->dev;
87}
88
89static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg)
90{
91 struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
92 struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
93
94 return drvdata->base + (reg << plat->shift);
95}
96
97static u32 txx9ndfmc_read(struct platform_device *dev, unsigned int reg)
98{
99 return __raw_readl(ndregaddr(dev, reg));
100}
101
102static void txx9ndfmc_write(struct platform_device *dev,
103 u32 val, unsigned int reg)
104{
105 __raw_writel(val, ndregaddr(dev, reg));
106}
107
108static uint8_t txx9ndfmc_read_byte(struct mtd_info *mtd)
109{
110 struct platform_device *dev = mtd_to_platdev(mtd);
111
112 return txx9ndfmc_read(dev, TXX9_NDFDTR);
113}
114
115static void txx9ndfmc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
116 int len)
117{
118 struct platform_device *dev = mtd_to_platdev(mtd);
119 void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR);
120 u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
121
122 txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_WE, TXX9_NDFMCR);
123 while (len--)
124 __raw_writel(*buf++, ndfdtr);
125 txx9ndfmc_write(dev, mcr, TXX9_NDFMCR);
126}
127
128static void txx9ndfmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
129{
130 struct platform_device *dev = mtd_to_platdev(mtd);
131 void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR);
132
133 while (len--)
134 *buf++ = __raw_readl(ndfdtr);
135}
136
137static int txx9ndfmc_verify_buf(struct mtd_info *mtd, const uint8_t *buf,
138 int len)
139{
140 struct platform_device *dev = mtd_to_platdev(mtd);
141 void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR);
142
143 while (len--)
144 if (*buf++ != (uint8_t)__raw_readl(ndfdtr))
145 return -EFAULT;
146 return 0;
147}
148
149static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd,
150 unsigned int ctrl)
151{
152 struct nand_chip *chip = mtd->priv;
153 struct txx9ndfmc_priv *txx9_priv = chip->priv;
154 struct platform_device *dev = txx9_priv->dev;
155 struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
156
157 if (ctrl & NAND_CTRL_CHANGE) {
158 u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
159
160 mcr &= ~(TXX9_NDFMCR_CLE | TXX9_NDFMCR_ALE | TXX9_NDFMCR_CE);
161 mcr |= ctrl & NAND_CLE ? TXX9_NDFMCR_CLE : 0;
162 mcr |= ctrl & NAND_ALE ? TXX9_NDFMCR_ALE : 0;
163 /* TXX9_NDFMCR_CE bit is 0:high 1:low */
164 mcr |= ctrl & NAND_NCE ? TXX9_NDFMCR_CE : 0;
165 if (txx9_priv->cs >= 0 && (ctrl & NAND_NCE)) {
166 mcr &= ~TXX9_NDFMCR_CS_MASK;
167 mcr |= TXX9_NDFMCR_CS(txx9_priv->cs);
168 }
169 txx9ndfmc_write(dev, mcr, TXX9_NDFMCR);
170 }
171 if (cmd != NAND_CMD_NONE)
172 txx9ndfmc_write(dev, cmd & 0xff, TXX9_NDFDTR);
173 if (plat->flags & NDFMC_PLAT_FLAG_DUMMYWRITE) {
174 /* dummy write to update external latch */
175 if ((ctrl & NAND_CTRL_CHANGE) && cmd == NAND_CMD_NONE)
176 txx9ndfmc_write(dev, 0, TXX9_NDFDTR);
177 }
178 mmiowb();
179}
180
181static int txx9ndfmc_dev_ready(struct mtd_info *mtd)
182{
183 struct platform_device *dev = mtd_to_platdev(mtd);
184
185 return !(txx9ndfmc_read(dev, TXX9_NDFSR) & TXX9_NDFSR_BUSY);
186}
187
188static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
189 uint8_t *ecc_code)
190{
191 struct platform_device *dev = mtd_to_platdev(mtd);
192 u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
193
194 mcr &= ~TXX9_NDFMCR_ECC_ALL;
195 txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);
196 txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR);
197 ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR);
198 ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR);
199 ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR);
200 txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);
201 return 0;
202}
203
204static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
205{
206 struct platform_device *dev = mtd_to_platdev(mtd);
207 u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
208
209 mcr &= ~TXX9_NDFMCR_ECC_ALL;
210 txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_RESET, TXX9_NDFMCR);
211 txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);
212 txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_ON, TXX9_NDFMCR);
213}
214
215static void txx9ndfmc_initialize(struct platform_device *dev)
216{
217 struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
218 struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
219 int tmout = 100;
220
221 if (plat->flags & NDFMC_PLAT_FLAG_NO_RSTR)
222 ; /* no NDFRSTR. Write to NDFSPR resets the NDFMC. */
223 else {
224 /* reset NDFMC */
225 txx9ndfmc_write(dev,
226 txx9ndfmc_read(dev, TXX9_NDFRSTR) |
227 TXX9_NDFRSTR_RST,
228 TXX9_NDFRSTR);
229 while (txx9ndfmc_read(dev, TXX9_NDFRSTR) & TXX9_NDFRSTR_RST) {
230 if (--tmout == 0) {
231 dev_err(&dev->dev, "reset failed.\n");
232 break;
233 }
234 udelay(1);
235 }
236 }
237 /* setup Hold Time, Strobe Pulse Width */
238 txx9ndfmc_write(dev, (drvdata->hold << 4) | drvdata->spw, TXX9_NDFSPR);
239 txx9ndfmc_write(dev,
240 (plat->flags & NDFMC_PLAT_FLAG_USE_BSPRT) ?
241 TXX9_NDFMCR_BSPRT : 0, TXX9_NDFMCR);
242}
243
244#define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \
245 DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000)
246
247static int __init txx9ndfmc_probe(struct platform_device *dev)
248{
249 struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
250#ifdef CONFIG_MTD_PARTITIONS
251 static const char *probes[] = { "cmdlinepart", NULL };
252#endif
253 int hold, spw;
254 int i;
255 struct txx9ndfmc_drvdata *drvdata;
256 unsigned long gbusclk = plat->gbus_clock;
257 struct resource *res;
258
259 res = platform_get_resource(dev, IORESOURCE_MEM, 0);
260 if (!res)
261 return -ENODEV;
262 drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL);
263 if (!drvdata)
264 return -ENOMEM;
265 if (!devm_request_mem_region(&dev->dev, res->start,
266 resource_size(res), dev_name(&dev->dev)))
267 return -EBUSY;
268 drvdata->base = devm_ioremap(&dev->dev, res->start,
269 resource_size(res));
270 if (!drvdata->base)
271 return -EBUSY;
272
273 hold = plat->hold ?: 20; /* tDH */
274 spw = plat->spw ?: 90; /* max(tREADID, tWP, tRP) */
275
276 hold = TXX9NDFMC_NS_TO_CYC(gbusclk, hold);
277 spw = TXX9NDFMC_NS_TO_CYC(gbusclk, spw);
278 if (plat->flags & NDFMC_PLAT_FLAG_HOLDADD)
279 hold -= 2; /* actual hold time : (HOLD + 2) BUSCLK */
280 spw -= 1; /* actual wait time : (SPW + 1) BUSCLK */
281 hold = clamp(hold, 1, 15);
282 drvdata->hold = hold;
283 spw = clamp(spw, 1, 15);
284 drvdata->spw = spw;
285 dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n",
286 (gbusclk + 500000) / 1000000, hold, spw);
287
288 spin_lock_init(&drvdata->hw_control.lock);
289 init_waitqueue_head(&drvdata->hw_control.wq);
290
291 platform_set_drvdata(dev, drvdata);
292 txx9ndfmc_initialize(dev);
293
294 for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) {
295 struct txx9ndfmc_priv *txx9_priv;
296 struct nand_chip *chip;
297 struct mtd_info *mtd;
298#ifdef CONFIG_MTD_PARTITIONS
299 int nr_parts;
300#endif
301
302 if (!(plat->ch_mask & (1 << i)))
303 continue;
304 txx9_priv = kzalloc(sizeof(struct txx9ndfmc_priv),
305 GFP_KERNEL);
306 if (!txx9_priv) {
307 dev_err(&dev->dev, "Unable to allocate "
308 "TXx9 NDFMC MTD device structure.\n");
309 continue;
310 }
311 chip = &txx9_priv->chip;
312 mtd = &txx9_priv->mtd;
313 mtd->owner = THIS_MODULE;
314
315 mtd->priv = chip;
316
317 chip->read_byte = txx9ndfmc_read_byte;
318 chip->read_buf = txx9ndfmc_read_buf;
319 chip->write_buf = txx9ndfmc_write_buf;
320 chip->verify_buf = txx9ndfmc_verify_buf;
321 chip->cmd_ctrl = txx9ndfmc_cmd_ctrl;
322 chip->dev_ready = txx9ndfmc_dev_ready;
323 chip->ecc.calculate = txx9ndfmc_calculate_ecc;
324 chip->ecc.correct = nand_correct_data;
325 chip->ecc.hwctl = txx9ndfmc_enable_hwecc;
326 chip->ecc.mode = NAND_ECC_HW;
327 chip->ecc.size = 256;
328 chip->ecc.bytes = 3;
329 chip->chip_delay = 100;
330 chip->controller = &drvdata->hw_control;
331
332 chip->priv = txx9_priv;
333 txx9_priv->dev = dev;
334
335 if (plat->ch_mask != 1) {
336 txx9_priv->cs = i;
337 sprintf(txx9_priv->mtdname, "%s.%u",
338 dev_name(&dev->dev), i);
339 } else {
340 txx9_priv->cs = -1;
341 strcpy(txx9_priv->mtdname, dev_name(&dev->dev));
342 }
343 if (plat->wide_mask & (1 << i))
344 chip->options |= NAND_BUSWIDTH_16;
345
346 if (nand_scan(mtd, 1)) {
347 kfree(txx9_priv);
348 continue;
349 }
350 mtd->name = txx9_priv->mtdname;
351
352#ifdef CONFIG_MTD_PARTITIONS
353 nr_parts = parse_mtd_partitions(mtd, probes,
354 &drvdata->parts[i], 0);
355 if (nr_parts > 0)
356 add_mtd_partitions(mtd, drvdata->parts[i], nr_parts);
357#endif
358 add_mtd_device(mtd);
359 drvdata->mtds[i] = mtd;
360 }
361
362 return 0;
363}
364
365static int __exit txx9ndfmc_remove(struct platform_device *dev)
366{
367 struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
368 int i;
369
370 platform_set_drvdata(dev, NULL);
371 if (!drvdata)
372 return 0;
373 for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) {
374 struct mtd_info *mtd = drvdata->mtds[i];
375 struct nand_chip *chip;
376 struct txx9ndfmc_priv *txx9_priv;
377
378 if (!mtd)
379 continue;
380 chip = mtd->priv;
381 txx9_priv = chip->priv;
382
383#ifdef CONFIG_MTD_PARTITIONS
384 del_mtd_partitions(mtd);
385 kfree(drvdata->parts[i]);
386#endif
387 del_mtd_device(mtd);
388 kfree(txx9_priv);
389 }
390 return 0;
391}
392
393#ifdef CONFIG_PM
394static int txx9ndfmc_resume(struct platform_device *dev)
395{
396 if (platform_get_drvdata(dev))
397 txx9ndfmc_initialize(dev);
398 return 0;
399}
400#else
401#define txx9ndfmc_resume NULL
402#endif
403
404static struct platform_driver txx9ndfmc_driver = {
405 .remove = __exit_p(txx9ndfmc_remove),
406 .resume = txx9ndfmc_resume,
407 .driver = {
408 .name = "txx9ndfmc",
409 .owner = THIS_MODULE,
410 },
411};
412
413static int __init txx9ndfmc_init(void)
414{
415 return platform_driver_probe(&txx9ndfmc_driver, txx9ndfmc_probe);
416}
417
418static void __exit txx9ndfmc_exit(void)
419{
420 platform_driver_unregister(&txx9ndfmc_driver);
421}
422
423module_init(txx9ndfmc_init);
424module_exit(txx9ndfmc_exit);
425
426MODULE_LICENSE("GPL");
427MODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver");
428MODULE_ALIAS("platform:txx9ndfmc");