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.c168
1 files changed, 127 insertions, 41 deletions
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index b34a460ab679..556139ed1fdf 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -1,26 +1,10 @@
1/* linux/drivers/mtd/nand/s3c2410.c 1/* linux/drivers/mtd/nand/s3c2410.c
2 * 2 *
3 * Copyright (c) 2004,2005 Simtec Electronics 3 * Copyright © 2004-2008 Simtec Electronics
4 * http://www.simtec.co.uk/products/SWLINUX/ 4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk> 5 * Ben Dooks <ben@simtec.co.uk>
6 * 6 *
7 * Samsung S3C2410/S3C240 NAND driver 7 * Samsung S3C2410/S3C2440/S3C2412 NAND driver
8 *
9 * Changelog:
10 * 21-Sep-2004 BJD Initial version
11 * 23-Sep-2004 BJD Multiple device support
12 * 28-Sep-2004 BJD Fixed ECC placement for Hardware mode
13 * 12-Oct-2004 BJD Fixed errors in use of platform data
14 * 18-Feb-2005 BJD Fix sparse errors
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
19 * 08-Jul-2005 BJD Fix OOPS when no platform data supplied
20 * 20-Oct-2005 BJD Fix timing calculation bug
21 * 14-Jan-2006 BJD Allow clock to be stopped when idle
22 *
23 * $Id: s3c2410.c,v 1.23 2006/04/01 18:06:29 bjd Exp $
24 * 8 *
25 * This program is free software; you can redistribute it and/or modify 9 * This program is free software; you can redistribute it and/or modify
26 * it under the terms of the GNU General Public License as published by 10 * it under the terms of the GNU General Public License as published by
@@ -52,6 +36,7 @@
52#include <linux/err.h> 36#include <linux/err.h>
53#include <linux/slab.h> 37#include <linux/slab.h>
54#include <linux/clk.h> 38#include <linux/clk.h>
39#include <linux/cpufreq.h>
55 40
56#include <linux/mtd/mtd.h> 41#include <linux/mtd/mtd.h>
57#include <linux/mtd/nand.h> 42#include <linux/mtd/nand.h>
@@ -120,8 +105,13 @@ struct s3c2410_nand_info {
120 int sel_bit; 105 int sel_bit;
121 int mtd_count; 106 int mtd_count;
122 unsigned long save_sel; 107 unsigned long save_sel;
108 unsigned long clk_rate;
123 109
124 enum s3c_cpu_type cpu_type; 110 enum s3c_cpu_type cpu_type;
111
112#ifdef CONFIG_CPU_FREQ
113 struct notifier_block freq_transition;
114#endif
125}; 115};
126 116
127/* conversion functions */ 117/* conversion functions */
@@ -179,17 +169,18 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
179 169
180/* controller setup */ 170/* controller setup */
181 171
182static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, 172static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
183 struct platform_device *pdev)
184{ 173{
185 struct s3c2410_platform_nand *plat = to_nand_plat(pdev); 174 struct s3c2410_platform_nand *plat = info->platform;
186 unsigned long clkrate = clk_get_rate(info->clk);
187 int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; 175 int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
188 int tacls, twrph0, twrph1; 176 int tacls, twrph0, twrph1;
189 unsigned long cfg = 0; 177 unsigned long clkrate = clk_get_rate(info->clk);
178 unsigned long set, cfg, mask;
179 unsigned long flags;
190 180
191 /* calculate the timing information for the controller */ 181 /* calculate the timing information for the controller */
192 182
183 info->clk_rate = clkrate;
193 clkrate /= 1000; /* turn clock into kHz for ease of use */ 184 clkrate /= 1000; /* turn clock into kHz for ease of use */
194 185
195 if (plat != NULL) { 186 if (plat != NULL) {
@@ -211,28 +202,69 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
211 dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", 202 dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
212 tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate)); 203 tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate));
213 204
205 switch (info->cpu_type) {
206 case TYPE_S3C2410:
207 mask = (S3C2410_NFCONF_TACLS(3) |
208 S3C2410_NFCONF_TWRPH0(7) |
209 S3C2410_NFCONF_TWRPH1(7));
210 set = S3C2410_NFCONF_EN;
211 set |= S3C2410_NFCONF_TACLS(tacls - 1);
212 set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
213 set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
214 break;
215
216 case TYPE_S3C2440:
217 case TYPE_S3C2412:
218 mask = (S3C2410_NFCONF_TACLS(tacls_max - 1) |
219 S3C2410_NFCONF_TWRPH0(7) |
220 S3C2410_NFCONF_TWRPH1(7));
221
222 set = S3C2440_NFCONF_TACLS(tacls - 1);
223 set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
224 set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
225 break;
226
227 default:
228 /* keep compiler happy */
229 mask = 0;
230 set = 0;
231 BUG();
232 }
233
234 dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
235
236 local_irq_save(flags);
237
238 cfg = readl(info->regs + S3C2410_NFCONF);
239 cfg &= ~mask;
240 cfg |= set;
241 writel(cfg, info->regs + S3C2410_NFCONF);
242
243 local_irq_restore(flags);
244
245 return 0;
246}
247
248static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
249{
250 int ret;
251
252 ret = s3c2410_nand_setrate(info);
253 if (ret < 0)
254 return ret;
255
214 switch (info->cpu_type) { 256 switch (info->cpu_type) {
215 case TYPE_S3C2410: 257 case TYPE_S3C2410:
216 cfg = S3C2410_NFCONF_EN; 258 default:
217 cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
218 cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
219 cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
220 break; 259 break;
221 260
222 case TYPE_S3C2440: 261 case TYPE_S3C2440:
223 case TYPE_S3C2412: 262 case TYPE_S3C2412:
224 cfg = S3C2440_NFCONF_TACLS(tacls - 1);
225 cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
226 cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
227
228 /* enable the controller and de-assert nFCE */ 263 /* enable the controller and de-assert nFCE */
229 264
230 writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT); 265 writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);
231 } 266 }
232 267
233 dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
234
235 writel(cfg, info->regs + S3C2410_NFCONF);
236 return 0; 268 return 0;
237} 269}
238 270
@@ -513,6 +545,52 @@ static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int
513 writesl(info->regs + S3C2440_NFDATA, buf, len / 4); 545 writesl(info->regs + S3C2440_NFDATA, buf, len / 4);
514} 546}
515 547
548/* cpufreq driver support */
549
550#ifdef CONFIG_CPU_FREQ
551
552static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb,
553 unsigned long val, void *data)
554{
555 struct s3c2410_nand_info *info;
556 unsigned long newclk;
557
558 info = container_of(nb, struct s3c2410_nand_info, freq_transition);
559 newclk = clk_get_rate(info->clk);
560
561 if ((val == CPUFREQ_POSTCHANGE && newclk < info->clk_rate) ||
562 (val == CPUFREQ_PRECHANGE && newclk > info->clk_rate)) {
563 s3c2410_nand_setrate(info);
564 }
565
566 return 0;
567}
568
569static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info)
570{
571 info->freq_transition.notifier_call = s3c2410_nand_cpufreq_transition;
572
573 return cpufreq_register_notifier(&info->freq_transition,
574 CPUFREQ_TRANSITION_NOTIFIER);
575}
576
577static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info)
578{
579 cpufreq_unregister_notifier(&info->freq_transition,
580 CPUFREQ_TRANSITION_NOTIFIER);
581}
582
583#else
584static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info)
585{
586 return 0;
587}
588
589static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info)
590{
591}
592#endif
593
516/* device management functions */ 594/* device management functions */
517 595
518static int s3c2410_nand_remove(struct platform_device *pdev) 596static int s3c2410_nand_remove(struct platform_device *pdev)
@@ -524,9 +602,10 @@ static int s3c2410_nand_remove(struct platform_device *pdev)
524 if (info == NULL) 602 if (info == NULL)
525 return 0; 603 return 0;
526 604
527 /* first thing we need to do is release all our mtds 605 s3c2410_nand_cpufreq_deregister(info);
528 * and their partitions, then go through freeing the 606
529 * resources used 607 /* Release all our mtds and their partitions, then go through
608 * freeing the resources used
530 */ 609 */
531 610
532 if (info->mtds != NULL) { 611 if (info->mtds != NULL) {
@@ -691,7 +770,8 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
691{ 770{
692 struct nand_chip *chip = &nmtd->chip; 771 struct nand_chip *chip = &nmtd->chip;
693 772
694 printk("%s: chip %p: %d\n", __func__, chip, chip->page_shift); 773 dev_dbg(info->device, "chip %p => page shift %d\n",
774 chip, chip->page_shift);
695 775
696 if (hardware_ecc) { 776 if (hardware_ecc) {
697 /* change the behaviour depending on wether we are using 777 /* change the behaviour depending on wether we are using
@@ -784,7 +864,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
784 864
785 /* initialise the hardware */ 865 /* initialise the hardware */
786 866
787 err = s3c2410_nand_inithw(info, pdev); 867 err = s3c2410_nand_inithw(info);
788 if (err != 0) 868 if (err != 0)
789 goto exit_error; 869 goto exit_error;
790 870
@@ -827,6 +907,12 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
827 sets++; 907 sets++;
828 } 908 }
829 909
910 err = s3c2410_nand_cpufreq_register(info);
911 if (err < 0) {
912 dev_err(&pdev->dev, "failed to init cpufreq support\n");
913 goto exit_error;
914 }
915
830 if (allow_clk_stop(info)) { 916 if (allow_clk_stop(info)) {
831 dev_info(&pdev->dev, "clock idle support enabled\n"); 917 dev_info(&pdev->dev, "clock idle support enabled\n");
832 clk_disable(info->clk); 918 clk_disable(info->clk);
@@ -874,7 +960,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
874 960
875 if (info) { 961 if (info) {
876 clk_enable(info->clk); 962 clk_enable(info->clk);
877 s3c2410_nand_inithw(info, dev); 963 s3c2410_nand_inithw(info);
878 964
879 /* Restore the state of the nFCE line. */ 965 /* Restore the state of the nFCE line. */
880 966