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.c143
1 files changed, 122 insertions, 21 deletions
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 35c6db54c985..556139ed1fdf 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -36,6 +36,7 @@
36#include <linux/err.h> 36#include <linux/err.h>
37#include <linux/slab.h> 37#include <linux/slab.h>
38#include <linux/clk.h> 38#include <linux/clk.h>
39#include <linux/cpufreq.h>
39 40
40#include <linux/mtd/mtd.h> 41#include <linux/mtd/mtd.h>
41#include <linux/mtd/nand.h> 42#include <linux/mtd/nand.h>
@@ -104,8 +105,13 @@ struct s3c2410_nand_info {
104 int sel_bit; 105 int sel_bit;
105 int mtd_count; 106 int mtd_count;
106 unsigned long save_sel; 107 unsigned long save_sel;
108 unsigned long clk_rate;
107 109
108 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
109}; 115};
110 116
111/* conversion functions */ 117/* conversion functions */
@@ -163,17 +169,18 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
163 169
164/* controller setup */ 170/* controller setup */
165 171
166static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, 172static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
167 struct platform_device *pdev)
168{ 173{
169 struct s3c2410_platform_nand *plat = to_nand_plat(pdev); 174 struct s3c2410_platform_nand *plat = info->platform;
170 unsigned long clkrate = clk_get_rate(info->clk);
171 int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; 175 int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
172 int tacls, twrph0, twrph1; 176 int tacls, twrph0, twrph1;
173 unsigned long cfg = 0; 177 unsigned long clkrate = clk_get_rate(info->clk);
178 unsigned long set, cfg, mask;
179 unsigned long flags;
174 180
175 /* calculate the timing information for the controller */ 181 /* calculate the timing information for the controller */
176 182
183 info->clk_rate = clkrate;
177 clkrate /= 1000; /* turn clock into kHz for ease of use */ 184 clkrate /= 1000; /* turn clock into kHz for ease of use */
178 185
179 if (plat != NULL) { 186 if (plat != NULL) {
@@ -195,28 +202,69 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
195 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",
196 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));
197 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
198 switch (info->cpu_type) { 256 switch (info->cpu_type) {
199 case TYPE_S3C2410: 257 case TYPE_S3C2410:
200 cfg = S3C2410_NFCONF_EN; 258 default:
201 cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
202 cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
203 cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
204 break; 259 break;
205 260
206 case TYPE_S3C2440: 261 case TYPE_S3C2440:
207 case TYPE_S3C2412: 262 case TYPE_S3C2412:
208 cfg = S3C2440_NFCONF_TACLS(tacls - 1);
209 cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
210 cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
211
212 /* enable the controller and de-assert nFCE */ 263 /* enable the controller and de-assert nFCE */
213 264
214 writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT); 265 writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);
215 } 266 }
216 267
217 dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
218
219 writel(cfg, info->regs + S3C2410_NFCONF);
220 return 0; 268 return 0;
221} 269}
222 270
@@ -497,6 +545,52 @@ static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int
497 writesl(info->regs + S3C2440_NFDATA, buf, len / 4); 545 writesl(info->regs + S3C2440_NFDATA, buf, len / 4);
498} 546}
499 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
500/* device management functions */ 594/* device management functions */
501 595
502static int s3c2410_nand_remove(struct platform_device *pdev) 596static int s3c2410_nand_remove(struct platform_device *pdev)
@@ -508,9 +602,10 @@ static int s3c2410_nand_remove(struct platform_device *pdev)
508 if (info == NULL) 602 if (info == NULL)
509 return 0; 603 return 0;
510 604
511 /* first thing we need to do is release all our mtds 605 s3c2410_nand_cpufreq_deregister(info);
512 * and their partitions, then go through freeing the 606
513 * resources used 607 /* Release all our mtds and their partitions, then go through
608 * freeing the resources used
514 */ 609 */
515 610
516 if (info->mtds != NULL) { 611 if (info->mtds != NULL) {
@@ -769,7 +864,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
769 864
770 /* initialise the hardware */ 865 /* initialise the hardware */
771 866
772 err = s3c2410_nand_inithw(info, pdev); 867 err = s3c2410_nand_inithw(info);
773 if (err != 0) 868 if (err != 0)
774 goto exit_error; 869 goto exit_error;
775 870
@@ -812,6 +907,12 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
812 sets++; 907 sets++;
813 } 908 }
814 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
815 if (allow_clk_stop(info)) { 916 if (allow_clk_stop(info)) {
816 dev_info(&pdev->dev, "clock idle support enabled\n"); 917 dev_info(&pdev->dev, "clock idle support enabled\n");
817 clk_disable(info->clk); 918 clk_disable(info->clk);
@@ -859,7 +960,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
859 960
860 if (info) { 961 if (info) {
861 clk_enable(info->clk); 962 clk_enable(info->clk);
862 s3c2410_nand_inithw(info, dev); 963 s3c2410_nand_inithw(info);
863 964
864 /* Restore the state of the nFCE line. */ 965 /* Restore the state of the nFCE line. */
865 966