aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/s3c2410.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/s3c2410.c')
-rw-r--r--drivers/mtd/nand/s3c2410.c268
1 files changed, 181 insertions, 87 deletions
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 8e375d5fe231..11dc7e69c4fb 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -74,6 +74,14 @@ static struct nand_ecclayout nand_hw_eccoob = {
74 74
75struct s3c2410_nand_info; 75struct s3c2410_nand_info;
76 76
77/**
78 * struct s3c2410_nand_mtd - driver MTD structure
79 * @mtd: The MTD instance to pass to the MTD layer.
80 * @chip: The NAND chip information.
81 * @set: The platform information supplied for this set of NAND chips.
82 * @info: Link back to the hardware information.
83 * @scan_res: The result from calling nand_scan_ident().
84*/
77struct s3c2410_nand_mtd { 85struct s3c2410_nand_mtd {
78 struct mtd_info mtd; 86 struct mtd_info mtd;
79 struct nand_chip chip; 87 struct nand_chip chip;
@@ -90,6 +98,21 @@ enum s3c_cpu_type {
90 98
91/* overview of the s3c2410 nand state */ 99/* overview of the s3c2410 nand state */
92 100
101/**
102 * struct s3c2410_nand_info - NAND controller state.
103 * @mtds: An array of MTD instances on this controoler.
104 * @platform: The platform data for this board.
105 * @device: The platform device we bound to.
106 * @area: The IO area resource that came from request_mem_region().
107 * @clk: The clock resource for this controller.
108 * @regs: The area mapped for the hardware registers described by @area.
109 * @sel_reg: Pointer to the register controlling the NAND selection.
110 * @sel_bit: The bit in @sel_reg to select the NAND chip.
111 * @mtd_count: The number of MTDs created from this controller.
112 * @save_sel: The contents of @sel_reg to be saved over suspend.
113 * @clk_rate: The clock rate from @clk.
114 * @cpu_type: The exact type of this controller.
115 */
93struct s3c2410_nand_info { 116struct s3c2410_nand_info {
94 /* mtd info */ 117 /* mtd info */
95 struct nand_hw_control controller; 118 struct nand_hw_control controller;
@@ -145,12 +168,19 @@ static inline int allow_clk_stop(struct s3c2410_nand_info *info)
145 168
146#define NS_IN_KHZ 1000000 169#define NS_IN_KHZ 1000000
147 170
171/**
172 * s3c_nand_calc_rate - calculate timing data.
173 * @wanted: The cycle time in nanoseconds.
174 * @clk: The clock rate in kHz.
175 * @max: The maximum divider value.
176 *
177 * Calculate the timing value from the given parameters.
178 */
148static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) 179static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
149{ 180{
150 int result; 181 int result;
151 182
152 result = (wanted * clk) / NS_IN_KHZ; 183 result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ);
153 result++;
154 184
155 pr_debug("result %d from %ld, %d\n", result, clk, wanted); 185 pr_debug("result %d from %ld, %d\n", result, clk, wanted);
156 186
@@ -169,13 +199,21 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
169 199
170/* controller setup */ 200/* controller setup */
171 201
202/**
203 * s3c2410_nand_setrate - setup controller timing information.
204 * @info: The controller instance.
205 *
206 * Given the information supplied by the platform, calculate and set
207 * the necessary timing registers in the hardware to generate the
208 * necessary timing cycles to the hardware.
209 */
172static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) 210static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
173{ 211{
174 struct s3c2410_platform_nand *plat = info->platform; 212 struct s3c2410_platform_nand *plat = info->platform;
175 int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; 213 int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
176 int tacls, twrph0, twrph1; 214 int tacls, twrph0, twrph1;
177 unsigned long clkrate = clk_get_rate(info->clk); 215 unsigned long clkrate = clk_get_rate(info->clk);
178 unsigned long set, cfg, mask; 216 unsigned long uninitialized_var(set), cfg, uninitialized_var(mask);
179 unsigned long flags; 217 unsigned long flags;
180 218
181 /* calculate the timing information for the controller */ 219 /* calculate the timing information for the controller */
@@ -215,9 +253,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
215 253
216 case TYPE_S3C2440: 254 case TYPE_S3C2440:
217 case TYPE_S3C2412: 255 case TYPE_S3C2412:
218 mask = (S3C2410_NFCONF_TACLS(tacls_max - 1) | 256 mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) |
219 S3C2410_NFCONF_TWRPH0(7) | 257 S3C2440_NFCONF_TWRPH0(7) |
220 S3C2410_NFCONF_TWRPH1(7)); 258 S3C2440_NFCONF_TWRPH1(7));
221 259
222 set = S3C2440_NFCONF_TACLS(tacls - 1); 260 set = S3C2440_NFCONF_TACLS(tacls - 1);
223 set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); 261 set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
@@ -225,14 +263,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
225 break; 263 break;
226 264
227 default: 265 default:
228 /* keep compiler happy */
229 mask = 0;
230 set = 0;
231 BUG(); 266 BUG();
232 } 267 }
233 268
234 dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
235
236 local_irq_save(flags); 269 local_irq_save(flags);
237 270
238 cfg = readl(info->regs + S3C2410_NFCONF); 271 cfg = readl(info->regs + S3C2410_NFCONF);
@@ -242,9 +275,18 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
242 275
243 local_irq_restore(flags); 276 local_irq_restore(flags);
244 277
278 dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
279
245 return 0; 280 return 0;
246} 281}
247 282
283/**
284 * s3c2410_nand_inithw - basic hardware initialisation
285 * @info: The hardware state.
286 *
287 * Do the basic initialisation of the hardware, using s3c2410_nand_setrate()
288 * to setup the hardware access speeds and set the controller to be enabled.
289*/
248static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) 290static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
249{ 291{
250 int ret; 292 int ret;
@@ -268,8 +310,19 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
268 return 0; 310 return 0;
269} 311}
270 312
271/* select chip */ 313/**
272 314 * s3c2410_nand_select_chip - select the given nand chip
315 * @mtd: The MTD instance for this chip.
316 * @chip: The chip number.
317 *
318 * This is called by the MTD layer to either select a given chip for the
319 * @mtd instance, or to indicate that the access has finished and the
320 * chip can be de-selected.
321 *
322 * The routine ensures that the nFCE line is correctly setup, and any
323 * platform specific selection code is called to route nFCE to the specific
324 * chip.
325 */
273static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) 326static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
274{ 327{
275 struct s3c2410_nand_info *info; 328 struct s3c2410_nand_info *info;
@@ -530,7 +583,16 @@ static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
530static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) 583static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
531{ 584{
532 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); 585 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
533 readsl(info->regs + S3C2440_NFDATA, buf, len / 4); 586
587 readsl(info->regs + S3C2440_NFDATA, buf, len >> 2);
588
589 /* cleanup if we've got less than a word to do */
590 if (len & 3) {
591 buf += len & ~3;
592
593 for (; len & 3; len--)
594 *buf++ = readb(info->regs + S3C2440_NFDATA);
595 }
534} 596}
535 597
536static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) 598static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
@@ -542,7 +604,16 @@ static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int
542static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) 604static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
543{ 605{
544 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); 606 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
545 writesl(info->regs + S3C2440_NFDATA, buf, len / 4); 607
608 writesl(info->regs + S3C2440_NFDATA, buf, len >> 2);
609
610 /* cleanup any fractional write */
611 if (len & 3) {
612 buf += len & ~3;
613
614 for (; len & 3; len--, buf++)
615 writeb(*buf, info->regs + S3C2440_NFDATA);
616 }
546} 617}
547 618
548/* cpufreq driver support */ 619/* cpufreq driver support */
@@ -593,7 +664,7 @@ static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *inf
593 664
594/* device management functions */ 665/* device management functions */
595 666
596static int s3c2410_nand_remove(struct platform_device *pdev) 667static int s3c24xx_nand_remove(struct platform_device *pdev)
597{ 668{
598 struct s3c2410_nand_info *info = to_nand_info(pdev); 669 struct s3c2410_nand_info *info = to_nand_info(pdev);
599 670
@@ -645,17 +716,31 @@ static int s3c2410_nand_remove(struct platform_device *pdev)
645} 716}
646 717
647#ifdef CONFIG_MTD_PARTITIONS 718#ifdef CONFIG_MTD_PARTITIONS
719const char *part_probes[] = { "cmdlinepart", NULL };
648static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, 720static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
649 struct s3c2410_nand_mtd *mtd, 721 struct s3c2410_nand_mtd *mtd,
650 struct s3c2410_nand_set *set) 722 struct s3c2410_nand_set *set)
651{ 723{
724 struct mtd_partition *part_info;
725 int nr_part = 0;
726
652 if (set == NULL) 727 if (set == NULL)
653 return add_mtd_device(&mtd->mtd); 728 return add_mtd_device(&mtd->mtd);
654 729
655 if (set->nr_partitions > 0 && set->partitions != NULL) { 730 if (set->nr_partitions == 0) {
656 return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions); 731 mtd->mtd.name = set->name;
732 nr_part = parse_mtd_partitions(&mtd->mtd, part_probes,
733 &part_info, 0);
734 } else {
735 if (set->nr_partitions > 0 && set->partitions != NULL) {
736 nr_part = set->nr_partitions;
737 part_info = set->partitions;
738 }
657 } 739 }
658 740
741 if (nr_part > 0 && part_info)
742 return add_mtd_partitions(&mtd->mtd, part_info, nr_part);
743
659 return add_mtd_device(&mtd->mtd); 744 return add_mtd_device(&mtd->mtd);
660} 745}
661#else 746#else
@@ -667,11 +752,16 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
667} 752}
668#endif 753#endif
669 754
670/* s3c2410_nand_init_chip 755/**
756 * s3c2410_nand_init_chip - initialise a single instance of an chip
757 * @info: The base NAND controller the chip is on.
758 * @nmtd: The new controller MTD instance to fill in.
759 * @set: The information passed from the board specific platform data.
671 * 760 *
672 * init a single instance of an chip 761 * Initialise the given @nmtd from the information in @info and @set. This
673*/ 762 * readies the structure for use with the MTD layer functions by ensuring
674 763 * all pointers are setup and the necessary control routines selected.
764 */
675static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, 765static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
676 struct s3c2410_nand_mtd *nmtd, 766 struct s3c2410_nand_mtd *nmtd,
677 struct s3c2410_nand_set *set) 767 struct s3c2410_nand_set *set)
@@ -757,14 +847,40 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
757 847
758 if (set->disable_ecc) 848 if (set->disable_ecc)
759 chip->ecc.mode = NAND_ECC_NONE; 849 chip->ecc.mode = NAND_ECC_NONE;
850
851 switch (chip->ecc.mode) {
852 case NAND_ECC_NONE:
853 dev_info(info->device, "NAND ECC disabled\n");
854 break;
855 case NAND_ECC_SOFT:
856 dev_info(info->device, "NAND soft ECC\n");
857 break;
858 case NAND_ECC_HW:
859 dev_info(info->device, "NAND hardware ECC\n");
860 break;
861 default:
862 dev_info(info->device, "NAND ECC UNKNOWN\n");
863 break;
864 }
865
866 /* If you use u-boot BBT creation code, specifying this flag will
867 * let the kernel fish out the BBT from the NAND, and also skip the
868 * full NAND scan that can take 1/2s or so. Little things... */
869 if (set->flash_bbt)
870 chip->options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN;
760} 871}
761 872
762/* s3c2410_nand_update_chip 873/**
874 * s3c2410_nand_update_chip - post probe update
875 * @info: The controller instance.
876 * @nmtd: The driver version of the MTD instance.
763 * 877 *
764 * post-probe chip update, to change any items, such as the 878 * This routine is called after the chip probe has succesfully completed
765 * layout for large page nand 879 * and the relevant per-chip information updated. This call ensure that
766 */ 880 * we update the internal state accordingly.
767 881 *
882 * The internal state is currently limited to the ECC state information.
883*/
768static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, 884static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
769 struct s3c2410_nand_mtd *nmtd) 885 struct s3c2410_nand_mtd *nmtd)
770{ 886{
@@ -773,33 +889,33 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
773 dev_dbg(info->device, "chip %p => page shift %d\n", 889 dev_dbg(info->device, "chip %p => page shift %d\n",
774 chip, chip->page_shift); 890 chip, chip->page_shift);
775 891
776 if (hardware_ecc) { 892 if (chip->ecc.mode != NAND_ECC_HW)
893 return;
894
777 /* change the behaviour depending on wether we are using 895 /* change the behaviour depending on wether we are using
778 * the large or small page nand device */ 896 * the large or small page nand device */
779 897
780 if (chip->page_shift > 10) { 898 if (chip->page_shift > 10) {
781 chip->ecc.size = 256; 899 chip->ecc.size = 256;
782 chip->ecc.bytes = 3; 900 chip->ecc.bytes = 3;
783 } else { 901 } else {
784 chip->ecc.size = 512; 902 chip->ecc.size = 512;
785 chip->ecc.bytes = 3; 903 chip->ecc.bytes = 3;
786 chip->ecc.layout = &nand_hw_eccoob; 904 chip->ecc.layout = &nand_hw_eccoob;
787 }
788 } 905 }
789} 906}
790 907
791/* s3c2410_nand_probe 908/* s3c24xx_nand_probe
792 * 909 *
793 * called by device layer when it finds a device matching 910 * called by device layer when it finds a device matching
794 * one our driver can handled. This code checks to see if 911 * one our driver can handled. This code checks to see if
795 * it can allocate all necessary resources then calls the 912 * it can allocate all necessary resources then calls the
796 * nand layer to look for devices 913 * nand layer to look for devices
797*/ 914*/
798 915static int s3c24xx_nand_probe(struct platform_device *pdev)
799static int s3c24xx_nand_probe(struct platform_device *pdev,
800 enum s3c_cpu_type cpu_type)
801{ 916{
802 struct s3c2410_platform_nand *plat = to_nand_plat(pdev); 917 struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
918 enum s3c_cpu_type cpu_type;
803 struct s3c2410_nand_info *info; 919 struct s3c2410_nand_info *info;
804 struct s3c2410_nand_mtd *nmtd; 920 struct s3c2410_nand_mtd *nmtd;
805 struct s3c2410_nand_set *sets; 921 struct s3c2410_nand_set *sets;
@@ -809,6 +925,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
809 int nr_sets; 925 int nr_sets;
810 int setno; 926 int setno;
811 927
928 cpu_type = platform_get_device_id(pdev)->driver_data;
929
812 pr_debug("s3c2410_nand_probe(%p)\n", pdev); 930 pr_debug("s3c2410_nand_probe(%p)\n", pdev);
813 931
814 info = kmalloc(sizeof(*info), GFP_KERNEL); 932 info = kmalloc(sizeof(*info), GFP_KERNEL);
@@ -922,7 +1040,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
922 return 0; 1040 return 0;
923 1041
924 exit_error: 1042 exit_error:
925 s3c2410_nand_remove(pdev); 1043 s3c24xx_nand_remove(pdev);
926 1044
927 if (err == 0) 1045 if (err == 0)
928 err = -EINVAL; 1046 err = -EINVAL;
@@ -983,50 +1101,33 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
983 1101
984/* driver device registration */ 1102/* driver device registration */
985 1103
986static int s3c2410_nand_probe(struct platform_device *dev) 1104static struct platform_device_id s3c24xx_driver_ids[] = {
987{ 1105 {
988 return s3c24xx_nand_probe(dev, TYPE_S3C2410); 1106 .name = "s3c2410-nand",
989} 1107 .driver_data = TYPE_S3C2410,
990 1108 }, {
991static int s3c2440_nand_probe(struct platform_device *dev) 1109 .name = "s3c2440-nand",
992{ 1110 .driver_data = TYPE_S3C2440,
993 return s3c24xx_nand_probe(dev, TYPE_S3C2440); 1111 }, {
994} 1112 .name = "s3c2412-nand",
995 1113 .driver_data = TYPE_S3C2412,
996static int s3c2412_nand_probe(struct platform_device *dev) 1114 }, {
997{ 1115 .name = "s3c6400-nand",
998 return s3c24xx_nand_probe(dev, TYPE_S3C2412); 1116 .driver_data = TYPE_S3C2412, /* compatible with 2412 */
999}
1000
1001static struct platform_driver s3c2410_nand_driver = {
1002 .probe = s3c2410_nand_probe,
1003 .remove = s3c2410_nand_remove,
1004 .suspend = s3c24xx_nand_suspend,
1005 .resume = s3c24xx_nand_resume,
1006 .driver = {
1007 .name = "s3c2410-nand",
1008 .owner = THIS_MODULE,
1009 }, 1117 },
1118 { }
1010}; 1119};
1011 1120
1012static struct platform_driver s3c2440_nand_driver = { 1121MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
1013 .probe = s3c2440_nand_probe,
1014 .remove = s3c2410_nand_remove,
1015 .suspend = s3c24xx_nand_suspend,
1016 .resume = s3c24xx_nand_resume,
1017 .driver = {
1018 .name = "s3c2440-nand",
1019 .owner = THIS_MODULE,
1020 },
1021};
1022 1122
1023static struct platform_driver s3c2412_nand_driver = { 1123static struct platform_driver s3c24xx_nand_driver = {
1024 .probe = s3c2412_nand_probe, 1124 .probe = s3c24xx_nand_probe,
1025 .remove = s3c2410_nand_remove, 1125 .remove = s3c24xx_nand_remove,
1026 .suspend = s3c24xx_nand_suspend, 1126 .suspend = s3c24xx_nand_suspend,
1027 .resume = s3c24xx_nand_resume, 1127 .resume = s3c24xx_nand_resume,
1128 .id_table = s3c24xx_driver_ids,
1028 .driver = { 1129 .driver = {
1029 .name = "s3c2412-nand", 1130 .name = "s3c24xx-nand",
1030 .owner = THIS_MODULE, 1131 .owner = THIS_MODULE,
1031 }, 1132 },
1032}; 1133};
@@ -1035,16 +1136,12 @@ static int __init s3c2410_nand_init(void)
1035{ 1136{
1036 printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n"); 1137 printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
1037 1138
1038 platform_driver_register(&s3c2412_nand_driver); 1139 return platform_driver_register(&s3c24xx_nand_driver);
1039 platform_driver_register(&s3c2440_nand_driver);
1040 return platform_driver_register(&s3c2410_nand_driver);
1041} 1140}
1042 1141
1043static void __exit s3c2410_nand_exit(void) 1142static void __exit s3c2410_nand_exit(void)
1044{ 1143{
1045 platform_driver_unregister(&s3c2412_nand_driver); 1144 platform_driver_unregister(&s3c24xx_nand_driver);
1046 platform_driver_unregister(&s3c2440_nand_driver);
1047 platform_driver_unregister(&s3c2410_nand_driver);
1048} 1145}
1049 1146
1050module_init(s3c2410_nand_init); 1147module_init(s3c2410_nand_init);
@@ -1053,6 +1150,3 @@ module_exit(s3c2410_nand_exit);
1053MODULE_LICENSE("GPL"); 1150MODULE_LICENSE("GPL");
1054MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 1151MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
1055MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); 1152MODULE_DESCRIPTION("S3C24XX MTD NAND driver");
1056MODULE_ALIAS("platform:s3c2410-nand");
1057MODULE_ALIAS("platform:s3c2412-nand");
1058MODULE_ALIAS("platform:s3c2440-nand");