CPUFREQ: S3C24XX NAND driver frequency scaling support.

Add support for CPU frequency scalling to the S3C24XX NAND driver.

Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>

authored by

Ben Dooks and committed by
David Woodhouse
30821fee ee39a0e6

+122 -21
+122 -21
drivers/mtd/nand/s3c2410.c
··· 36 #include <linux/err.h> 37 #include <linux/slab.h> 38 #include <linux/clk.h> 39 40 #include <linux/mtd/mtd.h> 41 #include <linux/mtd/nand.h> ··· 105 int sel_bit; 106 int mtd_count; 107 unsigned long save_sel; 108 109 enum s3c_cpu_type cpu_type; 110 }; 111 112 /* conversion functions */ ··· 169 170 /* controller setup */ 171 172 - static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, 173 - struct platform_device *pdev) 174 { 175 - struct s3c2410_platform_nand *plat = to_nand_plat(pdev); 176 - unsigned long clkrate = clk_get_rate(info->clk); 177 int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; 178 int tacls, twrph0, twrph1; 179 - unsigned long cfg = 0; 180 181 /* calculate the timing information for the controller */ 182 183 clkrate /= 1000; /* turn clock into kHz for ease of use */ 184 185 if (plat != NULL) { ··· 202 dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", 203 tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate)); 204 205 switch (info->cpu_type) { 206 case TYPE_S3C2410: 207 - cfg = S3C2410_NFCONF_EN; 208 - cfg |= S3C2410_NFCONF_TACLS(tacls - 1); 209 - cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); 210 - cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); 211 break; 212 213 case TYPE_S3C2440: 214 case TYPE_S3C2412: 215 - cfg = S3C2440_NFCONF_TACLS(tacls - 1); 216 - cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); 217 - cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); 218 - 219 /* enable the controller and de-assert nFCE */ 220 221 writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT); 222 } 223 224 - dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); 225 - 226 - writel(cfg, info->regs + S3C2410_NFCONF); 227 return 0; 228 } 229 ··· 545 writesl(info->regs + S3C2440_NFDATA, buf, len / 4); 546 } 547 548 /* device management functions */ 549 550 static int s3c2410_nand_remove(struct platform_device *pdev) ··· 602 if (info == NULL) 603 return 0; 604 605 - /* first thing we need to do is release all our mtds 606 - * and their partitions, then go through freeing the 607 - * resources used 608 */ 609 610 if (info->mtds != NULL) { ··· 864 865 /* initialise the hardware */ 866 867 - err = s3c2410_nand_inithw(info, pdev); 868 if (err != 0) 869 goto exit_error; 870 ··· 905 906 if (sets != NULL) 907 sets++; 908 } 909 910 if (allow_clk_stop(info)) { ··· 960 961 if (info) { 962 clk_enable(info->clk); 963 - s3c2410_nand_inithw(info, dev); 964 965 /* Restore the state of the nFCE line. */ 966
··· 36 #include <linux/err.h> 37 #include <linux/slab.h> 38 #include <linux/clk.h> 39 + #include <linux/cpufreq.h> 40 41 #include <linux/mtd/mtd.h> 42 #include <linux/mtd/nand.h> ··· 104 int sel_bit; 105 int mtd_count; 106 unsigned long save_sel; 107 + unsigned long clk_rate; 108 109 enum s3c_cpu_type cpu_type; 110 + 111 + #ifdef CONFIG_CPU_FREQ 112 + struct notifier_block freq_transition; 113 + #endif 114 }; 115 116 /* conversion functions */ ··· 163 164 /* controller setup */ 165 166 + static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) 167 { 168 + struct s3c2410_platform_nand *plat = info->platform; 169 int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; 170 int tacls, twrph0, twrph1; 171 + unsigned long clkrate = clk_get_rate(info->clk); 172 + unsigned long set, cfg, mask; 173 + unsigned long flags; 174 175 /* calculate the timing information for the controller */ 176 177 + info->clk_rate = clkrate; 178 clkrate /= 1000; /* turn clock into kHz for ease of use */ 179 180 if (plat != NULL) { ··· 195 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)); 197 198 + switch (info->cpu_type) { 199 + case TYPE_S3C2410: 200 + mask = (S3C2410_NFCONF_TACLS(3) | 201 + S3C2410_NFCONF_TWRPH0(7) | 202 + S3C2410_NFCONF_TWRPH1(7)); 203 + set = S3C2410_NFCONF_EN; 204 + set |= S3C2410_NFCONF_TACLS(tacls - 1); 205 + set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); 206 + set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); 207 + break; 208 + 209 + case TYPE_S3C2440: 210 + case TYPE_S3C2412: 211 + mask = (S3C2410_NFCONF_TACLS(tacls_max - 1) | 212 + S3C2410_NFCONF_TWRPH0(7) | 213 + S3C2410_NFCONF_TWRPH1(7)); 214 + 215 + set = S3C2440_NFCONF_TACLS(tacls - 1); 216 + set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); 217 + set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); 218 + break; 219 + 220 + default: 221 + /* keep compiler happy */ 222 + mask = 0; 223 + set = 0; 224 + BUG(); 225 + } 226 + 227 + dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); 228 + 229 + local_irq_save(flags); 230 + 231 + cfg = readl(info->regs + S3C2410_NFCONF); 232 + cfg &= ~mask; 233 + cfg |= set; 234 + writel(cfg, info->regs + S3C2410_NFCONF); 235 + 236 + local_irq_restore(flags); 237 + 238 + return 0; 239 + } 240 + 241 + static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) 242 + { 243 + int ret; 244 + 245 + ret = s3c2410_nand_setrate(info); 246 + if (ret < 0) 247 + return ret; 248 + 249 switch (info->cpu_type) { 250 case TYPE_S3C2410: 251 + default: 252 break; 253 254 case TYPE_S3C2440: 255 case TYPE_S3C2412: 256 /* enable the controller and de-assert nFCE */ 257 258 writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT); 259 } 260 261 return 0; 262 } 263 ··· 497 writesl(info->regs + S3C2440_NFDATA, buf, len / 4); 498 } 499 500 + /* cpufreq driver support */ 501 + 502 + #ifdef CONFIG_CPU_FREQ 503 + 504 + static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb, 505 + unsigned long val, void *data) 506 + { 507 + struct s3c2410_nand_info *info; 508 + unsigned long newclk; 509 + 510 + info = container_of(nb, struct s3c2410_nand_info, freq_transition); 511 + newclk = clk_get_rate(info->clk); 512 + 513 + if ((val == CPUFREQ_POSTCHANGE && newclk < info->clk_rate) || 514 + (val == CPUFREQ_PRECHANGE && newclk > info->clk_rate)) { 515 + s3c2410_nand_setrate(info); 516 + } 517 + 518 + return 0; 519 + } 520 + 521 + static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) 522 + { 523 + info->freq_transition.notifier_call = s3c2410_nand_cpufreq_transition; 524 + 525 + return cpufreq_register_notifier(&info->freq_transition, 526 + CPUFREQ_TRANSITION_NOTIFIER); 527 + } 528 + 529 + static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) 530 + { 531 + cpufreq_unregister_notifier(&info->freq_transition, 532 + CPUFREQ_TRANSITION_NOTIFIER); 533 + } 534 + 535 + #else 536 + static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) 537 + { 538 + return 0; 539 + } 540 + 541 + static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) 542 + { 543 + } 544 + #endif 545 + 546 /* device management functions */ 547 548 static int s3c2410_nand_remove(struct platform_device *pdev) ··· 508 if (info == NULL) 509 return 0; 510 511 + s3c2410_nand_cpufreq_deregister(info); 512 + 513 + /* Release all our mtds and their partitions, then go through 514 + * freeing the resources used 515 */ 516 517 if (info->mtds != NULL) { ··· 769 770 /* initialise the hardware */ 771 772 + err = s3c2410_nand_inithw(info); 773 if (err != 0) 774 goto exit_error; 775 ··· 810 811 if (sets != NULL) 812 sets++; 813 + } 814 + 815 + err = s3c2410_nand_cpufreq_register(info); 816 + if (err < 0) { 817 + dev_err(&pdev->dev, "failed to init cpufreq support\n"); 818 + goto exit_error; 819 } 820 821 if (allow_clk_stop(info)) { ··· 859 860 if (info) { 861 clk_enable(info->clk); 862 + s3c2410_nand_inithw(info); 863 864 /* Restore the state of the nFCE line. */ 865