diff options
author | Ben Dooks <ben@simtec.co.uk> | 2009-07-30 23:23:27 +0100 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2009-07-30 23:22:52 +0100 |
commit | a24c091db988551e2c350cfde9eb80ab6e791ffb (patch) | |
tree | 487a86d71ed0a97c27c25de9e618e7db25066c8e /arch/arm/mach-s3c2410/cpu-freq.c | |
parent | 831a6fcb9393960b35173fa2e0f835b710152fff (diff) |
ARM: S3C2410: CPUFREQ: Add core support.
Add core support for frequency scaling on the S3C2410 SoC.
Signed-off-by: Ben Dooks <ben@simtec.co.uk>
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch/arm/mach-s3c2410/cpu-freq.c')
-rw-r--r-- | arch/arm/mach-s3c2410/cpu-freq.c | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/arch/arm/mach-s3c2410/cpu-freq.c b/arch/arm/mach-s3c2410/cpu-freq.c new file mode 100644 index 00000000000..f2cbdbab0df --- /dev/null +++ b/arch/arm/mach-s3c2410/cpu-freq.c @@ -0,0 +1,157 @@ +/* linux/arch/arm/mach-s3c2410/cpu-freq.c + * + * Copyright (c) 2006,2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C2410 CPU Frequency scaling + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/cpufreq.h> +#include <linux/sysdev.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include <mach/regs-clock.h> + +#include <plat/cpu.h> +#include <plat/clock.h> +#include <plat/cpu-freq-core.h> + +/* Note, 2410A has an extra mode for 1:4:4 ratio, bit 2 of CLKDIV */ + +static void s3c2410_cpufreq_setdivs(struct s3c_cpufreq_config *cfg) +{ + u32 clkdiv = 0; + + if (cfg->divs.h_divisor == 2) + clkdiv |= S3C2410_CLKDIVN_HDIVN; + + if (cfg->divs.p_divisor != cfg->divs.h_divisor) + clkdiv |= S3C2410_CLKDIVN_PDIVN; + + __raw_writel(clkdiv, S3C2410_CLKDIVN); +} + +static int s3c2410_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg) +{ + unsigned long hclk, fclk, pclk; + unsigned int hdiv, pdiv; + unsigned long hclk_max; + + fclk = cfg->freq.fclk; + hclk_max = cfg->max.hclk; + + cfg->freq.armclk = fclk; + + s3c_freq_dbg("%s: fclk is %lu, max hclk %lu\n", + __func__, fclk, hclk_max); + + hdiv = (fclk > cfg->max.hclk) ? 2 : 1; + hclk = fclk / hdiv; + + if (hclk > cfg->max.hclk) { + s3c_freq_dbg("%s: hclk too big\n", __func__); + return -EINVAL; + } + + pdiv = (hclk > cfg->max.pclk) ? 2 : 1; + pclk = hclk / pdiv; + + if (pclk > cfg->max.pclk) { + s3c_freq_dbg("%s: pclk too big\n", __func__); + return -EINVAL; + } + + pdiv *= hdiv; + + /* record the result */ + cfg->divs.p_divisor = pdiv; + cfg->divs.h_divisor = hdiv; + + return 0 ; +} + +static struct s3c_cpufreq_info s3c2410_cpufreq_info = { + .max = { + .fclk = 200000000, + .hclk = 100000000, + .pclk = 50000000, + }, + + /* transition latency is about 5ms worst-case, so + * set 10ms to be sure */ + .latency = 10000000, + + .locktime_m = 150, + .locktime_u = 150, + .locktime_bits = 12, + + .need_pll = 1, + + .name = "s3c2410", + .calc_iotiming = s3c2410_iotiming_calc, + .set_iotiming = s3c2410_iotiming_set, + .get_iotiming = s3c2410_iotiming_get, + .resume_clocks = s3c2410_setup_clocks, + + .set_fvco = s3c2410_set_fvco, + .set_refresh = s3c2410_cpufreq_setrefresh, + .set_divs = s3c2410_cpufreq_setdivs, + .calc_divs = s3c2410_cpufreq_calcdivs, +}; + +static int s3c2410_cpufreq_add(struct sys_device *sysdev) +{ + return s3c_cpufreq_register(&s3c2410_cpufreq_info); +} + +static struct sysdev_driver s3c2410_cpufreq_driver = { + .add = s3c2410_cpufreq_add, +}; + +static int __init s3c2410_cpufreq_init(void) +{ + return sysdev_driver_register(&s3c2410_sysclass, + &s3c2410_cpufreq_driver); +} + +arch_initcall(s3c2410_cpufreq_init); + +static int s3c2410a_cpufreq_add(struct sys_device *sysdev) +{ + /* alter the maximum freq settings for S3C2410A. If a board knows + * it only has a maximum of 200, then it should register its own + * limits. */ + + s3c2410_cpufreq_info.max.fclk = 266000000; + s3c2410_cpufreq_info.max.hclk = 133000000; + s3c2410_cpufreq_info.max.pclk = 66500000; + s3c2410_cpufreq_info.name = "s3c2410a"; + + return s3c2410_cpufreq_add(sysdev); +} + +static struct sysdev_driver s3c2410a_cpufreq_driver = { + .add = s3c2410a_cpufreq_add, +}; + +static int __init s3c2410a_cpufreq_init(void) +{ + return sysdev_driver_register(&s3c2410a_sysclass, + &s3c2410a_cpufreq_driver); +} + +arch_initcall(s3c2410a_cpufreq_init); |