diff options
author | Ben Dooks <ben-mtd@fluff.org> | 2006-06-27 09:35:46 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2006-06-27 09:35:46 -0400 |
commit | 2c06a0821711a53d51a3d0492a9be0671b7152e5 (patch) | |
tree | 1ea68bac6653f454c712046976ec5d552a8bff1c /drivers | |
parent | 62ed948cb1405fe95d61d8c6445c102e0c9da0a6 (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')
-rw-r--r-- | drivers/mtd/nand/s3c2410.c | 154 |
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 | ||
100 | enum 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 | ||
102 | struct s3c2410_nand_info { | 108 | struct 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 | ||
149 | static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max) | 157 | static 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 | ||
173 | static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, struct platform_device *pdev) | 181 | static 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, | |||
309 | static int s3c2410_nand_devready(struct mtd_info *mtd) | 317 | static 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 | ||
323 | static 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 | |||
329 | static 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 | ||
320 | static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) | 337 | static 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 | ||
538 | static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440) | 587 | static 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 | ||
698 | static int s3c2410_nand_probe(struct platform_device *dev) | 748 | static 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 | ||
703 | static int s3c2440_nand_probe(struct platform_device *dev) | 753 | static 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 | |||
758 | static int s3c2412_nand_probe(struct platform_device *dev) | ||
759 | { | ||
760 | return s3c24xx_nand_probe(dev, TYPE_S3C2412); | ||
706 | } | 761 | } |
707 | 762 | ||
708 | static struct platform_driver s3c2410_nand_driver = { | 763 | static struct platform_driver s3c2410_nand_driver = { |
@@ -727,16 +782,29 @@ static struct platform_driver s3c2440_nand_driver = { | |||
727 | }, | 782 | }, |
728 | }; | 783 | }; |
729 | 784 | ||
785 | static 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 | |||
730 | static int __init s3c2410_nand_init(void) | 796 | static 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 | ||
738 | static void __exit s3c2410_nand_exit(void) | 805 | static 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 | } |