aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/mtd/nand/Kconfig6
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/ndfc.c317
-rw-r--r--include/linux/mtd/ndfc.h66
4 files changed, 390 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");
diff --git a/include/linux/mtd/ndfc.h b/include/linux/mtd/ndfc.h
new file mode 100644
index 000000000000..31d61f07d768
--- /dev/null
+++ b/include/linux/mtd/ndfc.h
@@ -0,0 +1,66 @@
1/*
2 * linux/include/linux/mtd/ndfc.h
3 *
4 * Copyright (c) 2006 Thomas Gleixner <tglx@linutronix.de>
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 version 2 as
8 * published by the Free Software Foundation.
9 *
10 * Info:
11 * Contains defines, datastructures for ndfc nand controller
12 *
13 */
14#ifndef __LINUX_MTD_NDFC_H
15#define __LINUX_MTD_NDFC_H
16
17/* NDFC Register definitions */
18#define NDFC_CMD 0x00
19#define NDFC_ALE 0x04
20#define NDFC_DATA 0x08
21#define NDFC_ECC 0x10
22#define NDFC_BCFG0 0x30
23#define NDFC_BCFG1 0x34
24#define NDFC_BCFG2 0x38
25#define NDFC_BCFG3 0x3c
26#define NDFC_CCR 0x40
27#define NDFC_STAT 0x44
28#define NDFC_HWCTL 0x48
29#define NDFC_REVID 0x50
30
31#define NDFC_STAT_IS_READY 0x01000000
32
33#define NDFC_CCR_RESET_CE 0x80000000 /* CE Reset */
34#define NDFC_CCR_RESET_ECC 0x40000000 /* ECC Reset */
35#define NDFC_CCR_RIE 0x20000000 /* Interrupt Enable on Device Rdy */
36#define NDFC_CCR_REN 0x10000000 /* Enable wait for Rdy in LinearR */
37#define NDFC_CCR_ROMEN 0x08000000 /* Enable ROM In LinearR */
38#define NDFC_CCR_ARE 0x04000000 /* Auto-Read Enable */
39#define NDFC_CCR_BS(x) (((x) & 0x3) << 24) /* Select Bank on CE[x] */
40#define NDFC_CCR_BS_MASK 0x03000000 /* Select Bank */
41#define NDFC_CCR_ARAC0 0x00000000 /* 3 Addr, 1 Col 2 Row 512b page */
42#define NDFC_CCR_ARAC1 0x00001000 /* 4 Addr, 1 Col 3 Row 512b page */
43#define NDFC_CCR_ARAC2 0x00002000 /* 4 Addr, 2 Col 2 Row 2K page */
44#define NDFC_CCR_ARAC3 0x00003000 /* 5 Addr, 2 Col 3 Row 2K page */
45#define NDFC_CCR_ARAC_MASK 0x00003000 /* Auto-Read mode Addr Cycles */
46#define NDFC_CCR_RPG 0x0000C000 /* Auto-Read Page */
47#define NDFC_CCR_EBCC 0x00000004 /* EBC Configuration Completed */
48#define NDFC_CCR_DHC 0x00000002 /* Direct Hardware Control Enable */
49
50#define NDFC_BxCFG_EN 0x80000000 /* Bank Enable */
51#define NDFC_BxCFG_CED 0x40000000 /* nCE Style */
52#define NDFC_BxCFG_SZ_MASK 0x08000000 /* Bank Size */
53#define NDFC_BxCFG_SZ_8BIT 0x00000000 /* 8bit */
54#define NDFC_BxCFG_SZ_16BIT 0x08000000 /* 16bit */
55
56#define NDFC_MAX_BANKS 4
57
58struct ndfc_controller_settings {
59 uint32_t ccr_settings;
60};
61
62struct ndfc_chip_settings {
63 uint32_t bank_settings;
64};
65
66#endif