aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@cruncher.tec.linutronix.de>2006-05-23 05:43:28 -0400
committerThomas Gleixner <tglx@cruncher.tec.linutronix.de>2006-05-23 05:43:28 -0400
commitce4c61f184864991881ec789f7524f4b332eaafc (patch)
tree405bc1011d919c3b83f252c49a51f40e7d5de536 /drivers/mtd/nand
parent41796c2ea9b74cdf3bc2c368193d15b8ae8950ca (diff)
[MTD] Add support for NDFC NAND controller
NDFC NAND Flash controller is embedded in PPC EP44x SoCs. Add platform driver based support. Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r--drivers/mtd/nand/Kconfig6
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/ndfc.c317
3 files changed, 324 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 4d235b91267d..c2cb87fc4cb8 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -129,6 +129,12 @@ config MTD_NAND_S3C2410_HWECC
129 currently not be able to switch to software, as there is no 129 currently not be able to switch to software, as there is no
130 implementation for ECC method used by the S3C2410 130 implementation for ECC method used by the S3C2410
131 131
132config MTD_NAND_NDFC
133 tristate "NDFC NanD Flash Controller"
134 depends on MTD_NAND && 44x
135 help
136 NDFC Nand Flash Controllers are integrated in EP44x SoCs
137
132config MTD_NAND_DISKONCHIP 138config MTD_NAND_DISKONCHIP
133 tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)" 139 tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
134 depends on MTD_NAND && EXPERIMENTAL 140 depends on MTD_NAND && EXPERIMENTAL
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 33475087dbff..f74759351c91 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -21,5 +21,6 @@ obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
21obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o 21obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o
22obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o 22obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
23obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o 23obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
24obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
24 25
25nand-objs = nand_base.o nand_bbt.o 26nand-objs = nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
new file mode 100644
index 000000000000..22fd682b70ca
--- /dev/null
+++ b/drivers/mtd/nand/ndfc.c
@@ -0,0 +1,317 @@
1/*
2 * drivers/mtd/ndfc.c
3 *
4 * Overview:
5 * Platform independend driver for NDFC (NanD Flash Controller)
6 * integrated into EP440 cores
7 *
8 * Author: Thomas Gleixner
9 *
10 * Copyright 2006 IBM
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version.
16 *
17 */
18#include <linux/module.h>
19#include <linux/mtd/nand.h>
20#include <linux/mtd/nand_ecc.h>
21#include <linux/mtd/partitions.h>
22#include <linux/mtd/ndfc.h>
23#include <linux/mtd/ubi.h>
24#include <linux/mtd/mtd.h>
25#include <linux/platform_device.h>
26
27#include <asm/io.h>
28#include <asm/ibm44x.h>
29
30struct ndfc_nand_mtd {
31 struct mtd_info mtd;
32 struct nand_chip chip;
33 struct platform_nand_chip *pl_chip;
34};
35
36static struct ndfc_nand_mtd ndfc_mtd[NDFC_MAX_BANKS];
37
38struct ndfc_controller {
39 void __iomem *ndfcbase;
40 struct nand_hw_control ndfc_control;
41 atomic_t childs_active;
42};
43
44static struct ndfc_controller ndfc_ctrl;
45
46static void ndfc_select_chip(struct mtd_info *mtd, int chip)
47{
48 uint32_t ccr;
49 struct ndfc_controller *ndfc = &ndfc_ctrl;
50 struct nand_chip *nandchip = mtd->priv;
51 struct ndfc_nand_mtd *nandmtd = nandchip->priv;
52 struct platform_nand_chip *pchip = nandmtd->pl_chip;
53
54 ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR);
55 if (chip >= 0) {
56 ccr &= ~NDFC_CCR_BS_MASK;
57 ccr |= NDFC_CCR_BS(chip + pchip->chip_offset);
58 } else
59 ccr |= NDFC_CCR_RESET_CE;
60 writel(ccr, ndfc->ndfcbase + NDFC_CCR);
61}
62
63static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd)
64{
65 struct ndfc_controller *ndfc = &ndfc_ctrl;
66 struct nand_chip *chip = mtd->priv;
67
68 switch (cmd) {
69 case NAND_CTL_SETCLE:
70 chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_CMD;
71 break;
72 case NAND_CTL_SETALE:
73 chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_ALE;
74 break;
75 default:
76 chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA;
77 break;
78 }
79}
80
81static int ndfc_ready(struct mtd_info *mtd)
82{
83 struct ndfc_controller *ndfc = &ndfc_ctrl;
84
85 return __raw_readl(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY;
86}
87
88static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode)
89{
90 uint32_t ccr;
91 struct ndfc_controller *ndfc = &ndfc_ctrl;
92
93 ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR);
94 ccr |= NDFC_CCR_RESET_ECC;
95 __raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR);
96 wmb();
97}
98
99static int ndfc_calculate_ecc(struct mtd_info *mtd,
100 const u_char *dat, u_char *ecc_code)
101{
102 struct ndfc_controller *ndfc = &ndfc_ctrl;
103 uint32_t ecc;
104 uint8_t *p = (uint8_t *)&ecc;
105
106 wmb();
107 ecc = __raw_readl(ndfc->ndfcbase + NDFC_ECC);
108 ecc_code[0] = p[1];
109 ecc_code[1] = p[2];
110 ecc_code[2] = p[3];
111
112 return 0;
113}
114
115/*
116 * Speedups for buffer read/write/verify
117 *
118 * NDFC allows 32bit read/write of data. So we can speed up the buffer
119 * functions. No further checking, as nand_base will always read/write
120 * page aligned.
121 */
122static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
123{
124 struct ndfc_controller *ndfc = &ndfc_ctrl;
125 uint32_t *p = (uint32_t *) buf;
126
127 for(;len > 0; len -= 4)
128 *p++ = __raw_readl(ndfc->ndfcbase + NDFC_DATA);
129}
130
131static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
132{
133 struct ndfc_controller *ndfc = &ndfc_ctrl;
134 uint32_t *p = (uint32_t *) buf;
135
136 for(;len > 0; len -= 4)
137 __raw_writel(*p++, ndfc->ndfcbase + NDFC_DATA);
138}
139
140static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
141{
142 struct ndfc_controller *ndfc = &ndfc_ctrl;
143 uint32_t *p = (uint32_t *) buf;
144
145 for(;len > 0; len -= 4)
146 if (*p++ != __raw_readl(ndfc->ndfcbase + NDFC_DATA))
147 return -EFAULT;
148 return 0;
149}
150
151/*
152 * Initialize chip structure
153 */
154static void ndfc_chip_init(struct ndfc_nand_mtd *mtd)
155{
156 struct ndfc_controller *ndfc = &ndfc_ctrl;
157 struct nand_chip *chip = &mtd->chip;
158
159 chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA;
160 chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA;
161 chip->hwcontrol = ndfc_hwcontrol;
162 chip->dev_ready = ndfc_ready;
163 chip->select_chip = ndfc_select_chip;
164 chip->chip_delay = 50;
165 chip->priv = mtd;
166 chip->options = mtd->pl_chip->options;
167 chip->controller = &ndfc->ndfc_control;
168 chip->read_buf = ndfc_read_buf;
169 chip->write_buf = ndfc_write_buf;
170 chip->verify_buf = ndfc_verify_buf;
171 chip->correct_data = nand_correct_data;
172 chip->enable_hwecc = ndfc_enable_hwecc;
173 chip->calculate_ecc = ndfc_calculate_ecc;
174 chip->eccmode = NAND_ECC_HW3_256;
175 chip->autooob = mtd->pl_chip->autooob;
176 mtd->mtd.priv = chip;
177 mtd->mtd.owner = THIS_MODULE;
178}
179
180static int ndfc_chip_probe(struct platform_device *pdev)
181{
182 int rc;
183 struct platform_nand_chip *nc = pdev->dev.platform_data;
184 struct ndfc_chip_settings *settings = nc->priv;
185 struct ndfc_controller *ndfc = &ndfc_ctrl;
186 struct ndfc_nand_mtd *nandmtd;
187
188 if (nc->chip_offset >= NDFC_MAX_BANKS || nc->nr_chips > NDFC_MAX_BANKS)
189 return -EINVAL;
190
191 /* Set the bank settings */
192 __raw_writel(settings->bank_settings,
193 ndfc->ndfcbase + NDFC_BCFG0 + (nc->chip_offset << 2));
194
195 nandmtd = &ndfc_mtd[pdev->id];
196 if (nandmtd->pl_chip)
197 return -EBUSY;
198
199 nandmtd->pl_chip = nc;
200 ndfc_chip_init(nandmtd);
201
202 /* Scan for chips */
203 if (nand_scan(&nandmtd->mtd, nc->nr_chips)) {
204 nandmtd->pl_chip = NULL;
205 return -ENODEV;
206 }
207
208#ifdef CONFIG_MTD_PARTITIONS
209 printk("Number of partitions %d\n", nc->nr_partitions);
210 if (nc->nr_partitions) {
211 struct mtd_info *mtd_ubi;
212 nc->partitions[NAND_PARTS_CONTENT_IDX].mtdp = &mtd_ubi;
213
214 add_mtd_device(&nandmtd->mtd); /* for testing */
215 add_mtd_partitions(&nandmtd->mtd,
216 nc->partitions,
217 nc->nr_partitions);
218
219 add_mtd_device(mtd_ubi);
220
221 } else
222#else
223 add_mtd_device(&nandmtd->mtd);
224#endif
225
226 atomic_inc(&ndfc->childs_active);
227 return 0;
228}
229
230static int ndfc_chip_remove(struct platform_device *pdev)
231{
232 return 0;
233}
234
235static int ndfc_nand_probe(struct platform_device *pdev)
236{
237 struct platform_nand_ctrl *nc = pdev->dev.platform_data;
238 struct ndfc_controller_settings *settings = nc->priv;
239 struct resource *res = pdev->resource;
240 struct ndfc_controller *ndfc = &ndfc_ctrl;
241 unsigned long long phys = NDFC_PHYSADDR_OFFS | res->start;
242
243 ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1);
244 if (!ndfc->ndfcbase) {
245 printk(KERN_ERR "NDFC: ioremap failed\n");
246 return -EIO;
247 }
248
249 __raw_writel(settings->ccr_settings, ndfc->ndfcbase + NDFC_CCR);
250
251 spin_lock_init(&ndfc->ndfc_control.lock);
252 init_waitqueue_head(&ndfc->ndfc_control.wq);
253
254 platform_set_drvdata(pdev, ndfc);
255
256 printk("NDFC NAND Driver initialized. Chip-Rev: 0x%08x\n",
257 __raw_readl(ndfc->ndfcbase + NDFC_REVID));
258
259 return 0;
260}
261
262static int ndfc_nand_remove(struct platform_device *pdev)
263{
264 struct ndfc_controller *ndfc = platform_get_drvdata(pdev);
265
266 if (atomic_read(&ndfc->childs_active))
267 return -EBUSY;
268
269 if (ndfc) {
270 platform_set_drvdata(pdev, NULL);
271 iounmap(ndfc_ctrl.ndfcbase);
272 ndfc_ctrl.ndfcbase = NULL;
273 }
274 return 0;
275}
276
277/* driver device registration */
278
279static struct platform_driver ndfc_chip_driver = {
280 .probe = ndfc_chip_probe,
281 .remove = ndfc_chip_remove,
282 .driver = {
283 .name = "ndfc-chip",
284 .owner = THIS_MODULE,
285 },
286};
287
288static struct platform_driver ndfc_nand_driver = {
289 .probe = ndfc_nand_probe,
290 .remove = ndfc_nand_remove,
291 .driver = {
292 .name = "ndfc-nand",
293 .owner = THIS_MODULE,
294 },
295};
296
297static int __init ndfc_nand_init(void)
298{
299 int ret = platform_driver_register(&ndfc_nand_driver);
300
301 if (!ret)
302 ret = platform_driver_register(&ndfc_chip_driver);
303 return ret;
304}
305
306static void __exit ndfc_nand_exit(void)
307{
308 platform_driver_unregister(&ndfc_chip_driver);
309 platform_driver_unregister(&ndfc_nand_driver);
310}
311
312module_init(ndfc_nand_init);
313module_exit(ndfc_nand_exit);
314
315MODULE_LICENSE("GPL");
316MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
317MODULE_DESCRIPTION("Platform driver for NDFC");