diff options
-rw-r--r-- | arch/arm/Kconfig | 4 | ||||
-rw-r--r-- | arch/arm/mach-exynos4/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/Makefile | 4 | ||||
-rw-r--r-- | arch/arm/mach-s5pv210/Makefile | 1 | ||||
-rw-r--r-- | drivers/cpufreq/Kconfig | 5 | ||||
-rw-r--r-- | drivers/cpufreq/Kconfig.arm | 32 | ||||
-rw-r--r-- | drivers/cpufreq/Makefile | 8 | ||||
-rw-r--r-- | drivers/cpufreq/acpi-cpufreq.c | 2 | ||||
-rw-r--r-- | drivers/cpufreq/exynos4210-cpufreq.c (renamed from arch/arm/mach-exynos4/cpufreq.c) | 9 | ||||
-rw-r--r-- | drivers/cpufreq/s3c64xx-cpufreq.c (renamed from arch/arm/mach-s3c64xx/cpufreq.c) | 11 | ||||
-rw-r--r-- | drivers/cpufreq/s5pv210-cpufreq.c (renamed from arch/arm/mach-s5pv210/cpufreq.c) | 210 |
11 files changed, 241 insertions, 46 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 1478c6171b0..83a7aa2ca57 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1895,10 +1895,6 @@ config CPU_FREQ_PXA default y select CPU_FREQ_DEFAULT_GOV_USERSPACE -config CPU_FREQ_S3C64XX - bool "CPUfreq support for Samsung S3C64XX CPUs" - depends on CPU_FREQ && CPU_S3C6410 - config CPU_FREQ_S3C bool help diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-exynos4/Makefile index 60fe5ecf359..1366995d8c2 100644 --- a/arch/arm/mach-exynos4/Makefile +++ b/arch/arm/mach-exynos4/Makefile @@ -15,7 +15,6 @@ obj- := obj-$(CONFIG_CPU_EXYNOS4210) += cpu.o init.o clock.o irq-combiner.o obj-$(CONFIG_CPU_EXYNOS4210) += setup-i2c0.o irq-eint.o dma.o obj-$(CONFIG_PM) += pm.o sleep.o -obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_SMP) += platsmp.o headsmp.o diff --git a/arch/arm/mach-s3c64xx/Makefile b/arch/arm/mach-s3c64xx/Makefile index 4657363f067..f5a7144a052 100644 --- a/arch/arm/mach-s3c64xx/Makefile +++ b/arch/arm/mach-s3c64xx/Makefile @@ -23,10 +23,6 @@ obj-$(CONFIG_CPU_S3C6410) += s3c6410.o obj-y += irq.o obj-y += irq-eint.o -# CPU frequency scaling - -obj-$(CONFIG_CPU_FREQ_S3C64XX) += cpufreq.o - # DMA support obj-$(CONFIG_S3C64XX_DMA) += dma.o diff --git a/arch/arm/mach-s5pv210/Makefile b/arch/arm/mach-s5pv210/Makefile index 50907aca006..599a3c0e8f6 100644 --- a/arch/arm/mach-s5pv210/Makefile +++ b/arch/arm/mach-s5pv210/Makefile @@ -15,7 +15,6 @@ obj- := obj-$(CONFIG_CPU_S5PV210) += cpu.o init.o clock.o dma.o obj-$(CONFIG_CPU_S5PV210) += setup-i2c0.o obj-$(CONFIG_S5PV210_PM) += pm.o sleep.o -obj-$(CONFIG_CPU_FREQ) += cpufreq.o # machine support diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 9fb84853d8e..e898215b88a 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -184,5 +184,10 @@ depends on X86 source "drivers/cpufreq/Kconfig.x86" endmenu +menu "ARM CPU frequency scaling drivers" +depends on ARM +source "drivers/cpufreq/Kconfig.arm" +endmenu + endif endmenu diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm new file mode 100644 index 00000000000..72a0044c1ba --- /dev/null +++ b/drivers/cpufreq/Kconfig.arm @@ -0,0 +1,32 @@ +# +# ARM CPU Frequency scaling drivers +# + +config ARM_S3C64XX_CPUFREQ + bool "Samsung S3C64XX" + depends on CPU_S3C6410 + default y + help + This adds the CPUFreq driver for Samsung S3C6410 SoC. + + If in doubt, say N. + +config ARM_S5PV210_CPUFREQ + bool "Samsung S5PV210 and S5PC110" + depends on CPU_S5PV210 + default y + help + This adds the CPUFreq driver for Samsung S5PV210 and + S5PC110 SoCs. + + If in doubt, say N. + +config ARM_EXYNOS4210_CPUFREQ + bool "Samsung EXYNOS4210" + depends on CPU_EXYNOS4210 + default y + help + This adds the CPUFreq driver for Samsung EXYNOS4210 + SoC (S5PV310 or S5PC210). + + If in doubt, say N. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index e2fc2d21fa6..ab75e573c69 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o # CPUfreq cross-arch helpers obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o -##################################################################################d +################################################################################## # x86 drivers. # Link order matters. K8 is preferred to ACPI because of firmware bugs in early # K8 systems. ACPI is preferred to all other hardware-specific drivers. @@ -37,7 +37,9 @@ obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o -##################################################################################d - +################################################################################## # ARM SoC drivers obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o +obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o +obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o +obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 596d5dd32f4..56c6c6b4eb4 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -655,7 +655,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) acpi_processor_notify_smm(THIS_MODULE); /* Check for APERF/MPERF support in hardware */ - if (cpu_has(c, X86_FEATURE_APERFMPERF)) + if (boot_cpu_has(X86_FEATURE_APERFMPERF)) acpi_cpufreq_driver.getavg = cpufreq_get_measured_perf; pr_debug("CPU%u - ACPI performance management activated.\n", cpu); diff --git a/arch/arm/mach-exynos4/cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index a1bd258f0c4..b7c3a84c4cf 100644 --- a/arch/arm/mach-exynos4/cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c @@ -1,5 +1,4 @@ -/* linux/arch/arm/mach-exynos4/cpufreq.c - * +/* * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * @@ -192,17 +191,17 @@ static unsigned int exynos4_apll_pms_table[CPUFREQ_LEVEL_END] = { ((200 << 16) | (6 << 8) | 4), }; -int exynos4_verify_speed(struct cpufreq_policy *policy) +static int exynos4_verify_speed(struct cpufreq_policy *policy) { return cpufreq_frequency_table_verify(policy, exynos4_freq_table); } -unsigned int exynos4_getspeed(unsigned int cpu) +static unsigned int exynos4_getspeed(unsigned int cpu) { return clk_get_rate(cpu_clk) / 1000; } -void exynos4_set_clkdiv(unsigned int div_index) +static void exynos4_set_clkdiv(unsigned int div_index) { unsigned int tmp; diff --git a/arch/arm/mach-s3c64xx/cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c index 4375b97588b..b8d1d205e1e 100644 --- a/arch/arm/mach-s3c64xx/cpufreq.c +++ b/drivers/cpufreq/s3c64xx-cpufreq.c @@ -1,5 +1,4 @@ -/* linux/arch/arm/plat-s3c64xx/cpufreq.c - * +/* * Copyright 2009 Wolfson Microelectronics plc * * S3C64xx CPUfreq Support @@ -32,11 +31,14 @@ static struct s3c64xx_dvfs s3c64xx_dvfs_table[] = { [1] = { 1050000, 1150000 }, [2] = { 1100000, 1150000 }, [3] = { 1200000, 1350000 }, + [4] = { 1300000, 1350000 }, }; static struct cpufreq_frequency_table s3c64xx_freq_table[] = { { 0, 66000 }, + { 0, 100000 }, { 0, 133000 }, + { 1, 200000 }, { 1, 222000 }, { 1, 266000 }, { 2, 333000 }, @@ -44,6 +46,7 @@ static struct cpufreq_frequency_table s3c64xx_freq_table[] = { { 2, 532000 }, { 2, 533000 }, { 3, 667000 }, + { 4, 800000 }, { 0, CPUFREQ_TABLE_END }, }; #endif @@ -111,6 +114,8 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, goto err; } + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + #ifdef CONFIG_REGULATOR if (vddarm && freqs.new < freqs.old) { ret = regulator_set_voltage(vddarm, @@ -124,8 +129,6 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, } #endif - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); - pr_debug("cpufreq: Set actual frequency %lukHz\n", clk_get_rate(armclk) / 1000); diff --git a/arch/arm/mach-s5pv210/cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index 153af8b359e..a484aaea980 100644 --- a/arch/arm/mach-s5pv210/cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c @@ -1,5 +1,4 @@ -/* linux/arch/arm/mach-s5pv210/cpufreq.c - * +/* * Copyright (c) 2010 Samsung Electronics Co., Ltd. * http://www.samsung.com * @@ -17,6 +16,9 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/cpufreq.h> +#include <linux/reboot.h> +#include <linux/regulator/consumer.h> +#include <linux/suspend.h> #include <mach/map.h> #include <mach/regs-clock.h> @@ -25,11 +27,27 @@ static struct clk *cpu_clk; static struct clk *dmc0_clk; static struct clk *dmc1_clk; static struct cpufreq_freqs freqs; +static DEFINE_MUTEX(set_freq_lock); /* APLL M,P,S values for 1G/800Mhz */ #define APLL_VAL_1000 ((1 << 31) | (125 << 16) | (3 << 8) | 1) #define APLL_VAL_800 ((1 << 31) | (100 << 16) | (3 << 8) | 1) +/* Use 800MHz when entering sleep mode */ +#define SLEEP_FREQ (800 * 1000) + +/* + * relation has an additional symantics other than the standard of cpufreq + * DISALBE_FURTHER_CPUFREQ: disable further access to target + * ENABLE_FURTUER_CPUFREQ: enable access to target + */ +enum cpufreq_access { + DISABLE_FURTHER_CPUFREQ = 0x10, + ENABLE_FURTHER_CPUFREQ = 0x20, +}; + +static bool no_cpufreq_access; + /* * DRAM configurations to calculate refresh counter for changing * frequency of memory. @@ -66,6 +84,40 @@ static struct cpufreq_frequency_table s5pv210_freq_table[] = { {0, CPUFREQ_TABLE_END}, }; +static struct regulator *arm_regulator; +static struct regulator *int_regulator; + +struct s5pv210_dvs_conf { + int arm_volt; /* uV */ + int int_volt; /* uV */ +}; + +static const int arm_volt_max = 1350000; +static const int int_volt_max = 1250000; + +static struct s5pv210_dvs_conf dvs_conf[] = { + [L0] = { + .arm_volt = 1250000, + .int_volt = 1100000, + }, + [L1] = { + .arm_volt = 1200000, + .int_volt = 1100000, + }, + [L2] = { + .arm_volt = 1050000, + .int_volt = 1100000, + }, + [L3] = { + .arm_volt = 950000, + .int_volt = 1100000, + }, + [L4] = { + .arm_volt = 950000, + .int_volt = 1000000, + }, +}; + static u32 clkdiv_val[5][11] = { /* * Clock divider value for following @@ -122,7 +174,7 @@ static void s5pv210_set_refresh(enum s5pv210_dmc_port ch, unsigned long freq) __raw_writel(tmp1, reg); } -int s5pv210_verify_speed(struct cpufreq_policy *policy) +static int s5pv210_verify_speed(struct cpufreq_policy *policy) { if (policy->cpu) return -EINVAL; @@ -130,7 +182,7 @@ int s5pv210_verify_speed(struct cpufreq_policy *policy) return cpufreq_frequency_table_verify(policy, s5pv210_freq_table); } -unsigned int s5pv210_getspeed(unsigned int cpu) +static unsigned int s5pv210_getspeed(unsigned int cpu) { if (cpu) return 0; @@ -146,30 +198,66 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index, priv_index; unsigned int pll_changing = 0; unsigned int bus_speed_changing = 0; + int arm_volt, int_volt; + int ret = 0; + + mutex_lock(&set_freq_lock); + + if (relation & ENABLE_FURTHER_CPUFREQ) + no_cpufreq_access = false; + + if (no_cpufreq_access) { +#ifdef CONFIG_PM_VERBOSE + pr_err("%s:%d denied access to %s as it is disabled" + "temporarily\n", __FILE__, __LINE__, __func__); +#endif + ret = -EINVAL; + goto exit; + } + + if (relation & DISABLE_FURTHER_CPUFREQ) + no_cpufreq_access = true; + + relation &= ~(ENABLE_FURTHER_CPUFREQ | DISABLE_FURTHER_CPUFREQ); freqs.old = s5pv210_getspeed(0); if (cpufreq_frequency_table_target(policy, s5pv210_freq_table, - target_freq, relation, &index)) - return -EINVAL; + target_freq, relation, &index)) { + ret = -EINVAL; + goto exit; + } freqs.new = s5pv210_freq_table[index].frequency; freqs.cpu = 0; if (freqs.new == freqs.old) - return 0; + goto exit; /* Finding current running level index */ if (cpufreq_frequency_table_target(policy, s5pv210_freq_table, - freqs.old, relation, &priv_index)) - return -EINVAL; + freqs.old, relation, &priv_index)) { + ret = -EINVAL; + goto exit; + } - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + arm_volt = dvs_conf[index].arm_volt; + int_volt = dvs_conf[index].int_volt; if (freqs.new > freqs.old) { - /* Voltage up: will be implemented */ + ret = regulator_set_voltage(arm_regulator, + arm_volt, arm_volt_max); + if (ret) + goto exit; + + ret = regulator_set_voltage(int_regulator, + int_volt, int_volt_max); + if (ret) + goto exit; } + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + /* Check if there need to change PLL */ if ((index == L0) || (priv_index == L0)) pll_changing = 1; @@ -380,15 +468,21 @@ static int s5pv210_target(struct cpufreq_policy *policy, } } + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + if (freqs.new < freqs.old) { - /* Voltage down: will be implemented */ - } + regulator_set_voltage(int_regulator, + int_volt, int_volt_max); - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + regulator_set_voltage(arm_regulator, + arm_volt, arm_volt_max); + } printk(KERN_DEBUG "Perf changed[L%d]\n", index); - return 0; +exit: + mutex_unlock(&set_freq_lock); + return ret; } #ifdef CONFIG_PM @@ -416,6 +510,7 @@ static int check_mem_type(void __iomem *dmc_reg) static int __init s5pv210_cpu_init(struct cpufreq_policy *policy) { unsigned long mem_type; + int ret; cpu_clk = clk_get(NULL, "armclk"); if (IS_ERR(cpu_clk)) @@ -423,19 +518,20 @@ static int __init s5pv210_cpu_init(struct cpufreq_policy *policy) dmc0_clk = clk_get(NULL, "sclk_dmc0"); if (IS_ERR(dmc0_clk)) { - clk_put(cpu_clk); - return PTR_ERR(dmc0_clk); + ret = PTR_ERR(dmc0_clk); + goto out_dmc0; } dmc1_clk = clk_get(NULL, "hclk_msys"); if (IS_ERR(dmc1_clk)) { - clk_put(dmc0_clk); - clk_put(cpu_clk); - return PTR_ERR(dmc1_clk); + ret = PTR_ERR(dmc1_clk); + goto out_dmc1; } - if (policy->cpu != 0) - return -EINVAL; + if (policy->cpu != 0) { + ret = -EINVAL; + goto out_dmc1; + } /* * check_mem_type : This driver only support LPDDR & LPDDR2. @@ -445,7 +541,8 @@ static int __init s5pv210_cpu_init(struct cpufreq_policy *policy) if ((mem_type != LPDDR) && (mem_type != LPDDR2)) { printk(KERN_ERR "CPUFreq doesn't support this memory type\n"); - return -EINVAL; + ret = -EINVAL; + goto out_dmc1; } /* Find current refresh counter and frequency each DMC */ @@ -462,6 +559,49 @@ static int __init s5pv210_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = 40000; return cpufreq_frequency_table_cpuinfo(policy, s5pv210_freq_table); + +out_dmc1: + clk_put(dmc0_clk); +out_dmc0: + clk_put(cpu_clk); + return ret; +} + +static int s5pv210_cpufreq_notifier_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + int ret; + + switch (event) { + case PM_SUSPEND_PREPARE: + ret = cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ, + DISABLE_FURTHER_CPUFREQ); + if (ret < 0) + return NOTIFY_BAD; + + return NOTIFY_OK; + case PM_POST_RESTORE: + case PM_POST_SUSPEND: + cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ, + ENABLE_FURTHER_CPUFREQ); + + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + +static int s5pv210_cpufreq_reboot_notifier_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + int ret; + + ret = cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ, + DISABLE_FURTHER_CPUFREQ); + if (ret < 0) + return NOTIFY_BAD; + + return NOTIFY_DONE; } static struct cpufreq_driver s5pv210_driver = { @@ -477,8 +617,32 @@ static struct cpufreq_driver s5pv210_driver = { #endif }; +static struct notifier_block s5pv210_cpufreq_notifier = { + .notifier_call = s5pv210_cpufreq_notifier_event, +}; + +static struct notifier_block s5pv210_cpufreq_reboot_notifier = { + .notifier_call = s5pv210_cpufreq_reboot_notifier_event, +}; + static int __init s5pv210_cpufreq_init(void) { + arm_regulator = regulator_get(NULL, "vddarm"); + if (IS_ERR(arm_regulator)) { + pr_err("failed to get regulator vddarm"); + return PTR_ERR(arm_regulator); + } + + int_regulator = regulator_get(NULL, "vddint"); + if (IS_ERR(int_regulator)) { + pr_err("failed to get regulator vddint"); + regulator_put(arm_regulator); + return PTR_ERR(int_regulator); + } + + register_pm_notifier(&s5pv210_cpufreq_notifier); + register_reboot_notifier(&s5pv210_cpufreq_reboot_notifier); + return cpufreq_register_driver(&s5pv210_driver); } |