diff options
Diffstat (limited to 'drivers/mtd/nand/s3c2410.c')
-rw-r--r-- | drivers/mtd/nand/s3c2410.c | 61 |
1 files changed, 33 insertions, 28 deletions
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 2df5e47d1f5c..97e9b7892d29 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c | |||
@@ -17,8 +17,9 @@ | |||
17 | * 02-May-2005 BJD Reduced hwcontrol decode | 17 | * 02-May-2005 BJD Reduced hwcontrol decode |
18 | * 20-Jun-2005 BJD Updated s3c2440 support, fixed timing bug | 18 | * 20-Jun-2005 BJD Updated s3c2440 support, fixed timing bug |
19 | * 08-Jul-2005 BJD Fix OOPS when no platform data supplied | 19 | * 08-Jul-2005 BJD Fix OOPS when no platform data supplied |
20 | * 20-Oct-2005 BJD Fix timing calculation bug | ||
20 | * | 21 | * |
21 | * $Id: s3c2410.c,v 1.14 2005/07/06 20:05:06 bjd Exp $ | 22 | * $Id: s3c2410.c,v 1.20 2005/11/07 11:14:31 gleixner Exp $ |
22 | * | 23 | * |
23 | * This program is free software; you can redistribute it and/or modify | 24 | * This program is free software; you can redistribute it and/or modify |
24 | * it under the terms of the GNU General Public License as published by | 25 | * it under the terms of the GNU General Public License as published by |
@@ -136,13 +137,13 @@ static struct s3c2410_platform_nand *to_nand_plat(struct device *dev) | |||
136 | 137 | ||
137 | /* timing calculations */ | 138 | /* timing calculations */ |
138 | 139 | ||
139 | #define NS_IN_KHZ 10000000 | 140 | #define NS_IN_KHZ 1000000 |
140 | 141 | ||
141 | static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max) | 142 | static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max) |
142 | { | 143 | { |
143 | int result; | 144 | int result; |
144 | 145 | ||
145 | result = (wanted * NS_IN_KHZ) / clk; | 146 | result = (wanted * clk) / NS_IN_KHZ; |
146 | result++; | 147 | result++; |
147 | 148 | ||
148 | pr_debug("result %d from %ld, %d\n", result, clk, wanted); | 149 | pr_debug("result %d from %ld, %d\n", result, clk, wanted); |
@@ -159,20 +160,22 @@ static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max) | |||
159 | return result; | 160 | return result; |
160 | } | 161 | } |
161 | 162 | ||
162 | #define to_ns(ticks,clk) (((clk) * (ticks)) / NS_IN_KHZ) | 163 | #define to_ns(ticks,clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk)) |
163 | 164 | ||
164 | /* controller setup */ | 165 | /* controller setup */ |
165 | 166 | ||
166 | static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, | 167 | static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, |
167 | struct device *dev) | 168 | struct device *dev) |
168 | { | 169 | { |
169 | struct s3c2410_platform_nand *plat = to_nand_plat(dev); | 170 | struct s3c2410_platform_nand *plat = to_nand_plat(dev); |
170 | unsigned int tacls, twrph0, twrph1; | ||
171 | unsigned long clkrate = clk_get_rate(info->clk); | 171 | unsigned long clkrate = clk_get_rate(info->clk); |
172 | int tacls, twrph0, twrph1; | ||
172 | unsigned long cfg; | 173 | unsigned long cfg; |
173 | 174 | ||
174 | /* calculate the timing information for the controller */ | 175 | /* calculate the timing information for the controller */ |
175 | 176 | ||
177 | clkrate /= 1000; /* turn clock into kHz for ease of use */ | ||
178 | |||
176 | if (plat != NULL) { | 179 | if (plat != NULL) { |
177 | tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 4); | 180 | tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 4); |
178 | twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); | 181 | twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); |
@@ -183,16 +186,16 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, | |||
183 | twrph0 = 8; | 186 | twrph0 = 8; |
184 | twrph1 = 8; | 187 | twrph1 = 8; |
185 | } | 188 | } |
186 | 189 | ||
187 | if (tacls < 0 || twrph0 < 0 || twrph1 < 0) { | 190 | if (tacls < 0 || twrph0 < 0 || twrph1 < 0) { |
188 | printk(KERN_ERR PFX "cannot get timings suitable for board\n"); | 191 | printk(KERN_ERR PFX "cannot get timings suitable for board\n"); |
189 | return -EINVAL; | 192 | return -EINVAL; |
190 | } | 193 | } |
191 | 194 | ||
192 | printk(KERN_INFO PFX "timing: Tacls %ldns, Twrph0 %ldns, Twrph1 %ldns\n", | 195 | printk(KERN_INFO PFX "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", |
193 | to_ns(tacls, clkrate), | 196 | tacls, to_ns(tacls, clkrate), |
194 | to_ns(twrph0, clkrate), | 197 | twrph0, to_ns(twrph0, clkrate), |
195 | to_ns(twrph1, clkrate)); | 198 | twrph1, to_ns(twrph1, clkrate)); |
196 | 199 | ||
197 | if (!info->is_s3c2440) { | 200 | if (!info->is_s3c2440) { |
198 | cfg = S3C2410_NFCONF_EN; | 201 | cfg = S3C2410_NFCONF_EN; |
@@ -216,7 +219,7 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, | |||
216 | static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) | 219 | static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) |
217 | { | 220 | { |
218 | struct s3c2410_nand_info *info; | 221 | struct s3c2410_nand_info *info; |
219 | struct s3c2410_nand_mtd *nmtd; | 222 | struct s3c2410_nand_mtd *nmtd; |
220 | struct nand_chip *this = mtd->priv; | 223 | struct nand_chip *this = mtd->priv; |
221 | void __iomem *reg; | 224 | void __iomem *reg; |
222 | unsigned long cur; | 225 | unsigned long cur; |
@@ -249,7 +252,7 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) | |||
249 | writel(cur, reg); | 252 | writel(cur, reg); |
250 | } | 253 | } |
251 | 254 | ||
252 | /* command and control functions | 255 | /* command and control functions |
253 | * | 256 | * |
254 | * Note, these all use tglx's method of changing the IO_ADDR_W field | 257 | * Note, these all use tglx's method of changing the IO_ADDR_W field |
255 | * to make the code simpler, and use the nand layer's code to issue the | 258 | * to make the code simpler, and use the nand layer's code to issue the |
@@ -321,7 +324,7 @@ static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd) | |||
321 | static int s3c2410_nand_devready(struct mtd_info *mtd) | 324 | static int s3c2410_nand_devready(struct mtd_info *mtd) |
322 | { | 325 | { |
323 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 326 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
324 | 327 | ||
325 | if (info->is_s3c2440) | 328 | if (info->is_s3c2440) |
326 | return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY; | 329 | return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY; |
327 | return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; | 330 | return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; |
@@ -342,7 +345,7 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, | |||
342 | 345 | ||
343 | if (read_ecc[0] == calc_ecc[0] && | 346 | if (read_ecc[0] == calc_ecc[0] && |
344 | read_ecc[1] == calc_ecc[1] && | 347 | read_ecc[1] == calc_ecc[1] && |
345 | read_ecc[2] == calc_ecc[2]) | 348 | read_ecc[2] == calc_ecc[2]) |
346 | return 0; | 349 | return 0; |
347 | 350 | ||
348 | /* we curently have no method for correcting the error */ | 351 | /* we curently have no method for correcting the error */ |
@@ -433,14 +436,14 @@ static int s3c2410_nand_remove(struct device *dev) | |||
433 | 436 | ||
434 | dev_set_drvdata(dev, NULL); | 437 | dev_set_drvdata(dev, NULL); |
435 | 438 | ||
436 | if (info == NULL) | 439 | if (info == NULL) |
437 | return 0; | 440 | return 0; |
438 | 441 | ||
439 | /* first thing we need to do is release all our mtds | 442 | /* first thing we need to do is release all our mtds |
440 | * and their partitions, then go through freeing the | 443 | * and their partitions, then go through freeing the |
441 | * resources used | 444 | * resources used |
442 | */ | 445 | */ |
443 | 446 | ||
444 | if (info->mtds != NULL) { | 447 | if (info->mtds != NULL) { |
445 | struct s3c2410_nand_mtd *ptr = info->mtds; | 448 | struct s3c2410_nand_mtd *ptr = info->mtds; |
446 | int mtdno; | 449 | int mtdno; |
@@ -504,7 +507,7 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, | |||
504 | 507 | ||
505 | /* s3c2410_nand_init_chip | 508 | /* s3c2410_nand_init_chip |
506 | * | 509 | * |
507 | * init a single instance of an chip | 510 | * init a single instance of an chip |
508 | */ | 511 | */ |
509 | 512 | ||
510 | static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, | 513 | static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, |
@@ -576,7 +579,7 @@ static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440) | |||
576 | 579 | ||
577 | info = kmalloc(sizeof(*info), GFP_KERNEL); | 580 | info = kmalloc(sizeof(*info), GFP_KERNEL); |
578 | if (info == NULL) { | 581 | if (info == NULL) { |
579 | printk(KERN_ERR PFX "no memory for flash info\n"); | 582 | dev_err(dev, "no memory for flash info\n"); |
580 | err = -ENOMEM; | 583 | err = -ENOMEM; |
581 | goto exit_error; | 584 | goto exit_error; |
582 | } | 585 | } |
@@ -591,7 +594,7 @@ static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440) | |||
591 | 594 | ||
592 | info->clk = clk_get(dev, "nand"); | 595 | info->clk = clk_get(dev, "nand"); |
593 | if (IS_ERR(info->clk)) { | 596 | if (IS_ERR(info->clk)) { |
594 | printk(KERN_ERR PFX "failed to get clock"); | 597 | dev_err(dev, "failed to get clock"); |
595 | err = -ENOENT; | 598 | err = -ENOENT; |
596 | goto exit_error; | 599 | goto exit_error; |
597 | } | 600 | } |
@@ -608,7 +611,7 @@ static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440) | |||
608 | info->area = request_mem_region(res->start, size, pdev->name); | 611 | info->area = request_mem_region(res->start, size, pdev->name); |
609 | 612 | ||
610 | if (info->area == NULL) { | 613 | if (info->area == NULL) { |
611 | printk(KERN_ERR PFX "cannot reserve register region\n"); | 614 | dev_err(dev, "cannot reserve register region\n"); |
612 | err = -ENOENT; | 615 | err = -ENOENT; |
613 | goto exit_error; | 616 | goto exit_error; |
614 | } | 617 | } |
@@ -619,12 +622,12 @@ static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440) | |||
619 | info->is_s3c2440 = is_s3c2440; | 622 | info->is_s3c2440 = is_s3c2440; |
620 | 623 | ||
621 | if (info->regs == NULL) { | 624 | if (info->regs == NULL) { |
622 | printk(KERN_ERR PFX "cannot reserve register region\n"); | 625 | dev_err(dev, "cannot reserve register region\n"); |
623 | err = -EIO; | 626 | err = -EIO; |
624 | goto exit_error; | 627 | goto exit_error; |
625 | } | 628 | } |
626 | 629 | ||
627 | printk(KERN_INFO PFX "mapped registers at %p\n", info->regs); | 630 | dev_dbg(dev, "mapped registers at %p\n", info->regs); |
628 | 631 | ||
629 | /* initialise the hardware */ | 632 | /* initialise the hardware */ |
630 | 633 | ||
@@ -642,7 +645,7 @@ static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440) | |||
642 | size = nr_sets * sizeof(*info->mtds); | 645 | size = nr_sets * sizeof(*info->mtds); |
643 | info->mtds = kmalloc(size, GFP_KERNEL); | 646 | info->mtds = kmalloc(size, GFP_KERNEL); |
644 | if (info->mtds == NULL) { | 647 | if (info->mtds == NULL) { |
645 | printk(KERN_ERR PFX "failed to allocate mtd storage\n"); | 648 | dev_err(dev, "failed to allocate mtd storage\n"); |
646 | err = -ENOMEM; | 649 | err = -ENOMEM; |
647 | goto exit_error; | 650 | goto exit_error; |
648 | } | 651 | } |
@@ -656,7 +659,7 @@ static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440) | |||
656 | for (setno = 0; setno < nr_sets; setno++, nmtd++) { | 659 | for (setno = 0; setno < nr_sets; setno++, nmtd++) { |
657 | pr_debug("initialising set %d (%p, info %p)\n", | 660 | pr_debug("initialising set %d (%p, info %p)\n", |
658 | setno, nmtd, info); | 661 | setno, nmtd, info); |
659 | 662 | ||
660 | s3c2410_nand_init_chip(info, nmtd, sets); | 663 | s3c2410_nand_init_chip(info, nmtd, sets); |
661 | 664 | ||
662 | nmtd->scan_res = nand_scan(&nmtd->mtd, | 665 | nmtd->scan_res = nand_scan(&nmtd->mtd, |
@@ -669,7 +672,7 @@ static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440) | |||
669 | if (sets != NULL) | 672 | if (sets != NULL) |
670 | sets++; | 673 | sets++; |
671 | } | 674 | } |
672 | 675 | ||
673 | pr_debug("initialised ok\n"); | 676 | pr_debug("initialised ok\n"); |
674 | return 0; | 677 | return 0; |
675 | 678 | ||
@@ -695,6 +698,7 @@ static int s3c2440_nand_probe(struct device *dev) | |||
695 | 698 | ||
696 | static struct device_driver s3c2410_nand_driver = { | 699 | static struct device_driver s3c2410_nand_driver = { |
697 | .name = "s3c2410-nand", | 700 | .name = "s3c2410-nand", |
701 | .owner = THIS_MODULE, | ||
698 | .bus = &platform_bus_type, | 702 | .bus = &platform_bus_type, |
699 | .probe = s3c2410_nand_probe, | 703 | .probe = s3c2410_nand_probe, |
700 | .remove = s3c2410_nand_remove, | 704 | .remove = s3c2410_nand_remove, |
@@ -702,6 +706,7 @@ static struct device_driver s3c2410_nand_driver = { | |||
702 | 706 | ||
703 | static struct device_driver s3c2440_nand_driver = { | 707 | static struct device_driver s3c2440_nand_driver = { |
704 | .name = "s3c2440-nand", | 708 | .name = "s3c2440-nand", |
709 | .owner = THIS_MODULE, | ||
705 | .bus = &platform_bus_type, | 710 | .bus = &platform_bus_type, |
706 | .probe = s3c2440_nand_probe, | 711 | .probe = s3c2440_nand_probe, |
707 | .remove = s3c2410_nand_remove, | 712 | .remove = s3c2410_nand_remove, |