Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/davej/cpufreq
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 25 Jul 2011 03:56:18 +0000 (20:56 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 25 Jul 2011 03:56:18 +0000 (20:56 -0700)
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/davej/cpufreq:
  [CPUFREQ] s5pv210: make needlessly global symbols static
  [CPUFREQ] exynos4210: make needlessly global symbols static
  [CPUFREQ] S3C6410: Add some lower frequencies for 800MHz base clock operation
  [CPUFREQ] S5PV210: Add reboot notifier to prevent system hang
  [CPUFREQ] S5PV210: Adjust udelay prior to voltage scaling down
  [CPUFREQ] S5PV210: Lock a mutex while changing the cpu frequency
  [CPUFREQ] S5PV210: Add pm_notifier to prevent system unstable
  [CPUFREQ] S5PV210: Add arm/int voltage control support
  [CPUFREQ] S5PV210: Add additional symantics for "relation" in cpufreq with pm
  [CPUFREQ] S3C64xx: Notify transition complete as soon as frequency changed
  [CPUFREQ] S3C6410: Support 800MHz operation in cpufreq
  [CPUFREQ] s5pv210-cpufreq.c: Add missing clk_put
  [CPUFREQ] Move compile for S3C64XX cpufreq to /drivers/cpufreq
  [CPUFREQ] Remove some vi noise that escaped into the Makefile.
  [CPUFREQ] Move ARM Samsung cpufreq drivers to drivers/cpufreq/
  [CPUFREQ/S3C64xx] Move S3C64xx CPUfreq driver into drivers/cpufreq
  [CPUFREQ] Handle CPUs with different capabilities in acpi-cpufreq

arch/arm/Kconfig
arch/arm/mach-exynos4/Makefile
arch/arm/mach-s3c64xx/Makefile
arch/arm/mach-s5pv210/Makefile
drivers/cpufreq/Kconfig
drivers/cpufreq/Kconfig.arm [new file with mode: 0644]
drivers/cpufreq/Makefile
drivers/cpufreq/acpi-cpufreq.c
drivers/cpufreq/exynos4210-cpufreq.c [moved from arch/arm/mach-exynos4/cpufreq.c with 98% similarity]
drivers/cpufreq/s3c64xx-cpufreq.c [moved from arch/arm/mach-s3c64xx/cpufreq.c with 98% similarity]
drivers/cpufreq/s5pv210-cpufreq.c [moved from arch/arm/mach-s5pv210/cpufreq.c with 73% similarity]

index 1478c61..83a7aa2 100644 (file)
@@ -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
index 60fe5ec..1366995 100644 (file)
@@ -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
index 4657363..f5a7144 100644 (file)
@@ -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
index 50907ac..599a3c0 100644 (file)
@@ -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
 
index 9fb8485..e898215 100644 (file)
@@ -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 (file)
index 0000000..72a0044
--- /dev/null
@@ -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.
index e2fc2d2..ab75e57 100644 (file)
@@ -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
index 596d5dd..56c6c6b 100644 (file)
@@ -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);
similarity index 98%
rename from arch/arm/mach-exynos4/cpufreq.c
rename to drivers/cpufreq/exynos4210-cpufreq.c
index a1bd258..b7c3a84 100644 (file)
@@ -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;
 
similarity index 98%
rename from arch/arm/mach-s3c64xx/cpufreq.c
rename to drivers/cpufreq/s3c64xx-cpufreq.c
index 4375b97..b8d1d20 100644 (file)
@@ -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);
 
similarity index 73%
rename from arch/arm/mach-s5pv210/cpufreq.c
rename to drivers/cpufreq/s5pv210-cpufreq.c
index 153af8b..a484aae 100644 (file)
@@ -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);
 }