aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBen Dooks <ben@simtec.co.uk>2005-06-20 07:48:25 -0400
committerThomas Gleixner <tglx@mtd.linutronix.de>2005-06-29 08:30:47 -0400
commita4f957f16d41b9ff944dddd84c4892496a129f68 (patch)
tree16b2984a1c648cd2bc5e5cd4ba85bc24ae8fa8b3 /drivers
parentd7e78d4f2173298c34e88f496c3acea247feec61 (diff)
[MTD] NAND: s3c24xx updates
Fix error in timing generation, Tacls is only in the range 0..3 Add proper support for the s3c2440 NAND controller, which has now been tested on several s3c2440 implementations. Signed-off-by: Ben Dooks <ben@simtec.co.uk> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/nand/Kconfig7
-rw-r--r--drivers/mtd/nand/s3c2410.c180
2 files changed, 152 insertions, 35 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index f7801eb730ce..94b1d0e3ec85 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -1,5 +1,5 @@
1# drivers/mtd/nand/Kconfig 1# drivers/mtd/nand/Kconfig
2# $Id: Kconfig,v 1.26 2005/01/05 12:42:24 dwmw2 Exp $ 2# $Id: Kconfig,v 1.31 2005/06/20 12:03:21 bjd Exp $
3 3
4menu "NAND Flash Device Drivers" 4menu "NAND Flash Device Drivers"
5 depends on MTD!=n 5 depends on MTD!=n
@@ -95,10 +95,11 @@ config MTD_NAND_PPCHAMELEONEVB
95 This enables the NAND flash driver on the PPChameleon EVB Board. 95 This enables the NAND flash driver on the PPChameleon EVB Board.
96 96
97config MTD_NAND_S3C2410 97config MTD_NAND_S3C2410
98 tristate "NAND Flash support for S3C2410 SoC" 98 tristate "NAND Flash support for S3C2410/S3C2440 SoC"
99 depends on ARCH_S3C2410 && MTD_NAND 99 depends on ARCH_S3C2410 && MTD_NAND
100 help 100 help
101 This enables the NAND flash controller on the S3C2410. 101 This enables the NAND flash controller on the S3C2410 and S3C2440
102 SoCs
102 103
103 No board specfic support is done by this driver, each board 104 No board specfic support is done by this driver, each board
104 must advertise a platform_device for the driver to attach. 105 must advertise a platform_device for the driver to attach.
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 64b1d95e3e37..630a9c0edf31 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -1,10 +1,10 @@
1/* linux/drivers/mtd/nand/s3c2410.c 1/* linux/drivers/mtd/nand/s3c2410.c
2 * 2 *
3 * Copyright (c) 2004 Simtec Electronics 3 * Copyright (c) 2004,2005 Simtec Electronics
4 * http://www.simtec.co.uk/products/SWLINUX/ 4 * http://www.simtec.co.uk/products/SWLINUX/
5 * Ben Dooks <ben@simtec.co.uk> 5 * Ben Dooks <ben@simtec.co.uk>
6 * 6 *
7 * Samsung S3C2410 NAND driver 7 * Samsung S3C2410/S3C240 NAND driver
8 * 8 *
9 * Changelog: 9 * Changelog:
10 * 21-Sep-2004 BJD Initial version 10 * 21-Sep-2004 BJD Initial version
@@ -13,8 +13,11 @@
13 * 12-Oct-2004 BJD Fixed errors in use of platform data 13 * 12-Oct-2004 BJD Fixed errors in use of platform data
14 * 18-Feb-2005 BJD Fix sparse errors 14 * 18-Feb-2005 BJD Fix sparse errors
15 * 14-Mar-2005 BJD Applied tglx's code reduction patch 15 * 14-Mar-2005 BJD Applied tglx's code reduction patch
16 * 02-May-2005 BJD Fixed s3c2440 support
17 * 02-May-2005 BJD Reduced hwcontrol decode
18 * 20-Jun-2005 BJD Updated s3c2440 support, fixed timing bug
16 * 19 *
17 * $Id: s3c2410.c,v 1.12 2005/03/17 11:31:26 bjd Exp $ 20 * $Id: s3c2410.c,v 1.13 2005/06/20 11:48:21 bjd Exp $
18 * 21 *
19 * This program is free software; you can redistribute it and/or modify 22 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by 23 * it under the terms of the GNU General Public License as published by
@@ -104,6 +107,8 @@ struct s3c2410_nand_info {
104 struct clk *clk; 107 struct clk *clk;
105 void __iomem *regs; 108 void __iomem *regs;
106 int mtd_count; 109 int mtd_count;
110
111 unsigned char is_s3c2440;
107}; 112};
108 113
109/* conversion functions */ 114/* conversion functions */
@@ -168,12 +173,12 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
168 /* calculate the timing information for the controller */ 173 /* calculate the timing information for the controller */
169 174
170 if (plat != NULL) { 175 if (plat != NULL) {
171 tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8); 176 tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 4);
172 twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); 177 twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
173 twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8); 178 twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8);
174 } else { 179 } else {
175 /* default timings */ 180 /* default timings */
176 tacls = 8; 181 tacls = 4;
177 twrph0 = 8; 182 twrph0 = 8;
178 twrph1 = 8; 183 twrph1 = 8;
179 } 184 }
@@ -188,10 +193,16 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
188 to_ns(twrph0, clkrate), 193 to_ns(twrph0, clkrate),
189 to_ns(twrph1, clkrate)); 194 to_ns(twrph1, clkrate));
190 195
191 cfg = S3C2410_NFCONF_EN; 196 if (!info->is_s3c2440) {
192 cfg |= S3C2410_NFCONF_TACLS(tacls-1); 197 cfg = S3C2410_NFCONF_EN;
193 cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1); 198 cfg |= S3C2410_NFCONF_TACLS(tacls-1);
194 cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1); 199 cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1);
200 cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1);
201 } else {
202 cfg = S3C2440_NFCONF_TACLS(tacls-1);
203 cfg |= S3C2440_NFCONF_TWRPH0(twrph0-1);
204 cfg |= S3C2440_NFCONF_TWRPH1(twrph1-1);
205 }
195 206
196 pr_debug(PFX "NF_CONF is 0x%lx\n", cfg); 207 pr_debug(PFX "NF_CONF is 0x%lx\n", cfg);
197 208
@@ -206,15 +217,20 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
206 struct s3c2410_nand_info *info; 217 struct s3c2410_nand_info *info;
207 struct s3c2410_nand_mtd *nmtd; 218 struct s3c2410_nand_mtd *nmtd;
208 struct nand_chip *this = mtd->priv; 219 struct nand_chip *this = mtd->priv;
220 void __iomem *reg;
209 unsigned long cur; 221 unsigned long cur;
222 unsigned long bit;
210 223
211 nmtd = this->priv; 224 nmtd = this->priv;
212 info = nmtd->info; 225 info = nmtd->info;
213 226
214 cur = readl(info->regs + S3C2410_NFCONF); 227 bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE;
228 reg = info->regs+((info->is_s3c2440) ? S3C2440_NFCONT:S3C2410_NFCONF);
229
230 cur = readl(reg);
215 231
216 if (chip == -1) { 232 if (chip == -1) {
217 cur |= S3C2410_NFCONF_nFCE; 233 cur |= bit;
218 } else { 234 } else {
219 if (chip > nmtd->set->nr_chips) { 235 if (chip > nmtd->set->nr_chips) {
220 printk(KERN_ERR PFX "chip %d out of range\n", chip); 236 printk(KERN_ERR PFX "chip %d out of range\n", chip);
@@ -226,45 +242,72 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
226 (info->platform->select_chip)(nmtd->set, chip); 242 (info->platform->select_chip)(nmtd->set, chip);
227 } 243 }
228 244
229 cur &= ~S3C2410_NFCONF_nFCE; 245 cur &= ~bit;
230 } 246 }
231 247
232 writel(cur, info->regs + S3C2410_NFCONF); 248 writel(cur, reg);
233} 249}
234 250
235/* command and control functions */ 251/* command and control functions
252 *
253 * Note, these all use tglx's method of changing the IO_ADDR_W field
254 * to make the code simpler, and use the nand layer's code to issue the
255 * command and address sequences via the proper IO ports.
256 *
257*/
236 258
237static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd) 259static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
238{ 260{
239 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); 261 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
240 struct nand_chip *chip = mtd->priv; 262 struct nand_chip *chip = mtd->priv;
241 unsigned long cur;
242 263
243 switch (cmd) { 264 switch (cmd) {
244 case NAND_CTL_SETNCE: 265 case NAND_CTL_SETNCE:
245 cur = readl(info->regs + S3C2410_NFCONF); 266 case NAND_CTL_CLRNCE:
246 cur &= ~S3C2410_NFCONF_nFCE; 267 printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
247 writel(cur, info->regs + S3C2410_NFCONF); 268 break;
269
270 case NAND_CTL_SETCLE:
271 chip->IO_ADDR_W = info->regs + S3C2410_NFCMD;
272 break;
273
274 case NAND_CTL_SETALE:
275 chip->IO_ADDR_W = info->regs + S3C2410_NFADDR;
276 break;
277
278 /* NAND_CTL_CLRCLE: */
279 /* NAND_CTL_CLRALE: */
280 default:
281 chip->IO_ADDR_W = info->regs + S3C2410_NFDATA;
248 break; 282 break;
283 }
284}
285
286/* command and control functions */
287
288static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd)
289{
290 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
291 struct nand_chip *chip = mtd->priv;
249 292
293 switch (cmd) {
294 case NAND_CTL_SETNCE:
250 case NAND_CTL_CLRNCE: 295 case NAND_CTL_CLRNCE:
251 cur = readl(info->regs + S3C2410_NFCONF); 296 printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
252 cur |= S3C2410_NFCONF_nFCE;
253 writel(cur, info->regs + S3C2410_NFCONF);
254 break; 297 break;
255 298
256 case NAND_CTL_SETCLE: 299 case NAND_CTL_SETCLE:
257 chip->IO_ADDR_W = info->regs + S3C2410_NFCMD; 300 chip->IO_ADDR_W = info->regs + S3C2440_NFCMD;
258 break; 301 break;
259 302
260 case NAND_CTL_SETALE: 303 case NAND_CTL_SETALE:
261 chip->IO_ADDR_W = info->regs + S3C2410_NFADDR; 304 chip->IO_ADDR_W = info->regs + S3C2440_NFADDR;
262 break; 305 break;
263 306
264 /* NAND_CTL_CLRCLE: */ 307 /* NAND_CTL_CLRCLE: */
265 /* NAND_CTL_CLRALE: */ 308 /* NAND_CTL_CLRALE: */
266 default: 309 default:
267 chip->IO_ADDR_W = info->regs + S3C2410_NFDATA; 310 chip->IO_ADDR_W = info->regs + S3C2440_NFDATA;
268 break; 311 break;
269 } 312 }
270} 313}
@@ -278,9 +321,12 @@ static int s3c2410_nand_devready(struct mtd_info *mtd)
278{ 321{
279 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); 322 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
280 323
324 if (info->is_s3c2440)
325 return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY;
281 return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; 326 return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;
282} 327}
283 328
329
284/* ECC handling functions */ 330/* ECC handling functions */
285 331
286static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, 332static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
@@ -303,6 +349,12 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
303 return -1; 349 return -1;
304} 350}
305 351
352/* ECC functions
353 *
354 * These allow the s3c2410 and s3c2440 to use the controller's ECC
355 * generator block to ECC the data as it passes through]
356*/
357
306static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) 358static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
307{ 359{
308 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); 360 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
@@ -313,6 +365,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
313 writel(ctrl, info->regs + S3C2410_NFCONF); 365 writel(ctrl, info->regs + S3C2410_NFCONF);
314} 366}
315 367
368static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
369{
370 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
371 unsigned long ctrl;
372
373 ctrl = readl(info->regs + S3C2440_NFCONT);
374 writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT);
375}
376
316static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, 377static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd,
317 const u_char *dat, u_char *ecc_code) 378 const u_char *dat, u_char *ecc_code)
318{ 379{
@@ -329,7 +390,26 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd,
329} 390}
330 391
331 392
332/* over-ride the standard functions for a little more speed? */ 393static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd,
394 const u_char *dat, u_char *ecc_code)
395{
396 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
397 unsigned long ecc = readl(info->regs + S3C2440_NFMECC0);
398
399 ecc_code[0] = ecc;
400 ecc_code[1] = ecc >> 8;
401 ecc_code[2] = ecc >> 16;
402
403 pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n",
404 ecc_code[0], ecc_code[1], ecc_code[2]);
405
406 return 0;
407}
408
409
410/* over-ride the standard functions for a little more speed. We can
411 * use read/write block to move the data buffers to/from the controller
412*/
333 413
334static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) 414static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
335{ 415{
@@ -444,6 +524,12 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
444 chip->options = 0; 524 chip->options = 0;
445 chip->controller = &info->controller; 525 chip->controller = &info->controller;
446 526
527 if (info->is_s3c2440) {
528 chip->IO_ADDR_R = info->regs + S3C2440_NFDATA;
529 chip->IO_ADDR_W = info->regs + S3C2440_NFDATA;
530 chip->hwcontrol = s3c2440_nand_hwcontrol;
531 }
532
447 nmtd->info = info; 533 nmtd->info = info;
448 nmtd->mtd.priv = chip; 534 nmtd->mtd.priv = chip;
449 nmtd->set = set; 535 nmtd->set = set;
@@ -454,6 +540,11 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
454 chip->calculate_ecc = s3c2410_nand_calculate_ecc; 540 chip->calculate_ecc = s3c2410_nand_calculate_ecc;
455 chip->eccmode = NAND_ECC_HW3_512; 541 chip->eccmode = NAND_ECC_HW3_512;
456 chip->autooob = &nand_hw_eccoob; 542 chip->autooob = &nand_hw_eccoob;
543
544 if (info->is_s3c2440) {
545 chip->enable_hwecc = s3c2440_nand_enable_hwecc;
546 chip->calculate_ecc = s3c2440_nand_calculate_ecc;
547 }
457 } else { 548 } else {
458 chip->eccmode = NAND_ECC_SOFT; 549 chip->eccmode = NAND_ECC_SOFT;
459 } 550 }
@@ -467,7 +558,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
467 * nand layer to look for devices 558 * nand layer to look for devices
468*/ 559*/
469 560
470static int s3c2410_nand_probe(struct device *dev) 561static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440)
471{ 562{
472 struct platform_device *pdev = to_platform_device(dev); 563 struct platform_device *pdev = to_platform_device(dev);
473 struct s3c2410_platform_nand *plat = to_nand_plat(dev); 564 struct s3c2410_platform_nand *plat = to_nand_plat(dev);
@@ -493,6 +584,7 @@ static int s3c2410_nand_probe(struct device *dev)
493 dev_set_drvdata(dev, info); 584 dev_set_drvdata(dev, info);
494 585
495 spin_lock_init(&info->controller.lock); 586 spin_lock_init(&info->controller.lock);
587 init_waitqueue_head(&info->controller.wq);
496 588
497 /* get the clock source and enable it */ 589 /* get the clock source and enable it */
498 590
@@ -508,7 +600,8 @@ static int s3c2410_nand_probe(struct device *dev)
508 600
509 /* allocate and map the resource */ 601 /* allocate and map the resource */
510 602
511 res = pdev->resource; /* assume that the flash has one resource */ 603 /* currently we assume we have the one resource */
604 res = pdev->resource;
512 size = res->end - res->start + 1; 605 size = res->end - res->start + 1;
513 606
514 info->area = request_mem_region(res->start, size, pdev->name); 607 info->area = request_mem_region(res->start, size, pdev->name);
@@ -519,9 +612,10 @@ static int s3c2410_nand_probe(struct device *dev)
519 goto exit_error; 612 goto exit_error;
520 } 613 }
521 614
522 info->device = dev; 615 info->device = dev;
523 info->platform = plat; 616 info->platform = plat;
524 info->regs = ioremap(res->start, size); 617 info->regs = ioremap(res->start, size);
618 info->is_s3c2440 = is_s3c2440;
525 619
526 if (info->regs == NULL) { 620 if (info->regs == NULL) {
527 printk(KERN_ERR PFX "cannot reserve register region\n"); 621 printk(KERN_ERR PFX "cannot reserve register region\n");
@@ -586,6 +680,18 @@ static int s3c2410_nand_probe(struct device *dev)
586 return err; 680 return err;
587} 681}
588 682
683/* driver device registration */
684
685static int s3c2410_nand_probe(struct device *dev)
686{
687 return s3c24xx_nand_probe(dev, 0);
688}
689
690static int s3c2440_nand_probe(struct device *dev)
691{
692 return s3c24xx_nand_probe(dev, 1);
693}
694
589static struct device_driver s3c2410_nand_driver = { 695static struct device_driver s3c2410_nand_driver = {
590 .name = "s3c2410-nand", 696 .name = "s3c2410-nand",
591 .bus = &platform_bus_type, 697 .bus = &platform_bus_type,
@@ -593,14 +699,24 @@ static struct device_driver s3c2410_nand_driver = {
593 .remove = s3c2410_nand_remove, 699 .remove = s3c2410_nand_remove,
594}; 700};
595 701
702static struct device_driver s3c2440_nand_driver = {
703 .name = "s3c2440-nand",
704 .bus = &platform_bus_type,
705 .probe = s3c2440_nand_probe,
706 .remove = s3c2410_nand_remove,
707};
708
596static int __init s3c2410_nand_init(void) 709static int __init s3c2410_nand_init(void)
597{ 710{
598 printk("S3C2410 NAND Driver, (c) 2004 Simtec Electronics\n"); 711 printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
712
713 driver_register(&s3c2440_nand_driver);
599 return driver_register(&s3c2410_nand_driver); 714 return driver_register(&s3c2410_nand_driver);
600} 715}
601 716
602static void __exit s3c2410_nand_exit(void) 717static void __exit s3c2410_nand_exit(void)
603{ 718{
719 driver_unregister(&s3c2440_nand_driver);
604 driver_unregister(&s3c2410_nand_driver); 720 driver_unregister(&s3c2410_nand_driver);
605} 721}
606 722
@@ -609,4 +725,4 @@ module_exit(s3c2410_nand_exit);
609 725
610MODULE_LICENSE("GPL"); 726MODULE_LICENSE("GPL");
611MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 727MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
612MODULE_DESCRIPTION("S3C2410 MTD NAND driver"); 728MODULE_DESCRIPTION("S3C24XX MTD NAND driver");