aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/s3c2410.c
diff options
context:
space:
mode:
authorBen Dooks <ben-mtd@fluff.org>2006-06-27 09:35:46 -0400
committerDavid Woodhouse <dwmw2@infradead.org>2006-06-27 09:35:46 -0400
commit2c06a0821711a53d51a3d0492a9be0671b7152e5 (patch)
tree1ea68bac6653f454c712046976ec5d552a8bff1c /drivers/mtd/nand/s3c2410.c
parent62ed948cb1405fe95d61d8c6445c102e0c9da0a6 (diff)
[MTD NAND] s3c2412 support in s3c2410.c
Add support for both the S3C2412 and S3C2412 Samsung SoCs to the increasingly mis-named s3c2410.c driver. This currently only supports SLC ECCs, and a chip on nFCE0. Signed-off-by: Ben Dooks <ben-linux@fluff.org> Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'drivers/mtd/nand/s3c2410.c')
-rw-r--r--drivers/mtd/nand/s3c2410.c154
1 files changed, 111 insertions, 43 deletions
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 5219bd212cf6..ff5cef24d5bb 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -97,6 +97,12 @@ struct s3c2410_nand_mtd {
97 int scan_res; 97 int scan_res;
98}; 98};
99 99
100enum s3c_cpu_type {
101 TYPE_S3C2410,
102 TYPE_S3C2412,
103 TYPE_S3C2440,
104};
105
100/* overview of the s3c2410 nand state */ 106/* overview of the s3c2410 nand state */
101 107
102struct s3c2410_nand_info { 108struct s3c2410_nand_info {
@@ -110,9 +116,11 @@ struct s3c2410_nand_info {
110 struct resource *area; 116 struct resource *area;
111 struct clk *clk; 117 struct clk *clk;
112 void __iomem *regs; 118 void __iomem *regs;
119 void __iomem *sel_reg;
120 int sel_bit;
113 int mtd_count; 121 int mtd_count;
114 122
115 unsigned char is_s3c2440; 123 enum s3c_cpu_type cpu_type;
116}; 124};
117 125
118/* conversion functions */ 126/* conversion functions */
@@ -146,7 +154,7 @@ static inline int allow_clk_stop(struct s3c2410_nand_info *info)
146 154
147#define NS_IN_KHZ 1000000 155#define NS_IN_KHZ 1000000
148 156
149static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max) 157static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
150{ 158{
151 int result; 159 int result;
152 160
@@ -170,24 +178,26 @@ static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max)
170 178
171/* controller setup */ 179/* controller setup */
172 180
173static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, struct platform_device *pdev) 181static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
182 struct platform_device *pdev)
174{ 183{
175 struct s3c2410_platform_nand *plat = to_nand_plat(pdev); 184 struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
176 unsigned long clkrate = clk_get_rate(info->clk); 185 unsigned long clkrate = clk_get_rate(info->clk);
186 int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
177 int tacls, twrph0, twrph1; 187 int tacls, twrph0, twrph1;
178 unsigned long cfg; 188 unsigned long cfg = 0;
179 189
180 /* calculate the timing information for the controller */ 190 /* calculate the timing information for the controller */
181 191
182 clkrate /= 1000; /* turn clock into kHz for ease of use */ 192 clkrate /= 1000; /* turn clock into kHz for ease of use */
183 193
184 if (plat != NULL) { 194 if (plat != NULL) {
185 tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 4); 195 tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max);
186 twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); 196 twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8);
187 twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8); 197 twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8);
188 } else { 198 } else {
189 /* default timings */ 199 /* default timings */
190 tacls = 4; 200 tacls = tacls_max;
191 twrph0 = 8; 201 twrph0 = 8;
192 twrph1 = 8; 202 twrph1 = 8;
193 } 203 }
@@ -200,20 +210,23 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, struct platform_d
200 dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", 210 dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
201 tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate)); 211 tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate));
202 212
203 if (!info->is_s3c2440) { 213 switch (info->cpu_type) {
214 case TYPE_S3C2410:
204 cfg = S3C2410_NFCONF_EN; 215 cfg = S3C2410_NFCONF_EN;
205 cfg |= S3C2410_NFCONF_TACLS(tacls - 1); 216 cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
206 cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); 217 cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
207 cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); 218 cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
208 } else { 219 break;
220
221 case TYPE_S3C2440:
222 case TYPE_S3C2412:
209 cfg = S3C2440_NFCONF_TACLS(tacls - 1); 223 cfg = S3C2440_NFCONF_TACLS(tacls - 1);
210 cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); 224 cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
211 cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); 225 cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
212 226
213 /* enable the controller and de-assert nFCE */ 227 /* enable the controller and de-assert nFCE */
214 228
215 writel(S3C2440_NFCONT_ENABLE | S3C2440_NFCONT_ENABLE, 229 writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);
216 info->regs + S3C2440_NFCONT);
217 } 230 }
218 231
219 dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); 232 dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
@@ -229,23 +242,18 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
229 struct s3c2410_nand_info *info; 242 struct s3c2410_nand_info *info;
230 struct s3c2410_nand_mtd *nmtd; 243 struct s3c2410_nand_mtd *nmtd;
231 struct nand_chip *this = mtd->priv; 244 struct nand_chip *this = mtd->priv;
232 void __iomem *reg;
233 unsigned long cur; 245 unsigned long cur;
234 unsigned long bit;
235 246
236 nmtd = this->priv; 247 nmtd = this->priv;
237 info = nmtd->info; 248 info = nmtd->info;
238 249
239 bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE;
240 reg = info->regs + ((info->is_s3c2440) ? S3C2440_NFCONT : S3C2410_NFCONF);
241
242 if (chip != -1 && allow_clk_stop(info)) 250 if (chip != -1 && allow_clk_stop(info))
243 clk_enable(info->clk); 251 clk_enable(info->clk);
244 252
245 cur = readl(reg); 253 cur = readl(info->sel_reg);
246 254
247 if (chip == -1) { 255 if (chip == -1) {
248 cur |= bit; 256 cur |= info->sel_bit;
249 } else { 257 } else {
250 if (nmtd->set != NULL && chip > nmtd->set->nr_chips) { 258 if (nmtd->set != NULL && chip > nmtd->set->nr_chips) {
251 dev_err(info->device, "invalid chip %d\n", chip); 259 dev_err(info->device, "invalid chip %d\n", chip);
@@ -257,10 +265,10 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
257 (info->platform->select_chip) (nmtd->set, chip); 265 (info->platform->select_chip) (nmtd->set, chip);
258 } 266 }
259 267
260 cur &= ~bit; 268 cur &= ~info->sel_bit;
261 } 269 }
262 270
263 writel(cur, reg); 271 writel(cur, info->sel_reg);
264 272
265 if (chip == -1 && allow_clk_stop(info)) 273 if (chip == -1 && allow_clk_stop(info))
266 clk_disable(info->clk); 274 clk_disable(info->clk);
@@ -309,15 +317,25 @@ static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd,
309static int s3c2410_nand_devready(struct mtd_info *mtd) 317static int s3c2410_nand_devready(struct mtd_info *mtd)
310{ 318{
311 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); 319 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
312
313 if (info->is_s3c2440)
314 return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY;
315 return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; 320 return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;
316} 321}
317 322
323static int s3c2440_nand_devready(struct mtd_info *mtd)
324{
325 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
326 return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY;
327}
328
329static int s3c2412_nand_devready(struct mtd_info *mtd)
330{
331 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
332 return readb(info->regs + S3C2412_NFSTAT) & S3C2412_NFSTAT_READY;
333}
334
318/* ECC handling functions */ 335/* ECC handling functions */
319 336
320static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) 337static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
338 u_char *read_ecc, u_char *calc_ecc)
321{ 339{
322 pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc); 340 pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc);
323 341
@@ -485,11 +503,8 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
485 struct s3c2410_nand_set *set) 503 struct s3c2410_nand_set *set)
486{ 504{
487 struct nand_chip *chip = &nmtd->chip; 505 struct nand_chip *chip = &nmtd->chip;
506 void __iomem *regs = info->regs;
488 507
489 chip->IO_ADDR_R = info->regs + S3C2410_NFDATA;
490 chip->IO_ADDR_W = info->regs + S3C2410_NFDATA;
491 chip->cmd_ctrl = s3c2410_nand_hwcontrol;
492 chip->dev_ready = s3c2410_nand_devready;
493 chip->write_buf = s3c2410_nand_write_buf; 508 chip->write_buf = s3c2410_nand_write_buf;
494 chip->read_buf = s3c2410_nand_read_buf; 509 chip->read_buf = s3c2410_nand_read_buf;
495 chip->select_chip = s3c2410_nand_select_chip; 510 chip->select_chip = s3c2410_nand_select_chip;
@@ -498,11 +513,37 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
498 chip->options = 0; 513 chip->options = 0;
499 chip->controller = &info->controller; 514 chip->controller = &info->controller;
500 515
501 if (info->is_s3c2440) { 516 switch (info->cpu_type) {
502 chip->IO_ADDR_R = info->regs + S3C2440_NFDATA; 517 case TYPE_S3C2410:
503 chip->IO_ADDR_W = info->regs + S3C2440_NFDATA; 518 chip->IO_ADDR_W = regs + S3C2410_NFDATA;
504 chip->cmd_ctrl = s3c2440_nand_hwcontrol; 519 info->sel_reg = regs + S3C2410_NFCONF;
505 } 520 info->sel_bit = S3C2410_NFCONF_nFCE;
521 chip->cmd_ctrl = s3c2410_nand_hwcontrol;
522 chip->dev_ready = s3c2410_nand_devready;
523 break;
524
525 case TYPE_S3C2440:
526 chip->IO_ADDR_W = regs + S3C2440_NFDATA;
527 info->sel_reg = regs + S3C2440_NFCONT;
528 info->sel_bit = S3C2440_NFCONT_nFCE;
529 chip->cmd_ctrl = s3c2440_nand_hwcontrol;
530 chip->dev_ready = s3c2440_nand_devready;
531 break;
532
533 case TYPE_S3C2412:
534 chip->IO_ADDR_W = regs + S3C2440_NFDATA;
535 info->sel_reg = regs + S3C2440_NFCONT;
536 info->sel_bit = S3C2412_NFCONT_nFCE0;
537 chip->cmd_ctrl = s3c2440_nand_hwcontrol;
538 chip->dev_ready = s3c2412_nand_devready;
539
540 if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT)
541 dev_info(info->device, "System booted from NAND\n");
542
543 break;
544 }
545
546 chip->IO_ADDR_R = chip->IO_ADDR_W;
506 547
507 nmtd->info = info; 548 nmtd->info = info;
508 nmtd->mtd.priv = chip; 549 nmtd->mtd.priv = chip;
@@ -510,17 +551,25 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
510 nmtd->set = set; 551 nmtd->set = set;
511 552
512 if (hardware_ecc) { 553 if (hardware_ecc) {
513 chip->ecc.correct = s3c2410_nand_correct_data;
514 chip->ecc.hwctl = s3c2410_nand_enable_hwecc;
515 chip->ecc.calculate = s3c2410_nand_calculate_ecc; 554 chip->ecc.calculate = s3c2410_nand_calculate_ecc;
555 chip->ecc.correct = s3c2410_nand_correct_data;
516 chip->ecc.mode = NAND_ECC_HW; 556 chip->ecc.mode = NAND_ECC_HW;
517 chip->ecc.size = 512; 557 chip->ecc.size = 512;
518 chip->ecc.bytes = 3; 558 chip->ecc.bytes = 3;
519 chip->ecc.layout = &nand_hw_eccoob; 559 chip->ecc.layout = &nand_hw_eccoob;
520 560
521 if (info->is_s3c2440) { 561 switch (info->cpu_type) {
522 chip->ecc.hwctl = s3c2440_nand_enable_hwecc; 562 case TYPE_S3C2410:
523 chip->ecc.calculate = s3c2440_nand_calculate_ecc; 563 chip->ecc.hwctl = s3c2410_nand_enable_hwecc;
564 chip->ecc.calculate = s3c2410_nand_calculate_ecc;
565 break;
566
567 case TYPE_S3C2412:
568 case TYPE_S3C2440:
569 chip->ecc.hwctl = s3c2440_nand_enable_hwecc;
570 chip->ecc.calculate = s3c2440_nand_calculate_ecc;
571 break;
572
524 } 573 }
525 } else { 574 } else {
526 chip->ecc.mode = NAND_ECC_SOFT; 575 chip->ecc.mode = NAND_ECC_SOFT;
@@ -535,7 +584,8 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
535 * nand layer to look for devices 584 * nand layer to look for devices
536*/ 585*/
537 586
538static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440) 587static int s3c24xx_nand_probe(struct platform_device *pdev,
588 enum s3c_cpu_type cpu_type)
539{ 589{
540 struct s3c2410_platform_nand *plat = to_nand_plat(pdev); 590 struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
541 struct s3c2410_nand_info *info; 591 struct s3c2410_nand_info *info;
@@ -590,7 +640,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440)
590 info->device = &pdev->dev; 640 info->device = &pdev->dev;
591 info->platform = plat; 641 info->platform = plat;
592 info->regs = ioremap(res->start, size); 642 info->regs = ioremap(res->start, size);
593 info->is_s3c2440 = is_s3c2440; 643 info->cpu_type = cpu_type;
594 644
595 if (info->regs == NULL) { 645 if (info->regs == NULL) {
596 dev_err(&pdev->dev, "cannot reserve register region\n"); 646 dev_err(&pdev->dev, "cannot reserve register region\n");
@@ -697,12 +747,17 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
697 747
698static int s3c2410_nand_probe(struct platform_device *dev) 748static int s3c2410_nand_probe(struct platform_device *dev)
699{ 749{
700 return s3c24xx_nand_probe(dev, 0); 750 return s3c24xx_nand_probe(dev, TYPE_S3C2410);
701} 751}
702 752
703static int s3c2440_nand_probe(struct platform_device *dev) 753static int s3c2440_nand_probe(struct platform_device *dev)
704{ 754{
705 return s3c24xx_nand_probe(dev, 1); 755 return s3c24xx_nand_probe(dev, TYPE_S3C2440);
756}
757
758static int s3c2412_nand_probe(struct platform_device *dev)
759{
760 return s3c24xx_nand_probe(dev, TYPE_S3C2412);
706} 761}
707 762
708static struct platform_driver s3c2410_nand_driver = { 763static struct platform_driver s3c2410_nand_driver = {
@@ -727,16 +782,29 @@ static struct platform_driver s3c2440_nand_driver = {
727 }, 782 },
728}; 783};
729 784
785static struct platform_driver s3c2412_nand_driver = {
786 .probe = s3c2412_nand_probe,
787 .remove = s3c2410_nand_remove,
788 .suspend = s3c24xx_nand_suspend,
789 .resume = s3c24xx_nand_resume,
790 .driver = {
791 .name = "s3c2412-nand",
792 .owner = THIS_MODULE,
793 },
794};
795
730static int __init s3c2410_nand_init(void) 796static int __init s3c2410_nand_init(void)
731{ 797{
732 printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n"); 798 printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
733 799
800 platform_driver_register(&s3c2412_nand_driver);
734 platform_driver_register(&s3c2440_nand_driver); 801 platform_driver_register(&s3c2440_nand_driver);
735 return platform_driver_register(&s3c2410_nand_driver); 802 return platform_driver_register(&s3c2410_nand_driver);
736} 803}
737 804
738static void __exit s3c2410_nand_exit(void) 805static void __exit s3c2410_nand_exit(void)
739{ 806{
807 platform_driver_unregister(&s3c2412_nand_driver);
740 platform_driver_unregister(&s3c2440_nand_driver); 808 platform_driver_unregister(&s3c2440_nand_driver);
741 platform_driver_unregister(&s3c2410_nand_driver); 809 platform_driver_unregister(&s3c2410_nand_driver);
742} 810}