aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
authorSean MacLennan <smaclennan@pikatech.com>2008-12-10 08:16:34 -0500
committerDavid Woodhouse <David.Woodhouse@intel.com>2009-01-05 11:42:44 -0500
commita808ad3b0d28411e2838117c5b2ae680ae42483c (patch)
treeedd291f3873ca95e0fb2a434034d30669666b6f4 /drivers/mtd
parent647b0d3854d776485faea9de7e7ed7fd4bdc6f20 (diff)
[MTD] [NAND] ndfc driver
The current ndfc driver only compiles under arch/ppc. This arch was removed from the kernel. I notice the event entry for the ndfc in Kconfig has been removed in 2.6.28. This patch converts the ndfc to a proper OF (OpenFirmware) driver. I can give a working example of the DTS if needed. The patch has been in production use on the PIKA Warp Appliance and is in use by others. The Warp basically boots from NAND, so the ndfc driver is very important to us. Signed-off-by: Sean MacLennan <smaclennan@pikatech.com> Acked-By: Josh Boyer <jwboyer@linux.vnet.ibm.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/nand/Kconfig7
-rw-r--r--drivers/mtd/nand/ndfc.c269
2 files changed, 140 insertions, 136 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index f8ae0400c49..8b12e6e109d 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -163,6 +163,13 @@ config MTD_NAND_S3C2410_HWECC
163 incorrect ECC generation, and if using these, the default of 163 incorrect ECC generation, and if using these, the default of
164 software ECC is preferable. 164 software ECC is preferable.
165 165
166config MTD_NAND_NDFC
167 tristate "NDFC NanD Flash Controller"
168 depends on 4xx
169 select MTD_NAND_ECC_SMC
170 help
171 NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
172
166config MTD_NAND_S3C2410_CLKSTOP 173config MTD_NAND_S3C2410_CLKSTOP
167 bool "S3C2410 NAND IDLE clock stop" 174 bool "S3C2410 NAND IDLE clock stop"
168 depends on MTD_NAND_S3C2410 175 depends on MTD_NAND_S3C2410
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
index 955959eb02d..582cf80f555 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/ndfc.c
@@ -2,12 +2,20 @@
2 * drivers/mtd/ndfc.c 2 * drivers/mtd/ndfc.c
3 * 3 *
4 * Overview: 4 * Overview:
5 * Platform independend driver for NDFC (NanD Flash Controller) 5 * Platform independent driver for NDFC (NanD Flash Controller)
6 * integrated into EP440 cores 6 * integrated into EP440 cores
7 * 7 *
8 * Ported to an OF platform driver by Sean MacLennan
9 *
10 * The NDFC supports multiple chips, but this driver only supports a
11 * single chip since I do not have access to any boards with
12 * multiple chips.
13 *
8 * Author: Thomas Gleixner 14 * Author: Thomas Gleixner
9 * 15 *
10 * Copyright 2006 IBM 16 * Copyright 2006 IBM
17 * Copyright 2008 PIKA Technologies
18 * Sean MacLennan <smaclennan@pikatech.com>
11 * 19 *
12 * This program is free software; you can redistribute it and/or modify it 20 * 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 21 * under the terms of the GNU General Public License as published by the
@@ -21,27 +29,20 @@
21#include <linux/mtd/partitions.h> 29#include <linux/mtd/partitions.h>
22#include <linux/mtd/ndfc.h> 30#include <linux/mtd/ndfc.h>
23#include <linux/mtd/mtd.h> 31#include <linux/mtd/mtd.h>
24#include <linux/platform_device.h> 32#include <linux/of_platform.h>
25
26#include <asm/io.h> 33#include <asm/io.h>
27#ifdef CONFIG_40x
28#include <asm/ibm405.h>
29#else
30#include <asm/ibm44x.h>
31#endif
32
33struct ndfc_nand_mtd {
34 struct mtd_info mtd;
35 struct nand_chip chip;
36 struct platform_nand_chip *pl_chip;
37};
38 34
39static struct ndfc_nand_mtd ndfc_mtd[NDFC_MAX_BANKS];
40 35
41struct ndfc_controller { 36struct ndfc_controller {
42 void __iomem *ndfcbase; 37 struct of_device *ofdev;
43 struct nand_hw_control ndfc_control; 38 void __iomem *ndfcbase;
44 atomic_t childs_active; 39 struct mtd_info mtd;
40 struct nand_chip chip;
41 int chip_select;
42 struct nand_hw_control ndfc_control;
43#ifdef CONFIG_MTD_PARTITIONS
44 struct mtd_partition *parts;
45#endif
45}; 46};
46 47
47static struct ndfc_controller ndfc_ctrl; 48static struct ndfc_controller ndfc_ctrl;
@@ -50,17 +51,14 @@ static void ndfc_select_chip(struct mtd_info *mtd, int chip)
50{ 51{
51 uint32_t ccr; 52 uint32_t ccr;
52 struct ndfc_controller *ndfc = &ndfc_ctrl; 53 struct ndfc_controller *ndfc = &ndfc_ctrl;
53 struct nand_chip *nandchip = mtd->priv;
54 struct ndfc_nand_mtd *nandmtd = nandchip->priv;
55 struct platform_nand_chip *pchip = nandmtd->pl_chip;
56 54
57 ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR); 55 ccr = in_be32(ndfc->ndfcbase + NDFC_CCR);
58 if (chip >= 0) { 56 if (chip >= 0) {
59 ccr &= ~NDFC_CCR_BS_MASK; 57 ccr &= ~NDFC_CCR_BS_MASK;
60 ccr |= NDFC_CCR_BS(chip + pchip->chip_offset); 58 ccr |= NDFC_CCR_BS(chip + ndfc->chip_select);
61 } else 59 } else
62 ccr |= NDFC_CCR_RESET_CE; 60 ccr |= NDFC_CCR_RESET_CE;
63 __raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR); 61 out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
64} 62}
65 63
66static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) 64static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
@@ -80,7 +78,7 @@ static int ndfc_ready(struct mtd_info *mtd)
80{ 78{
81 struct ndfc_controller *ndfc = &ndfc_ctrl; 79 struct ndfc_controller *ndfc = &ndfc_ctrl;
82 80
83 return __raw_readl(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; 81 return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY;
84} 82}
85 83
86static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) 84static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode)
@@ -88,9 +86,9 @@ static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode)
88 uint32_t ccr; 86 uint32_t ccr;
89 struct ndfc_controller *ndfc = &ndfc_ctrl; 87 struct ndfc_controller *ndfc = &ndfc_ctrl;
90 88
91 ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR); 89 ccr = in_be32(ndfc->ndfcbase + NDFC_CCR);
92 ccr |= NDFC_CCR_RESET_ECC; 90 ccr |= NDFC_CCR_RESET_ECC;
93 __raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR); 91 out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
94 wmb(); 92 wmb();
95} 93}
96 94
@@ -102,9 +100,10 @@ static int ndfc_calculate_ecc(struct mtd_info *mtd,
102 uint8_t *p = (uint8_t *)&ecc; 100 uint8_t *p = (uint8_t *)&ecc;
103 101
104 wmb(); 102 wmb();
105 ecc = __raw_readl(ndfc->ndfcbase + NDFC_ECC); 103 ecc = in_be32(ndfc->ndfcbase + NDFC_ECC);
106 ecc_code[0] = p[1]; 104 /* The NDFC uses Smart Media (SMC) bytes order */
107 ecc_code[1] = p[2]; 105 ecc_code[0] = p[2];
106 ecc_code[1] = p[1];
108 ecc_code[2] = p[3]; 107 ecc_code[2] = p[3];
109 108
110 return 0; 109 return 0;
@@ -123,7 +122,7 @@ static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
123 uint32_t *p = (uint32_t *) buf; 122 uint32_t *p = (uint32_t *) buf;
124 123
125 for(;len > 0; len -= 4) 124 for(;len > 0; len -= 4)
126 *p++ = __raw_readl(ndfc->ndfcbase + NDFC_DATA); 125 *p++ = in_be32(ndfc->ndfcbase + NDFC_DATA);
127} 126}
128 127
129static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) 128static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
@@ -132,7 +131,7 @@ static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
132 uint32_t *p = (uint32_t *) buf; 131 uint32_t *p = (uint32_t *) buf;
133 132
134 for(;len > 0; len -= 4) 133 for(;len > 0; len -= 4)
135 __raw_writel(*p++, ndfc->ndfcbase + NDFC_DATA); 134 out_be32(ndfc->ndfcbase + NDFC_DATA, *p++);
136} 135}
137 136
138static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) 137static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
@@ -141,7 +140,7 @@ static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
141 uint32_t *p = (uint32_t *) buf; 140 uint32_t *p = (uint32_t *) buf;
142 141
143 for(;len > 0; len -= 4) 142 for(;len > 0; len -= 4)
144 if (*p++ != __raw_readl(ndfc->ndfcbase + NDFC_DATA)) 143 if (*p++ != in_be32(ndfc->ndfcbase + NDFC_DATA))
145 return -EFAULT; 144 return -EFAULT;
146 return 0; 145 return 0;
147} 146}
@@ -149,10 +148,19 @@ static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
149/* 148/*
150 * Initialize chip structure 149 * Initialize chip structure
151 */ 150 */
152static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) 151static int ndfc_chip_init(struct ndfc_controller *ndfc,
152 struct device_node *node)
153{ 153{
154 struct ndfc_controller *ndfc = &ndfc_ctrl; 154#ifdef CONFIG_MTD_PARTITIONS
155 struct nand_chip *chip = &mtd->chip; 155#ifdef CONFIG_MTD_CMDLINE_PARTS
156 static const char *part_types[] = { "cmdlinepart", NULL };
157#else
158 static const char *part_types[] = { NULL };
159#endif
160#endif
161 struct device_node *flash_np;
162 struct nand_chip *chip = &ndfc->chip;
163 int ret;
156 164
157 chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA; 165 chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA;
158 chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; 166 chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA;
@@ -160,8 +168,6 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd)
160 chip->dev_ready = ndfc_ready; 168 chip->dev_ready = ndfc_ready;
161 chip->select_chip = ndfc_select_chip; 169 chip->select_chip = ndfc_select_chip;
162 chip->chip_delay = 50; 170 chip->chip_delay = 50;
163 chip->priv = mtd;
164 chip->options = mtd->pl_chip->options;
165 chip->controller = &ndfc->ndfc_control; 171 chip->controller = &ndfc->ndfc_control;
166 chip->read_buf = ndfc_read_buf; 172 chip->read_buf = ndfc_read_buf;
167 chip->write_buf = ndfc_write_buf; 173 chip->write_buf = ndfc_write_buf;
@@ -172,143 +178,136 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd)
172 chip->ecc.mode = NAND_ECC_HW; 178 chip->ecc.mode = NAND_ECC_HW;
173 chip->ecc.size = 256; 179 chip->ecc.size = 256;
174 chip->ecc.bytes = 3; 180 chip->ecc.bytes = 3;
175 chip->ecclayout = chip->ecc.layout = mtd->pl_chip->ecclayout;
176 mtd->mtd.priv = chip;
177 mtd->mtd.owner = THIS_MODULE;
178}
179
180static int ndfc_chip_probe(struct platform_device *pdev)
181{
182 struct platform_nand_chip *nc = pdev->dev.platform_data;
183 struct ndfc_chip_settings *settings = nc->priv;
184 struct ndfc_controller *ndfc = &ndfc_ctrl;
185 struct ndfc_nand_mtd *nandmtd;
186
187 if (nc->chip_offset >= NDFC_MAX_BANKS || nc->nr_chips > NDFC_MAX_BANKS)
188 return -EINVAL;
189
190 /* Set the bank settings */
191 __raw_writel(settings->bank_settings,
192 ndfc->ndfcbase + NDFC_BCFG0 + (nc->chip_offset << 2));
193 181
194 nandmtd = &ndfc_mtd[pdev->id]; 182 ndfc->mtd.priv = chip;
195 if (nandmtd->pl_chip) 183 ndfc->mtd.owner = THIS_MODULE;
196 return -EBUSY;
197 184
198 nandmtd->pl_chip = nc; 185 flash_np = of_get_next_child(node, NULL);
199 ndfc_chip_init(nandmtd); 186 if (!flash_np)
200
201 /* Scan for chips */
202 if (nand_scan(&nandmtd->mtd, nc->nr_chips)) {
203 nandmtd->pl_chip = NULL;
204 return -ENODEV; 187 return -ENODEV;
188
189 ndfc->mtd.name = kasprintf(GFP_KERNEL, "%s.%s",
190 ndfc->ofdev->dev.bus_id, flash_np->name);
191 if (!ndfc->mtd.name) {
192 ret = -ENOMEM;
193 goto err;
205 } 194 }
206 195
207#ifdef CONFIG_MTD_PARTITIONS 196 ret = nand_scan(&ndfc->mtd, 1);
208 printk("Number of partitions %d\n", nc->nr_partitions); 197 if (ret)
209 if (nc->nr_partitions) { 198 goto err;
210 /* Add the full device, so complete dumps can be made */
211 add_mtd_device(&nandmtd->mtd);
212 add_mtd_partitions(&nandmtd->mtd, nc->partitions,
213 nc->nr_partitions);
214 199
215 } else 200#ifdef CONFIG_MTD_PARTITIONS
216#else 201 ret = parse_mtd_partitions(&ndfc->mtd, part_types, &ndfc->parts, 0);
217 add_mtd_device(&nandmtd->mtd); 202 if (ret < 0)
203 goto err;
204
205#ifdef CONFIG_MTD_OF_PARTS
206 if (ret == 0) {
207 ret = of_mtd_parse_partitions(&ndfc->ofdev->dev, flash_np,
208 &ndfc->parts);
209 if (ret < 0)
210 goto err;
211 }
218#endif 212#endif
219 213
220 atomic_inc(&ndfc->childs_active); 214 if (ret > 0)
221 return 0; 215 ret = add_mtd_partitions(&ndfc->mtd, ndfc->parts, ret);
222} 216 else
217#endif
218 ret = add_mtd_device(&ndfc->mtd);
223 219
224static int ndfc_chip_remove(struct platform_device *pdev) 220err:
225{ 221 of_node_put(flash_np);
226 return 0; 222 if (ret)
223 kfree(ndfc->mtd.name);
224 return ret;
227} 225}
228 226
229static int ndfc_nand_probe(struct platform_device *pdev) 227static int __devinit ndfc_probe(struct of_device *ofdev,
228 const struct of_device_id *match)
230{ 229{
231 struct platform_nand_ctrl *nc = pdev->dev.platform_data;
232 struct ndfc_controller_settings *settings = nc->priv;
233 struct resource *res = pdev->resource;
234 struct ndfc_controller *ndfc = &ndfc_ctrl; 230 struct ndfc_controller *ndfc = &ndfc_ctrl;
235 unsigned long long phys = settings->ndfc_erpn | res->start; 231 const u32 *reg;
232 u32 ccr;
233 int err, len;
236 234
237#ifndef CONFIG_PHYS_64BIT 235 spin_lock_init(&ndfc->ndfc_control.lock);
238 ndfc->ndfcbase = ioremap((phys_addr_t)phys, res->end - res->start + 1); 236 init_waitqueue_head(&ndfc->ndfc_control.wq);
239#else 237 ndfc->ofdev = ofdev;
240 ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1); 238 dev_set_drvdata(&ofdev->dev, ndfc);
241#endif 239
240 /* Read the reg property to get the chip select */
241 reg = of_get_property(ofdev->node, "reg", &len);
242 if (reg == NULL || len != 12) {
243 dev_err(&ofdev->dev, "unable read reg property (%d)\n", len);
244 return -ENOENT;
245 }
246 ndfc->chip_select = reg[0];
247
248 ndfc->ndfcbase = of_iomap(ofdev->node, 0);
242 if (!ndfc->ndfcbase) { 249 if (!ndfc->ndfcbase) {
243 printk(KERN_ERR "NDFC: ioremap failed\n"); 250 dev_err(&ofdev->dev, "failed to get memory\n");
244 return -EIO; 251 return -EIO;
245 } 252 }
246 253
247 __raw_writel(settings->ccr_settings, ndfc->ndfcbase + NDFC_CCR); 254 ccr = NDFC_CCR_BS(ndfc->chip_select);
248 255
249 spin_lock_init(&ndfc->ndfc_control.lock); 256 /* It is ok if ccr does not exist - just default to 0 */
250 init_waitqueue_head(&ndfc->ndfc_control.wq); 257 reg = of_get_property(ofdev->node, "ccr", NULL);
258 if (reg)
259 ccr |= *reg;
251 260
252 platform_set_drvdata(pdev, ndfc); 261 out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
253 262
254 printk("NDFC NAND Driver initialized. Chip-Rev: 0x%08x\n", 263 /* Set the bank settings if given */
255 __raw_readl(ndfc->ndfcbase + NDFC_REVID)); 264 reg = of_get_property(ofdev->node, "bank-settings", NULL);
265 if (reg) {
266 int offset = NDFC_BCFG0 + (ndfc->chip_select << 2);
267 out_be32(ndfc->ndfcbase + offset, *reg);
268 }
269
270 err = ndfc_chip_init(ndfc, ofdev->node);
271 if (err) {
272 iounmap(ndfc->ndfcbase);
273 return err;
274 }
256 275
257 return 0; 276 return 0;
258} 277}
259 278
260static int ndfc_nand_remove(struct platform_device *pdev) 279static int __devexit ndfc_remove(struct of_device *ofdev)
261{ 280{
262 struct ndfc_controller *ndfc = platform_get_drvdata(pdev); 281 struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev);
263 282
264 if (atomic_read(&ndfc->childs_active)) 283 nand_release(&ndfc->mtd);
265 return -EBUSY;
266 284
267 if (ndfc) {
268 platform_set_drvdata(pdev, NULL);
269 iounmap(ndfc_ctrl.ndfcbase);
270 ndfc_ctrl.ndfcbase = NULL;
271 }
272 return 0; 285 return 0;
273} 286}
274 287
275/* driver device registration */ 288static const struct of_device_id ndfc_match[] = {
276 289 { .compatible = "ibm,ndfc", },
277static struct platform_driver ndfc_chip_driver = { 290 {}
278 .probe = ndfc_chip_probe,
279 .remove = ndfc_chip_remove,
280 .driver = {
281 .name = "ndfc-chip",
282 .owner = THIS_MODULE,
283 },
284}; 291};
292MODULE_DEVICE_TABLE(of, ndfc_match);
285 293
286static struct platform_driver ndfc_nand_driver = { 294static struct of_platform_driver ndfc_driver = {
287 .probe = ndfc_nand_probe, 295 .driver = {
288 .remove = ndfc_nand_remove, 296 .name = "ndfc",
289 .driver = {
290 .name = "ndfc-nand",
291 .owner = THIS_MODULE,
292 }, 297 },
298 .match_table = ndfc_match,
299 .probe = ndfc_probe,
300 .remove = __devexit_p(ndfc_remove),
293}; 301};
294 302
295static int __init ndfc_nand_init(void) 303static int __init ndfc_nand_init(void)
296{ 304{
297 int ret; 305 return of_register_platform_driver(&ndfc_driver);
298
299 spin_lock_init(&ndfc_ctrl.ndfc_control.lock);
300 init_waitqueue_head(&ndfc_ctrl.ndfc_control.wq);
301
302 ret = platform_driver_register(&ndfc_nand_driver);
303 if (!ret)
304 ret = platform_driver_register(&ndfc_chip_driver);
305 return ret;
306} 306}
307 307
308static void __exit ndfc_nand_exit(void) 308static void __exit ndfc_nand_exit(void)
309{ 309{
310 platform_driver_unregister(&ndfc_chip_driver); 310 of_unregister_platform_driver(&ndfc_driver);
311 platform_driver_unregister(&ndfc_nand_driver);
312} 311}
313 312
314module_init(ndfc_nand_init); 313module_init(ndfc_nand_init);
@@ -316,6 +315,4 @@ module_exit(ndfc_nand_exit);
316 315
317MODULE_LICENSE("GPL"); 316MODULE_LICENSE("GPL");
318MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); 317MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
319MODULE_DESCRIPTION("Platform driver for NDFC"); 318MODULE_DESCRIPTION("OF Platform driver for NDFC");
320MODULE_ALIAS("platform:ndfc-chip");
321MODULE_ALIAS("platform:ndfc-nand");