diff options
24 files changed, 442 insertions, 280 deletions
diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt index 366690cb86a3..e41c98ffbccb 100644 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | Generic CPU0 cpufreq driver | 1 | Generic cpufreq driver |
| 2 | 2 | ||
| 3 | It is a generic cpufreq driver for CPU0 frequency management. It | 3 | It is a generic DT based cpufreq driver for frequency management. It supports |
| 4 | supports both uniprocessor (UP) and symmetric multiprocessor (SMP) | 4 | both uniprocessor (UP) and symmetric multiprocessor (SMP) systems which share |
| 5 | systems which share clock and voltage across all CPUs. | 5 | clock and voltage across all CPUs. |
| 6 | 6 | ||
| 7 | Both required and optional properties listed below must be defined | 7 | Both required and optional properties listed below must be defined |
| 8 | under node /cpus/cpu@0. | 8 | under node /cpus/cpu@0. |
diff --git a/arch/arm/configs/mvebu_v7_defconfig b/arch/arm/configs/mvebu_v7_defconfig index fdfda1fa9521..7309988b0f1f 100644 --- a/arch/arm/configs/mvebu_v7_defconfig +++ b/arch/arm/configs/mvebu_v7_defconfig | |||
| @@ -32,7 +32,7 @@ CONFIG_ARM_ATAG_DTB_COMPAT=y | |||
| 32 | CONFIG_CPU_IDLE=y | 32 | CONFIG_CPU_IDLE=y |
| 33 | CONFIG_ARM_MVEBU_V7_CPUIDLE=y | 33 | CONFIG_ARM_MVEBU_V7_CPUIDLE=y |
| 34 | CONFIG_CPU_FREQ=y | 34 | CONFIG_CPU_FREQ=y |
| 35 | CONFIG_CPUFREQ_GENERIC=y | 35 | CONFIG_CPUFREQ_DT=y |
| 36 | CONFIG_VFP=y | 36 | CONFIG_VFP=y |
| 37 | CONFIG_NET=y | 37 | CONFIG_NET=y |
| 38 | CONFIG_INET=y | 38 | CONFIG_INET=y |
diff --git a/arch/arm/mach-imx/imx27-dt.c b/arch/arm/mach-imx/imx27-dt.c index 080e66c6a1d0..dc8f1a6f45f2 100644 --- a/arch/arm/mach-imx/imx27-dt.c +++ b/arch/arm/mach-imx/imx27-dt.c | |||
| @@ -20,7 +20,7 @@ | |||
| 20 | 20 | ||
| 21 | static void __init imx27_dt_init(void) | 21 | static void __init imx27_dt_init(void) |
| 22 | { | 22 | { |
| 23 | struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; | 23 | struct platform_device_info devinfo = { .name = "cpufreq-dt", }; |
| 24 | 24 | ||
| 25 | mxc_arch_reset_init_dt(); | 25 | mxc_arch_reset_init_dt(); |
| 26 | 26 | ||
diff --git a/arch/arm/mach-imx/mach-imx51.c b/arch/arm/mach-imx/mach-imx51.c index c77deb3f0893..2c5fcaf8675b 100644 --- a/arch/arm/mach-imx/mach-imx51.c +++ b/arch/arm/mach-imx/mach-imx51.c | |||
| @@ -51,7 +51,7 @@ static void __init imx51_ipu_mipi_setup(void) | |||
| 51 | 51 | ||
| 52 | static void __init imx51_dt_init(void) | 52 | static void __init imx51_dt_init(void) |
| 53 | { | 53 | { |
| 54 | struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; | 54 | struct platform_device_info devinfo = { .name = "cpufreq-dt", }; |
| 55 | 55 | ||
| 56 | mxc_arch_reset_init_dt(); | 56 | mxc_arch_reset_init_dt(); |
| 57 | imx51_ipu_mipi_setup(); | 57 | imx51_ipu_mipi_setup(); |
diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index 8a70a51533fd..bbd8664d1bac 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c | |||
| @@ -644,7 +644,7 @@ static int __init armada_xp_pmsu_cpufreq_init(void) | |||
| 644 | } | 644 | } |
| 645 | } | 645 | } |
| 646 | 646 | ||
| 647 | platform_device_register_simple("cpufreq-generic", -1, NULL, 0); | 647 | platform_device_register_simple("cpufreq-dt", -1, NULL, 0); |
| 648 | return 0; | 648 | return 0; |
| 649 | } | 649 | } |
| 650 | 650 | ||
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index 828aee9ea6a8..58920bc8807b 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c | |||
| @@ -282,7 +282,7 @@ static inline void omap_init_cpufreq(void) | |||
| 282 | if (!of_have_populated_dt()) | 282 | if (!of_have_populated_dt()) |
| 283 | devinfo.name = "omap-cpufreq"; | 283 | devinfo.name = "omap-cpufreq"; |
| 284 | else | 284 | else |
| 285 | devinfo.name = "cpufreq-cpu0"; | 285 | devinfo.name = "cpufreq-dt"; |
| 286 | platform_device_register_full(&devinfo); | 286 | platform_device_register_full(&devinfo); |
| 287 | } | 287 | } |
| 288 | 288 | ||
diff --git a/arch/arm/mach-shmobile/board-ape6evm-reference.c b/arch/arm/mach-shmobile/board-ape6evm-reference.c index 2f7723e5fe91..0110751da511 100644 --- a/arch/arm/mach-shmobile/board-ape6evm-reference.c +++ b/arch/arm/mach-shmobile/board-ape6evm-reference.c | |||
| @@ -50,7 +50,7 @@ static void __init ape6evm_add_standard_devices(void) | |||
| 50 | 50 | ||
| 51 | r8a73a4_add_dt_devices(); | 51 | r8a73a4_add_dt_devices(); |
| 52 | of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); | 52 | of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); |
| 53 | platform_device_register_simple("cpufreq-cpu0", -1, NULL, 0); | 53 | platform_device_register_simple("cpufreq-dt", -1, NULL, 0); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | static const char *ape6evm_boards_compat_dt[] __initdata = { | 56 | static const char *ape6evm_boards_compat_dt[] __initdata = { |
diff --git a/arch/arm/mach-shmobile/cpufreq.c b/arch/arm/mach-shmobile/cpufreq.c index 8a24b2be46ae..57fbff024dcd 100644 --- a/arch/arm/mach-shmobile/cpufreq.c +++ b/arch/arm/mach-shmobile/cpufreq.c | |||
| @@ -12,6 +12,6 @@ | |||
| 12 | 12 | ||
| 13 | int __init shmobile_cpufreq_init(void) | 13 | int __init shmobile_cpufreq_init(void) |
| 14 | { | 14 | { |
| 15 | platform_device_register_simple("cpufreq-cpu0", -1, NULL, 0); | 15 | platform_device_register_simple("cpufreq-dt", -1, NULL, 0); |
| 16 | return 0; | 16 | return 0; |
| 17 | } | 17 | } |
diff --git a/arch/arm/mach-shmobile/setup-sh73a0.c b/arch/arm/mach-shmobile/setup-sh73a0.c index 2c802ae9b241..15b990cd8c70 100644 --- a/arch/arm/mach-shmobile/setup-sh73a0.c +++ b/arch/arm/mach-shmobile/setup-sh73a0.c | |||
| @@ -775,7 +775,7 @@ void __init sh73a0_add_early_devices(void) | |||
| 775 | 775 | ||
| 776 | void __init sh73a0_add_standard_devices_dt(void) | 776 | void __init sh73a0_add_standard_devices_dt(void) |
| 777 | { | 777 | { |
| 778 | struct platform_device_info devinfo = { .name = "cpufreq-cpu0", .id = -1, }; | 778 | struct platform_device_info devinfo = { .name = "cpufreq-dt", .id = -1, }; |
| 779 | 779 | ||
| 780 | /* clocks are setup late during boot in the case of DT */ | 780 | /* clocks are setup late during boot in the case of DT */ |
| 781 | sh73a0_clock_init(); | 781 | sh73a0_clock_init(); |
| @@ -784,7 +784,7 @@ void __init sh73a0_add_standard_devices_dt(void) | |||
| 784 | ARRAY_SIZE(sh73a0_devices_dt)); | 784 | ARRAY_SIZE(sh73a0_devices_dt)); |
| 785 | of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); | 785 | of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); |
| 786 | 786 | ||
| 787 | /* Instantiate cpufreq-cpu0 */ | 787 | /* Instantiate cpufreq-dt */ |
| 788 | platform_device_register_full(&devinfo); | 788 | platform_device_register_full(&devinfo); |
| 789 | } | 789 | } |
| 790 | 790 | ||
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c index 31a6fa40ba37..ec03ec40e9c6 100644 --- a/arch/arm/mach-zynq/common.c +++ b/arch/arm/mach-zynq/common.c | |||
| @@ -104,7 +104,7 @@ static int __init zynq_get_revision(void) | |||
| 104 | */ | 104 | */ |
| 105 | static void __init zynq_init_machine(void) | 105 | static void __init zynq_init_machine(void) |
| 106 | { | 106 | { |
| 107 | struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; | 107 | struct platform_device_info devinfo = { .name = "cpufreq-dt", }; |
| 108 | struct soc_device_attribute *soc_dev_attr; | 108 | struct soc_device_attribute *soc_dev_attr; |
| 109 | struct soc_device *soc_dev; | 109 | struct soc_device *soc_dev; |
| 110 | struct device *parent = NULL; | 110 | struct device *parent = NULL; |
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index ffe350f86bca..3489f8f5fada 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig | |||
| @@ -183,14 +183,14 @@ config CPU_FREQ_GOV_CONSERVATIVE | |||
| 183 | 183 | ||
| 184 | If in doubt, say N. | 184 | If in doubt, say N. |
| 185 | 185 | ||
| 186 | config GENERIC_CPUFREQ_CPU0 | 186 | config CPUFREQ_DT |
| 187 | tristate "Generic CPU0 cpufreq driver" | 187 | tristate "Generic DT based cpufreq driver" |
| 188 | depends on HAVE_CLK && OF | 188 | depends on HAVE_CLK && OF |
| 189 | # if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y: | 189 | # if CPU_THERMAL is on and THERMAL=m, CPUFREQ_DT cannot be =y: |
| 190 | depends on !CPU_THERMAL || THERMAL | 190 | depends on !CPU_THERMAL || THERMAL |
| 191 | select PM_OPP | 191 | select PM_OPP |
| 192 | help | 192 | help |
| 193 | This adds a generic cpufreq driver for CPU0 frequency management. | 193 | This adds a generic DT based cpufreq driver for frequency management. |
| 194 | It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) | 194 | It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) |
| 195 | systems which share clock and voltage across all CPUs. | 195 | systems which share clock and voltage across all CPUs. |
| 196 | 196 | ||
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 7364a538e056..48ed28b789f7 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm | |||
| @@ -92,7 +92,7 @@ config ARM_EXYNOS_CPU_FREQ_BOOST_SW | |||
| 92 | 92 | ||
| 93 | config ARM_HIGHBANK_CPUFREQ | 93 | config ARM_HIGHBANK_CPUFREQ |
| 94 | tristate "Calxeda Highbank-based" | 94 | tristate "Calxeda Highbank-based" |
| 95 | depends on ARCH_HIGHBANK && GENERIC_CPUFREQ_CPU0 && REGULATOR | 95 | depends on ARCH_HIGHBANK && CPUFREQ_DT && REGULATOR |
| 96 | default m | 96 | default m |
| 97 | help | 97 | help |
| 98 | This adds the CPUFreq driver for Calxeda Highbank SoC | 98 | This adds the CPUFreq driver for Calxeda Highbank SoC |
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index db6d9a2fea4d..40c53dc1937e 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile | |||
| @@ -13,7 +13,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o | |||
| 13 | obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o | 13 | obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o |
| 14 | obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o | 14 | obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o |
| 15 | 15 | ||
| 16 | obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o | 16 | obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o |
| 17 | 17 | ||
| 18 | ################################################################################## | 18 | ################################################################################## |
| 19 | # x86 drivers. | 19 | # x86 drivers. |
diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c deleted file mode 100644 index 0d2172b07765..000000000000 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ /dev/null | |||
| @@ -1,248 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Freescale Semiconductor, Inc. | ||
| 3 | * | ||
| 4 | * The OPP code in function cpu0_set_target() is reused from | ||
| 5 | * drivers/cpufreq/omap-cpufreq.c | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 13 | |||
| 14 | #include <linux/clk.h> | ||
| 15 | #include <linux/cpu.h> | ||
| 16 | #include <linux/cpu_cooling.h> | ||
| 17 | #include <linux/cpufreq.h> | ||
| 18 | #include <linux/cpumask.h> | ||
| 19 | #include <linux/err.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/of.h> | ||
| 22 | #include <linux/pm_opp.h> | ||
| 23 | #include <linux/platform_device.h> | ||
| 24 | #include <linux/regulator/consumer.h> | ||
| 25 | #include <linux/slab.h> | ||
| 26 | #include <linux/thermal.h> | ||
| 27 | |||
| 28 | static unsigned int transition_latency; | ||
| 29 | static unsigned int voltage_tolerance; /* in percentage */ | ||
| 30 | |||
| 31 | static struct device *cpu_dev; | ||
| 32 | static struct clk *cpu_clk; | ||
| 33 | static struct regulator *cpu_reg; | ||
| 34 | static struct cpufreq_frequency_table *freq_table; | ||
| 35 | static struct thermal_cooling_device *cdev; | ||
| 36 | |||
| 37 | static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) | ||
| 38 | { | ||
| 39 | struct dev_pm_opp *opp; | ||
| 40 | unsigned long volt = 0, volt_old = 0, tol = 0; | ||
| 41 | unsigned int old_freq, new_freq; | ||
| 42 | long freq_Hz, freq_exact; | ||
| 43 | int ret; | ||
| 44 | |||
| 45 | freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); | ||
| 46 | if (freq_Hz <= 0) | ||
| 47 | freq_Hz = freq_table[index].frequency * 1000; | ||
| 48 | |||
| 49 | freq_exact = freq_Hz; | ||
| 50 | new_freq = freq_Hz / 1000; | ||
| 51 | old_freq = clk_get_rate(cpu_clk) / 1000; | ||
| 52 | |||
| 53 | if (!IS_ERR(cpu_reg)) { | ||
| 54 | rcu_read_lock(); | ||
| 55 | opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); | ||
| 56 | if (IS_ERR(opp)) { | ||
| 57 | rcu_read_unlock(); | ||
| 58 | pr_err("failed to find OPP for %ld\n", freq_Hz); | ||
| 59 | return PTR_ERR(opp); | ||
| 60 | } | ||
| 61 | volt = dev_pm_opp_get_voltage(opp); | ||
| 62 | rcu_read_unlock(); | ||
| 63 | tol = volt * voltage_tolerance / 100; | ||
| 64 | volt_old = regulator_get_voltage(cpu_reg); | ||
| 65 | } | ||
| 66 | |||
| 67 | pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n", | ||
| 68 | old_freq / 1000, volt_old ? volt_old / 1000 : -1, | ||
| 69 | new_freq / 1000, volt ? volt / 1000 : -1); | ||
| 70 | |||
| 71 | /* scaling up? scale voltage before frequency */ | ||
| 72 | if (!IS_ERR(cpu_reg) && new_freq > old_freq) { | ||
| 73 | ret = regulator_set_voltage_tol(cpu_reg, volt, tol); | ||
| 74 | if (ret) { | ||
| 75 | pr_err("failed to scale voltage up: %d\n", ret); | ||
| 76 | return ret; | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | ret = clk_set_rate(cpu_clk, freq_exact); | ||
| 81 | if (ret) { | ||
| 82 | pr_err("failed to set clock rate: %d\n", ret); | ||
| 83 | if (!IS_ERR(cpu_reg)) | ||
| 84 | regulator_set_voltage_tol(cpu_reg, volt_old, tol); | ||
| 85 | return ret; | ||
| 86 | } | ||
| 87 | |||
| 88 | /* scaling down? scale voltage after frequency */ | ||
| 89 | if (!IS_ERR(cpu_reg) && new_freq < old_freq) { | ||
| 90 | ret = regulator_set_voltage_tol(cpu_reg, volt, tol); | ||
| 91 | if (ret) { | ||
| 92 | pr_err("failed to scale voltage down: %d\n", ret); | ||
| 93 | clk_set_rate(cpu_clk, old_freq * 1000); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | return ret; | ||
| 98 | } | ||
| 99 | |||
| 100 | static int cpu0_cpufreq_init(struct cpufreq_policy *policy) | ||
| 101 | { | ||
| 102 | policy->clk = cpu_clk; | ||
| 103 | return cpufreq_generic_init(policy, freq_table, transition_latency); | ||
| 104 | } | ||
| 105 | |||
| 106 | static struct cpufreq_driver cpu0_cpufreq_driver = { | ||
| 107 | .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, | ||
| 108 | .verify = cpufreq_generic_frequency_table_verify, | ||
| 109 | .target_index = cpu0_set_target, | ||
| 110 | .get = cpufreq_generic_get, | ||
| 111 | .init = cpu0_cpufreq_init, | ||
| 112 | .name = "generic_cpu0", | ||
| 113 | .attr = cpufreq_generic_attr, | ||
| 114 | }; | ||
| 115 | |||
| 116 | static int cpu0_cpufreq_probe(struct platform_device *pdev) | ||
| 117 | { | ||
| 118 | struct device_node *np; | ||
| 119 | int ret; | ||
| 120 | |||
| 121 | cpu_dev = get_cpu_device(0); | ||
| 122 | if (!cpu_dev) { | ||
| 123 | pr_err("failed to get cpu0 device\n"); | ||
| 124 | return -ENODEV; | ||
| 125 | } | ||
| 126 | |||
| 127 | np = of_node_get(cpu_dev->of_node); | ||
| 128 | if (!np) { | ||
| 129 | pr_err("failed to find cpu0 node\n"); | ||
| 130 | return -ENOENT; | ||
| 131 | } | ||
| 132 | |||
| 133 | cpu_reg = regulator_get_optional(cpu_dev, "cpu0"); | ||
| 134 | if (IS_ERR(cpu_reg)) { | ||
| 135 | /* | ||
| 136 | * If cpu0 regulator supply node is present, but regulator is | ||
| 137 | * not yet registered, we should try defering probe. | ||
| 138 | */ | ||
| 139 | if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) { | ||
| 140 | dev_dbg(cpu_dev, "cpu0 regulator not ready, retry\n"); | ||
| 141 | ret = -EPROBE_DEFER; | ||
| 142 | goto out_put_node; | ||
| 143 | } | ||
| 144 | pr_warn("failed to get cpu0 regulator: %ld\n", | ||
| 145 | PTR_ERR(cpu_reg)); | ||
| 146 | } | ||
| 147 | |||
| 148 | cpu_clk = clk_get(cpu_dev, NULL); | ||
| 149 | if (IS_ERR(cpu_clk)) { | ||
| 150 | ret = PTR_ERR(cpu_clk); | ||
| 151 | pr_err("failed to get cpu0 clock: %d\n", ret); | ||
| 152 | goto out_put_reg; | ||
| 153 | } | ||
| 154 | |||
| 155 | /* OPPs might be populated at runtime, don't check for error here */ | ||
| 156 | of_init_opp_table(cpu_dev); | ||
| 157 | |||
| 158 | ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); | ||
| 159 | if (ret) { | ||
| 160 | pr_err("failed to init cpufreq table: %d\n", ret); | ||
| 161 | goto out_put_clk; | ||
| 162 | } | ||
| 163 | |||
| 164 | of_property_read_u32(np, "voltage-tolerance", &voltage_tolerance); | ||
| 165 | |||
| 166 | if (of_property_read_u32(np, "clock-latency", &transition_latency)) | ||
| 167 | transition_latency = CPUFREQ_ETERNAL; | ||
| 168 | |||
| 169 | if (!IS_ERR(cpu_reg)) { | ||
| 170 | struct dev_pm_opp *opp; | ||
| 171 | unsigned long min_uV, max_uV; | ||
| 172 | int i; | ||
| 173 | |||
| 174 | /* | ||
| 175 | * OPP is maintained in order of increasing frequency, and | ||
| 176 | * freq_table initialised from OPP is therefore sorted in the | ||
| 177 | * same order. | ||
| 178 | */ | ||
| 179 | for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) | ||
| 180 | ; | ||
| 181 | rcu_read_lock(); | ||
| 182 | opp = dev_pm_opp_find_freq_exact(cpu_dev, | ||
| 183 | freq_table[0].frequency * 1000, true); | ||
| 184 | min_uV = dev_pm_opp_get_voltage(opp); | ||
| 185 | opp = dev_pm_opp_find_freq_exact(cpu_dev, | ||
| 186 | freq_table[i-1].frequency * 1000, true); | ||
| 187 | max_uV = dev_pm_opp_get_voltage(opp); | ||
| 188 | rcu_read_unlock(); | ||
| 189 | ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV); | ||
| 190 | if (ret > 0) | ||
| 191 | transition_latency += ret * 1000; | ||
| 192 | } | ||
| 193 | |||
| 194 | ret = cpufreq_register_driver(&cpu0_cpufreq_driver); | ||
| 195 | if (ret) { | ||
| 196 | pr_err("failed register driver: %d\n", ret); | ||
| 197 | goto out_free_table; | ||
| 198 | } | ||
| 199 | |||
| 200 | /* | ||
| 201 | * For now, just loading the cooling device; | ||
| 202 | * thermal DT code takes care of matching them. | ||
| 203 | */ | ||
| 204 | if (of_find_property(np, "#cooling-cells", NULL)) { | ||
| 205 | cdev = of_cpufreq_cooling_register(np, cpu_present_mask); | ||
| 206 | if (IS_ERR(cdev)) | ||
| 207 | pr_err("running cpufreq without cooling device: %ld\n", | ||
| 208 | PTR_ERR(cdev)); | ||
| 209 | } | ||
| 210 | |||
| 211 | of_node_put(np); | ||
| 212 | return 0; | ||
| 213 | |||
| 214 | out_free_table: | ||
| 215 | dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); | ||
| 216 | out_put_clk: | ||
| 217 | if (!IS_ERR(cpu_clk)) | ||
| 218 | clk_put(cpu_clk); | ||
| 219 | out_put_reg: | ||
| 220 | if (!IS_ERR(cpu_reg)) | ||
| 221 | regulator_put(cpu_reg); | ||
| 222 | out_put_node: | ||
| 223 | of_node_put(np); | ||
| 224 | return ret; | ||
| 225 | } | ||
| 226 | |||
| 227 | static int cpu0_cpufreq_remove(struct platform_device *pdev) | ||
| 228 | { | ||
| 229 | cpufreq_cooling_unregister(cdev); | ||
| 230 | cpufreq_unregister_driver(&cpu0_cpufreq_driver); | ||
| 231 | dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); | ||
| 232 | |||
| 233 | return 0; | ||
| 234 | } | ||
| 235 | |||
| 236 | static struct platform_driver cpu0_cpufreq_platdrv = { | ||
| 237 | .driver = { | ||
| 238 | .name = "cpufreq-cpu0", | ||
| 239 | .owner = THIS_MODULE, | ||
| 240 | }, | ||
| 241 | .probe = cpu0_cpufreq_probe, | ||
| 242 | .remove = cpu0_cpufreq_remove, | ||
| 243 | }; | ||
| 244 | module_platform_driver(cpu0_cpufreq_platdrv); | ||
| 245 | |||
| 246 | MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); | ||
| 247 | MODULE_DESCRIPTION("Generic CPU0 cpufreq driver"); | ||
| 248 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c new file mode 100644 index 000000000000..6bbb8b913446 --- /dev/null +++ b/drivers/cpufreq/cpufreq-dt.c | |||
| @@ -0,0 +1,364 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Freescale Semiconductor, Inc. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2014 Linaro. | ||
| 5 | * Viresh Kumar <viresh.kumar@linaro.org> | ||
| 6 | * | ||
| 7 | * The OPP code in function set_target() is reused from | ||
| 8 | * drivers/cpufreq/omap-cpufreq.c | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License version 2 as | ||
| 12 | * published by the Free Software Foundation. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 16 | |||
| 17 | #include <linux/clk.h> | ||
| 18 | #include <linux/cpu.h> | ||
| 19 | #include <linux/cpu_cooling.h> | ||
| 20 | #include <linux/cpufreq.h> | ||
| 21 | #include <linux/cpumask.h> | ||
| 22 | #include <linux/err.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/of.h> | ||
| 25 | #include <linux/pm_opp.h> | ||
| 26 | #include <linux/platform_device.h> | ||
| 27 | #include <linux/regulator/consumer.h> | ||
| 28 | #include <linux/slab.h> | ||
| 29 | #include <linux/thermal.h> | ||
| 30 | |||
| 31 | struct private_data { | ||
| 32 | struct device *cpu_dev; | ||
| 33 | struct regulator *cpu_reg; | ||
| 34 | struct thermal_cooling_device *cdev; | ||
| 35 | unsigned int voltage_tolerance; /* in percentage */ | ||
| 36 | }; | ||
| 37 | |||
| 38 | static int set_target(struct cpufreq_policy *policy, unsigned int index) | ||
| 39 | { | ||
| 40 | struct dev_pm_opp *opp; | ||
| 41 | struct cpufreq_frequency_table *freq_table = policy->freq_table; | ||
| 42 | struct clk *cpu_clk = policy->clk; | ||
| 43 | struct private_data *priv = policy->driver_data; | ||
| 44 | struct device *cpu_dev = priv->cpu_dev; | ||
| 45 | struct regulator *cpu_reg = priv->cpu_reg; | ||
| 46 | unsigned long volt = 0, volt_old = 0, tol = 0; | ||
| 47 | unsigned int old_freq, new_freq; | ||
| 48 | long freq_Hz, freq_exact; | ||
| 49 | int ret; | ||
| 50 | |||
| 51 | freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); | ||
| 52 | if (freq_Hz <= 0) | ||
| 53 | freq_Hz = freq_table[index].frequency * 1000; | ||
| 54 | |||
| 55 | freq_exact = freq_Hz; | ||
| 56 | new_freq = freq_Hz / 1000; | ||
| 57 | old_freq = clk_get_rate(cpu_clk) / 1000; | ||
| 58 | |||
| 59 | if (!IS_ERR(cpu_reg)) { | ||
| 60 | rcu_read_lock(); | ||
| 61 | opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); | ||
| 62 | if (IS_ERR(opp)) { | ||
| 63 | rcu_read_unlock(); | ||
| 64 | dev_err(cpu_dev, "failed to find OPP for %ld\n", | ||
| 65 | freq_Hz); | ||
| 66 | return PTR_ERR(opp); | ||
| 67 | } | ||
| 68 | volt = dev_pm_opp_get_voltage(opp); | ||
| 69 | rcu_read_unlock(); | ||
| 70 | tol = volt * priv->voltage_tolerance / 100; | ||
| 71 | volt_old = regulator_get_voltage(cpu_reg); | ||
| 72 | } | ||
| 73 | |||
| 74 | dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", | ||
| 75 | old_freq / 1000, volt_old ? volt_old / 1000 : -1, | ||
| 76 | new_freq / 1000, volt ? volt / 1000 : -1); | ||
| 77 | |||
| 78 | /* scaling up? scale voltage before frequency */ | ||
| 79 | if (!IS_ERR(cpu_reg) && new_freq > old_freq) { | ||
| 80 | ret = regulator_set_voltage_tol(cpu_reg, volt, tol); | ||
| 81 | if (ret) { | ||
| 82 | dev_err(cpu_dev, "failed to scale voltage up: %d\n", | ||
| 83 | ret); | ||
| 84 | return ret; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | ret = clk_set_rate(cpu_clk, freq_exact); | ||
| 89 | if (ret) { | ||
| 90 | dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); | ||
| 91 | if (!IS_ERR(cpu_reg)) | ||
| 92 | regulator_set_voltage_tol(cpu_reg, volt_old, tol); | ||
| 93 | return ret; | ||
| 94 | } | ||
| 95 | |||
| 96 | /* scaling down? scale voltage after frequency */ | ||
| 97 | if (!IS_ERR(cpu_reg) && new_freq < old_freq) { | ||
| 98 | ret = regulator_set_voltage_tol(cpu_reg, volt, tol); | ||
| 99 | if (ret) { | ||
| 100 | dev_err(cpu_dev, "failed to scale voltage down: %d\n", | ||
| 101 | ret); | ||
| 102 | clk_set_rate(cpu_clk, old_freq * 1000); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | return ret; | ||
| 107 | } | ||
| 108 | |||
| 109 | static int allocate_resources(int cpu, struct device **cdev, | ||
| 110 | struct regulator **creg, struct clk **cclk) | ||
| 111 | { | ||
| 112 | struct device *cpu_dev; | ||
| 113 | struct regulator *cpu_reg; | ||
| 114 | struct clk *cpu_clk; | ||
| 115 | int ret = 0; | ||
| 116 | char *reg_cpu0 = "cpu0", *reg_cpu = "cpu", *reg; | ||
| 117 | |||
| 118 | cpu_dev = get_cpu_device(cpu); | ||
| 119 | if (!cpu_dev) { | ||
| 120 | pr_err("failed to get cpu%d device\n", cpu); | ||
| 121 | return -ENODEV; | ||
| 122 | } | ||
| 123 | |||
| 124 | /* Try "cpu0" for older DTs */ | ||
| 125 | if (!cpu) | ||
| 126 | reg = reg_cpu0; | ||
| 127 | else | ||
| 128 | reg = reg_cpu; | ||
| 129 | |||
| 130 | try_again: | ||
| 131 | cpu_reg = regulator_get_optional(cpu_dev, reg); | ||
| 132 | if (IS_ERR(cpu_reg)) { | ||
| 133 | /* | ||
| 134 | * If cpu's regulator supply node is present, but regulator is | ||
| 135 | * not yet registered, we should try defering probe. | ||
| 136 | */ | ||
| 137 | if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) { | ||
| 138 | dev_dbg(cpu_dev, "cpu%d regulator not ready, retry\n", | ||
| 139 | cpu); | ||
| 140 | return -EPROBE_DEFER; | ||
| 141 | } | ||
| 142 | |||
| 143 | /* Try with "cpu-supply" */ | ||
| 144 | if (reg == reg_cpu0) { | ||
| 145 | reg = reg_cpu; | ||
| 146 | goto try_again; | ||
| 147 | } | ||
| 148 | |||
| 149 | dev_warn(cpu_dev, "failed to get cpu%d regulator: %ld\n", | ||
| 150 | cpu, PTR_ERR(cpu_reg)); | ||
| 151 | } | ||
| 152 | |||
| 153 | cpu_clk = clk_get(cpu_dev, NULL); | ||
| 154 | if (IS_ERR(cpu_clk)) { | ||
| 155 | /* put regulator */ | ||
| 156 | if (!IS_ERR(cpu_reg)) | ||
| 157 | regulator_put(cpu_reg); | ||
| 158 | |||
| 159 | ret = PTR_ERR(cpu_clk); | ||
| 160 | |||
| 161 | /* | ||
| 162 | * If cpu's clk node is present, but clock is not yet | ||
| 163 | * registered, we should try defering probe. | ||
| 164 | */ | ||
| 165 | if (ret == -EPROBE_DEFER) | ||
| 166 | dev_dbg(cpu_dev, "cpu%d clock not ready, retry\n", cpu); | ||
| 167 | else | ||
| 168 | dev_err(cpu_dev, "failed to get cpu%d clock: %d\n", ret, | ||
| 169 | cpu); | ||
| 170 | } else { | ||
| 171 | *cdev = cpu_dev; | ||
| 172 | *creg = cpu_reg; | ||
| 173 | *cclk = cpu_clk; | ||
| 174 | } | ||
| 175 | |||
| 176 | return ret; | ||
| 177 | } | ||
| 178 | |||
| 179 | static int cpufreq_init(struct cpufreq_policy *policy) | ||
| 180 | { | ||
| 181 | struct cpufreq_frequency_table *freq_table; | ||
| 182 | struct thermal_cooling_device *cdev; | ||
| 183 | struct device_node *np; | ||
| 184 | struct private_data *priv; | ||
| 185 | struct device *cpu_dev; | ||
| 186 | struct regulator *cpu_reg; | ||
| 187 | struct clk *cpu_clk; | ||
| 188 | unsigned int transition_latency; | ||
| 189 | int ret; | ||
| 190 | |||
| 191 | ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk); | ||
| 192 | if (ret) { | ||
| 193 | pr_err("%s: Failed to allocate resources\n: %d", __func__, ret); | ||
| 194 | return ret; | ||
| 195 | } | ||
| 196 | |||
| 197 | np = of_node_get(cpu_dev->of_node); | ||
| 198 | if (!np) { | ||
| 199 | dev_err(cpu_dev, "failed to find cpu%d node\n", policy->cpu); | ||
| 200 | ret = -ENOENT; | ||
| 201 | goto out_put_reg_clk; | ||
| 202 | } | ||
| 203 | |||
| 204 | /* OPPs might be populated at runtime, don't check for error here */ | ||
| 205 | of_init_opp_table(cpu_dev); | ||
| 206 | |||
| 207 | ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); | ||
| 208 | if (ret) { | ||
| 209 | dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); | ||
| 210 | goto out_put_node; | ||
| 211 | } | ||
| 212 | |||
| 213 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
| 214 | if (!priv) { | ||
| 215 | ret = -ENOMEM; | ||
| 216 | goto out_free_table; | ||
| 217 | } | ||
| 218 | |||
| 219 | of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance); | ||
| 220 | |||
| 221 | if (of_property_read_u32(np, "clock-latency", &transition_latency)) | ||
| 222 | transition_latency = CPUFREQ_ETERNAL; | ||
| 223 | |||
| 224 | if (!IS_ERR(cpu_reg)) { | ||
| 225 | struct dev_pm_opp *opp; | ||
| 226 | unsigned long min_uV, max_uV; | ||
| 227 | int i; | ||
| 228 | |||
| 229 | /* | ||
| 230 | * OPP is maintained in order of increasing frequency, and | ||
| 231 | * freq_table initialised from OPP is therefore sorted in the | ||
| 232 | * same order. | ||
| 233 | */ | ||
| 234 | for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) | ||
| 235 | ; | ||
| 236 | rcu_read_lock(); | ||
| 237 | opp = dev_pm_opp_find_freq_exact(cpu_dev, | ||
| 238 | freq_table[0].frequency * 1000, true); | ||
| 239 | min_uV = dev_pm_opp_get_voltage(opp); | ||
| 240 | opp = dev_pm_opp_find_freq_exact(cpu_dev, | ||
| 241 | freq_table[i-1].frequency * 1000, true); | ||
| 242 | max_uV = dev_pm_opp_get_voltage(opp); | ||
| 243 | rcu_read_unlock(); | ||
| 244 | ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV); | ||
| 245 | if (ret > 0) | ||
| 246 | transition_latency += ret * 1000; | ||
| 247 | } | ||
| 248 | |||
| 249 | /* | ||
| 250 | * For now, just loading the cooling device; | ||
| 251 | * thermal DT code takes care of matching them. | ||
| 252 | */ | ||
| 253 | if (of_find_property(np, "#cooling-cells", NULL)) { | ||
| 254 | cdev = of_cpufreq_cooling_register(np, cpu_present_mask); | ||
| 255 | if (IS_ERR(cdev)) | ||
| 256 | dev_err(cpu_dev, | ||
| 257 | "running cpufreq without cooling device: %ld\n", | ||
| 258 | PTR_ERR(cdev)); | ||
| 259 | else | ||
| 260 | priv->cdev = cdev; | ||
| 261 | } | ||
| 262 | |||
| 263 | priv->cpu_dev = cpu_dev; | ||
| 264 | priv->cpu_reg = cpu_reg; | ||
| 265 | policy->driver_data = priv; | ||
| 266 | |||
| 267 | policy->clk = cpu_clk; | ||
| 268 | ret = cpufreq_generic_init(policy, freq_table, transition_latency); | ||
| 269 | if (ret) | ||
| 270 | goto out_cooling_unregister; | ||
| 271 | |||
| 272 | of_node_put(np); | ||
| 273 | |||
| 274 | return 0; | ||
| 275 | |||
| 276 | out_cooling_unregister: | ||
| 277 | cpufreq_cooling_unregister(priv->cdev); | ||
| 278 | kfree(priv); | ||
| 279 | out_free_table: | ||
| 280 | dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); | ||
| 281 | out_put_node: | ||
| 282 | of_node_put(np); | ||
| 283 | out_put_reg_clk: | ||
| 284 | clk_put(cpu_clk); | ||
| 285 | if (!IS_ERR(cpu_reg)) | ||
| 286 | regulator_put(cpu_reg); | ||
| 287 | |||
| 288 | return ret; | ||
| 289 | } | ||
| 290 | |||
| 291 | static int cpufreq_exit(struct cpufreq_policy *policy) | ||
| 292 | { | ||
| 293 | struct private_data *priv = policy->driver_data; | ||
| 294 | |||
| 295 | cpufreq_cooling_unregister(priv->cdev); | ||
| 296 | dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); | ||
| 297 | clk_put(policy->clk); | ||
| 298 | if (!IS_ERR(priv->cpu_reg)) | ||
| 299 | regulator_put(priv->cpu_reg); | ||
| 300 | kfree(priv); | ||
| 301 | |||
| 302 | return 0; | ||
| 303 | } | ||
| 304 | |||
| 305 | static struct cpufreq_driver dt_cpufreq_driver = { | ||
| 306 | .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, | ||
| 307 | .verify = cpufreq_generic_frequency_table_verify, | ||
| 308 | .target_index = set_target, | ||
| 309 | .get = cpufreq_generic_get, | ||
| 310 | .init = cpufreq_init, | ||
| 311 | .exit = cpufreq_exit, | ||
| 312 | .name = "cpufreq-dt", | ||
| 313 | .attr = cpufreq_generic_attr, | ||
| 314 | }; | ||
| 315 | |||
| 316 | static int dt_cpufreq_probe(struct platform_device *pdev) | ||
| 317 | { | ||
| 318 | struct device *cpu_dev; | ||
| 319 | struct regulator *cpu_reg; | ||
| 320 | struct clk *cpu_clk; | ||
| 321 | int ret; | ||
| 322 | |||
| 323 | /* | ||
| 324 | * All per-cluster (CPUs sharing clock/voltages) initialization is done | ||
| 325 | * from ->init(). In probe(), we just need to make sure that clk and | ||
| 326 | * regulators are available. Else defer probe and retry. | ||
| 327 | * | ||
| 328 | * FIXME: Is checking this only for CPU0 sufficient ? | ||
| 329 | */ | ||
| 330 | ret = allocate_resources(0, &cpu_dev, &cpu_reg, &cpu_clk); | ||
| 331 | if (ret) | ||
| 332 | return ret; | ||
| 333 | |||
| 334 | clk_put(cpu_clk); | ||
| 335 | if (!IS_ERR(cpu_reg)) | ||
| 336 | regulator_put(cpu_reg); | ||
| 337 | |||
| 338 | ret = cpufreq_register_driver(&dt_cpufreq_driver); | ||
| 339 | if (ret) | ||
| 340 | dev_err(cpu_dev, "failed register driver: %d\n", ret); | ||
| 341 | |||
| 342 | return ret; | ||
| 343 | } | ||
| 344 | |||
| 345 | static int dt_cpufreq_remove(struct platform_device *pdev) | ||
| 346 | { | ||
| 347 | cpufreq_unregister_driver(&dt_cpufreq_driver); | ||
| 348 | return 0; | ||
| 349 | } | ||
| 350 | |||
| 351 | static struct platform_driver dt_cpufreq_platdrv = { | ||
| 352 | .driver = { | ||
| 353 | .name = "cpufreq-dt", | ||
| 354 | .owner = THIS_MODULE, | ||
| 355 | }, | ||
| 356 | .probe = dt_cpufreq_probe, | ||
| 357 | .remove = dt_cpufreq_remove, | ||
| 358 | }; | ||
| 359 | module_platform_driver(dt_cpufreq_platdrv); | ||
| 360 | |||
| 361 | MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>"); | ||
| 362 | MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); | ||
| 363 | MODULE_DESCRIPTION("Generic cpufreq driver"); | ||
| 364 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 61190f6b4829..24bf76fba141 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c | |||
| @@ -437,7 +437,7 @@ static struct cpufreq_governor *__find_governor(const char *str_governor) | |||
| 437 | struct cpufreq_governor *t; | 437 | struct cpufreq_governor *t; |
| 438 | 438 | ||
| 439 | list_for_each_entry(t, &cpufreq_governor_list, governor_list) | 439 | list_for_each_entry(t, &cpufreq_governor_list, governor_list) |
| 440 | if (!strnicmp(str_governor, t->name, CPUFREQ_NAME_LEN)) | 440 | if (!strncasecmp(str_governor, t->name, CPUFREQ_NAME_LEN)) |
| 441 | return t; | 441 | return t; |
| 442 | 442 | ||
| 443 | return NULL; | 443 | return NULL; |
| @@ -455,10 +455,10 @@ static int cpufreq_parse_governor(char *str_governor, unsigned int *policy, | |||
| 455 | goto out; | 455 | goto out; |
| 456 | 456 | ||
| 457 | if (cpufreq_driver->setpolicy) { | 457 | if (cpufreq_driver->setpolicy) { |
| 458 | if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) { | 458 | if (!strncasecmp(str_governor, "performance", CPUFREQ_NAME_LEN)) { |
| 459 | *policy = CPUFREQ_POLICY_PERFORMANCE; | 459 | *policy = CPUFREQ_POLICY_PERFORMANCE; |
| 460 | err = 0; | 460 | err = 0; |
| 461 | } else if (!strnicmp(str_governor, "powersave", | 461 | } else if (!strncasecmp(str_governor, "powersave", |
| 462 | CPUFREQ_NAME_LEN)) { | 462 | CPUFREQ_NAME_LEN)) { |
| 463 | *policy = CPUFREQ_POLICY_POWERSAVE; | 463 | *policy = CPUFREQ_POLICY_POWERSAVE; |
| 464 | err = 0; | 464 | err = 0; |
| @@ -1382,7 +1382,7 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, | |||
| 1382 | if (!cpufreq_suspended) | 1382 | if (!cpufreq_suspended) |
| 1383 | pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n", | 1383 | pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n", |
| 1384 | __func__, new_cpu, cpu); | 1384 | __func__, new_cpu, cpu); |
| 1385 | } else if (cpufreq_driver->stop_cpu && cpufreq_driver->setpolicy) { | 1385 | } else if (cpufreq_driver->stop_cpu) { |
| 1386 | cpufreq_driver->stop_cpu(policy); | 1386 | cpufreq_driver->stop_cpu(policy); |
| 1387 | } | 1387 | } |
| 1388 | 1388 | ||
diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index 61a54310a1b9..843ec824fd91 100644 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c | |||
| @@ -127,7 +127,7 @@ int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) | |||
| 127 | * dependencies on platform headers. It is necessary to enable | 127 | * dependencies on platform headers. It is necessary to enable |
| 128 | * Exynos multi-platform support and will be removed together with | 128 | * Exynos multi-platform support and will be removed together with |
| 129 | * this whole driver as soon as Exynos gets migrated to use | 129 | * this whole driver as soon as Exynos gets migrated to use |
| 130 | * cpufreq-cpu0 driver. | 130 | * cpufreq-dt driver. |
| 131 | */ | 131 | */ |
| 132 | np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-clock"); | 132 | np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-clock"); |
| 133 | if (!np) { | 133 | if (!np) { |
diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c index 351a2074cfea..9e78a850e29f 100644 --- a/drivers/cpufreq/exynos4x12-cpufreq.c +++ b/drivers/cpufreq/exynos4x12-cpufreq.c | |||
| @@ -174,7 +174,7 @@ int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info) | |||
| 174 | * dependencies on platform headers. It is necessary to enable | 174 | * dependencies on platform headers. It is necessary to enable |
| 175 | * Exynos multi-platform support and will be removed together with | 175 | * Exynos multi-platform support and will be removed together with |
| 176 | * this whole driver as soon as Exynos gets migrated to use | 176 | * this whole driver as soon as Exynos gets migrated to use |
| 177 | * cpufreq-cpu0 driver. | 177 | * cpufreq-dt driver. |
| 178 | */ | 178 | */ |
| 179 | np = of_find_compatible_node(NULL, NULL, "samsung,exynos4412-clock"); | 179 | np = of_find_compatible_node(NULL, NULL, "samsung,exynos4412-clock"); |
| 180 | if (!np) { | 180 | if (!np) { |
diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c index c91ce69dc631..3eafdc7ba787 100644 --- a/drivers/cpufreq/exynos5250-cpufreq.c +++ b/drivers/cpufreq/exynos5250-cpufreq.c | |||
| @@ -153,7 +153,7 @@ int exynos5250_cpufreq_init(struct exynos_dvfs_info *info) | |||
| 153 | * dependencies on platform headers. It is necessary to enable | 153 | * dependencies on platform headers. It is necessary to enable |
| 154 | * Exynos multi-platform support and will be removed together with | 154 | * Exynos multi-platform support and will be removed together with |
| 155 | * this whole driver as soon as Exynos gets migrated to use | 155 | * this whole driver as soon as Exynos gets migrated to use |
| 156 | * cpufreq-cpu0 driver. | 156 | * cpufreq-dt driver. |
| 157 | */ | 157 | */ |
| 158 | np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-clock"); | 158 | np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-clock"); |
| 159 | if (!np) { | 159 | if (!np) { |
diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c index bf8902a0866d..ec399ad2f059 100644 --- a/drivers/cpufreq/highbank-cpufreq.c +++ b/drivers/cpufreq/highbank-cpufreq.c | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
| 7 | * | 7 | * |
| 8 | * This driver provides the clk notifier callbacks that are used when | 8 | * This driver provides the clk notifier callbacks that are used when |
| 9 | * the cpufreq-cpu0 driver changes to frequency to alert the highbank | 9 | * the cpufreq-dt driver changes to frequency to alert the highbank |
| 10 | * EnergyCore Management Engine (ECME) about the need to change | 10 | * EnergyCore Management Engine (ECME) about the need to change |
| 11 | * voltage. The ECME interfaces with the actual voltage regulators. | 11 | * voltage. The ECME interfaces with the actual voltage regulators. |
| 12 | */ | 12 | */ |
| @@ -60,7 +60,7 @@ static struct notifier_block hb_cpufreq_clk_nb = { | |||
| 60 | 60 | ||
| 61 | static int hb_cpufreq_driver_init(void) | 61 | static int hb_cpufreq_driver_init(void) |
| 62 | { | 62 | { |
| 63 | struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; | 63 | struct platform_device_info devinfo = { .name = "cpufreq-dt", }; |
| 64 | struct device *cpu_dev; | 64 | struct device *cpu_dev; |
| 65 | struct clk *cpu_clk; | 65 | struct clk *cpu_clk; |
| 66 | struct device_node *np; | 66 | struct device_node *np; |
| @@ -95,7 +95,7 @@ static int hb_cpufreq_driver_init(void) | |||
| 95 | goto out_put_node; | 95 | goto out_put_node; |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | /* Instantiate cpufreq-cpu0 */ | 98 | /* Instantiate cpufreq-dt */ |
| 99 | platform_device_register_full(&devinfo); | 99 | platform_device_register_full(&devinfo); |
| 100 | 100 | ||
| 101 | out_put_node: | 101 | out_put_node: |
diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c index 379c0837f5a9..2dfd4fdb5a52 100644 --- a/drivers/cpufreq/powernv-cpufreq.c +++ b/drivers/cpufreq/powernv-cpufreq.c | |||
| @@ -26,6 +26,7 @@ | |||
| 26 | #include <linux/cpufreq.h> | 26 | #include <linux/cpufreq.h> |
| 27 | #include <linux/smp.h> | 27 | #include <linux/smp.h> |
| 28 | #include <linux/of.h> | 28 | #include <linux/of.h> |
| 29 | #include <linux/reboot.h> | ||
| 29 | 30 | ||
| 30 | #include <asm/cputhreads.h> | 31 | #include <asm/cputhreads.h> |
| 31 | #include <asm/firmware.h> | 32 | #include <asm/firmware.h> |
| @@ -35,6 +36,7 @@ | |||
| 35 | #define POWERNV_MAX_PSTATES 256 | 36 | #define POWERNV_MAX_PSTATES 256 |
| 36 | 37 | ||
| 37 | static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1]; | 38 | static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1]; |
| 39 | static bool rebooting; | ||
| 38 | 40 | ||
| 39 | /* | 41 | /* |
| 40 | * Note: The set of pstates consists of contiguous integers, the | 42 | * Note: The set of pstates consists of contiguous integers, the |
| @@ -284,6 +286,15 @@ static void set_pstate(void *freq_data) | |||
| 284 | } | 286 | } |
| 285 | 287 | ||
| 286 | /* | 288 | /* |
| 289 | * get_nominal_index: Returns the index corresponding to the nominal | ||
| 290 | * pstate in the cpufreq table | ||
| 291 | */ | ||
| 292 | static inline unsigned int get_nominal_index(void) | ||
| 293 | { | ||
| 294 | return powernv_pstate_info.max - powernv_pstate_info.nominal; | ||
| 295 | } | ||
| 296 | |||
| 297 | /* | ||
| 287 | * powernv_cpufreq_target_index: Sets the frequency corresponding to | 298 | * powernv_cpufreq_target_index: Sets the frequency corresponding to |
| 288 | * the cpufreq table entry indexed by new_index on the cpus in the | 299 | * the cpufreq table entry indexed by new_index on the cpus in the |
| 289 | * mask policy->cpus | 300 | * mask policy->cpus |
| @@ -293,6 +304,9 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy, | |||
| 293 | { | 304 | { |
| 294 | struct powernv_smp_call_data freq_data; | 305 | struct powernv_smp_call_data freq_data; |
| 295 | 306 | ||
| 307 | if (unlikely(rebooting) && new_index != get_nominal_index()) | ||
| 308 | return 0; | ||
| 309 | |||
| 296 | freq_data.pstate_id = powernv_freqs[new_index].driver_data; | 310 | freq_data.pstate_id = powernv_freqs[new_index].driver_data; |
| 297 | 311 | ||
| 298 | /* | 312 | /* |
| @@ -317,6 +331,33 @@ static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy) | |||
| 317 | return cpufreq_table_validate_and_show(policy, powernv_freqs); | 331 | return cpufreq_table_validate_and_show(policy, powernv_freqs); |
| 318 | } | 332 | } |
| 319 | 333 | ||
| 334 | static int powernv_cpufreq_reboot_notifier(struct notifier_block *nb, | ||
| 335 | unsigned long action, void *unused) | ||
| 336 | { | ||
| 337 | int cpu; | ||
| 338 | struct cpufreq_policy cpu_policy; | ||
| 339 | |||
| 340 | rebooting = true; | ||
| 341 | for_each_online_cpu(cpu) { | ||
| 342 | cpufreq_get_policy(&cpu_policy, cpu); | ||
| 343 | powernv_cpufreq_target_index(&cpu_policy, get_nominal_index()); | ||
| 344 | } | ||
| 345 | |||
| 346 | return NOTIFY_DONE; | ||
| 347 | } | ||
| 348 | |||
| 349 | static struct notifier_block powernv_cpufreq_reboot_nb = { | ||
| 350 | .notifier_call = powernv_cpufreq_reboot_notifier, | ||
| 351 | }; | ||
| 352 | |||
| 353 | static void powernv_cpufreq_stop_cpu(struct cpufreq_policy *policy) | ||
| 354 | { | ||
| 355 | struct powernv_smp_call_data freq_data; | ||
| 356 | |||
| 357 | freq_data.pstate_id = powernv_pstate_info.min; | ||
| 358 | smp_call_function_single(policy->cpu, set_pstate, &freq_data, 1); | ||
| 359 | } | ||
| 360 | |||
| 320 | static struct cpufreq_driver powernv_cpufreq_driver = { | 361 | static struct cpufreq_driver powernv_cpufreq_driver = { |
| 321 | .name = "powernv-cpufreq", | 362 | .name = "powernv-cpufreq", |
| 322 | .flags = CPUFREQ_CONST_LOOPS, | 363 | .flags = CPUFREQ_CONST_LOOPS, |
| @@ -324,6 +365,7 @@ static struct cpufreq_driver powernv_cpufreq_driver = { | |||
| 324 | .verify = cpufreq_generic_frequency_table_verify, | 365 | .verify = cpufreq_generic_frequency_table_verify, |
| 325 | .target_index = powernv_cpufreq_target_index, | 366 | .target_index = powernv_cpufreq_target_index, |
| 326 | .get = powernv_cpufreq_get, | 367 | .get = powernv_cpufreq_get, |
| 368 | .stop_cpu = powernv_cpufreq_stop_cpu, | ||
| 327 | .attr = powernv_cpu_freq_attr, | 369 | .attr = powernv_cpu_freq_attr, |
| 328 | }; | 370 | }; |
| 329 | 371 | ||
| @@ -342,12 +384,14 @@ static int __init powernv_cpufreq_init(void) | |||
| 342 | return rc; | 384 | return rc; |
| 343 | } | 385 | } |
| 344 | 386 | ||
| 387 | register_reboot_notifier(&powernv_cpufreq_reboot_nb); | ||
| 345 | return cpufreq_register_driver(&powernv_cpufreq_driver); | 388 | return cpufreq_register_driver(&powernv_cpufreq_driver); |
| 346 | } | 389 | } |
| 347 | module_init(powernv_cpufreq_init); | 390 | module_init(powernv_cpufreq_init); |
| 348 | 391 | ||
| 349 | static void __exit powernv_cpufreq_exit(void) | 392 | static void __exit powernv_cpufreq_exit(void) |
| 350 | { | 393 | { |
| 394 | unregister_reboot_notifier(&powernv_cpufreq_reboot_nb); | ||
| 351 | cpufreq_unregister_driver(&powernv_cpufreq_driver); | 395 | cpufreq_unregister_driver(&powernv_cpufreq_driver); |
| 352 | } | 396 | } |
| 353 | module_exit(powernv_cpufreq_exit); | 397 | module_exit(powernv_cpufreq_exit); |
diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c index 3607070797af..bee5df7794d3 100644 --- a/drivers/cpufreq/ppc-corenet-cpufreq.c +++ b/drivers/cpufreq/ppc-corenet-cpufreq.c | |||
| @@ -199,7 +199,6 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy) | |||
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | data->table = table; | 201 | data->table = table; |
| 202 | per_cpu(cpu_data, cpu) = data; | ||
| 203 | 202 | ||
| 204 | /* update ->cpus if we have cluster, no harm if not */ | 203 | /* update ->cpus if we have cluster, no harm if not */ |
| 205 | cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu)); | 204 | cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu)); |
diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index 3f9791f07b8e..567caa6313ff 100644 --- a/drivers/cpufreq/s5pv210-cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c | |||
| @@ -597,7 +597,7 @@ static int s5pv210_cpufreq_probe(struct platform_device *pdev) | |||
| 597 | * and dependencies on platform headers. It is necessary to enable | 597 | * and dependencies on platform headers. It is necessary to enable |
| 598 | * S5PV210 multi-platform support and will be removed together with | 598 | * S5PV210 multi-platform support and will be removed together with |
| 599 | * this whole driver as soon as S5PV210 gets migrated to use | 599 | * this whole driver as soon as S5PV210 gets migrated to use |
| 600 | * cpufreq-cpu0 driver. | 600 | * cpufreq-dt driver. |
| 601 | */ | 601 | */ |
| 602 | np = of_find_compatible_node(NULL, NULL, "samsung,s5pv210-clock"); | 602 | np = of_find_compatible_node(NULL, NULL, "samsung,s5pv210-clock"); |
| 603 | if (!np) { | 603 | if (!np) { |
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 7d1955afa62c..138336b6bb04 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h | |||
| @@ -112,6 +112,9 @@ struct cpufreq_policy { | |||
| 112 | spinlock_t transition_lock; | 112 | spinlock_t transition_lock; |
| 113 | wait_queue_head_t transition_wait; | 113 | wait_queue_head_t transition_wait; |
| 114 | struct task_struct *transition_task; /* Task which is doing the transition */ | 114 | struct task_struct *transition_task; /* Task which is doing the transition */ |
| 115 | |||
| 116 | /* For cpufreq driver's internal use */ | ||
| 117 | void *driver_data; | ||
| 115 | }; | 118 | }; |
| 116 | 119 | ||
| 117 | /* Only for ACPI */ | 120 | /* Only for ACPI */ |
