* [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
@ 2011-12-15 11:16 ` Richard Zhao
0 siblings, 0 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-15 11:16 UTC (permalink / raw)
To: linux-arm-kernel
It support single core and multi-core ARM SoCs. But it assume
all cores share the same frequency and voltage.
Signed-off-by: Richard Zhao <richard.zhao@linaro.org>
---
drivers/cpufreq/Kconfig.arm | 8 ++
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/arm-cpufreq.c | 260 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 269 insertions(+), 0 deletions(-)
create mode 100644 drivers/cpufreq/arm-cpufreq.c
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 72a0044..a0f7d35 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -2,6 +2,14 @@
# ARM CPU Frequency scaling drivers
#
+config ARM_GENERIC_CPUFREQ
+ bool "ARM generic"
+ help
+ This adds ARM generic CPUFreq driver. It assumes all
+ cores of the SoC share the same clock and voltage.
+
+ If in doubt, say N.
+
config ARM_S3C64XX_CPUFREQ
bool "Samsung S3C64XX"
depends on CPU_S3C6410
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index ce75fcb..6ccf02d 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o
##################################################################################
# ARM SoC drivers
+obj-$(CONFIG_ARM_GENERIC_CPUFREQ) += arm-cpufreq.o
obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o
obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
diff --git a/drivers/cpufreq/arm-cpufreq.c b/drivers/cpufreq/arm-cpufreq.c
new file mode 100644
index 0000000..fd9759f
--- /dev/null
+++ b/drivers/cpufreq/arm-cpufreq.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/cpufreq.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <asm/cpu.h>
+#include <mach/hardware.h>
+#include <mach/clock.h>
+
+static u32 *cpu_freqs;
+static u32 *cpu_volts;
+static u32 trans_latency;
+static int cpu_op_nr;
+
+static int cpu_freq_khz_min;
+static int cpu_freq_khz_max;
+
+static struct clk *cpu_clk;
+static struct cpufreq_frequency_table *arm_freq_table;
+
+static int set_cpu_freq(int freq)
+{
+ int ret = 0;
+ int org_cpu_rate;
+
+ org_cpu_rate = clk_get_rate(cpu_clk);
+ if (org_cpu_rate == freq)
+ return ret;
+
+ ret = clk_set_rate(cpu_clk, freq);
+ if (ret != 0) {
+ printk(KERN_DEBUG "cannot set CPU clock rate\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int arm_verify_speed(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, arm_freq_table);
+}
+
+static unsigned int arm_get_speed(unsigned int cpu)
+{
+ return clk_get_rate(cpu_clk) / 1000;
+}
+
+static int arm_set_target(struct cpufreq_policy *policy,
+ unsigned int target_freq, unsigned int relation)
+{
+ struct cpufreq_freqs freqs;
+ int freq_Hz, cpu;
+ int ret = 0;
+ unsigned int index;
+
+ cpufreq_frequency_table_target(policy, arm_freq_table,
+ target_freq, relation, &index);
+ freq_Hz = arm_freq_table[index].frequency * 1000;
+
+ freqs.old = clk_get_rate(cpu_clk) / 1000;
+ freqs.new = clk_round_rate(cpu_clk, freq_Hz);
+ freqs.new = (freqs.new ? freqs.new : freq_Hz) / 1000;
+ freqs.flags = 0;
+
+ if (freqs.old == freqs.new)
+ return 0;
+
+ for_each_possible_cpu(cpu) {
+ freqs.cpu = cpu;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ }
+
+ ret = set_cpu_freq(freq_Hz);
+
+#ifdef CONFIG_SMP
+ /* loops_per_jiffy is not updated by the cpufreq core for SMP systems.
+ * So update it for all CPUs.
+ */
+ for_each_possible_cpu(cpu)
+ per_cpu(cpu_data, cpu).loops_per_jiffy =
+ cpufreq_scale(per_cpu(cpu_data, cpu).loops_per_jiffy,
+ freqs.old, freqs.new);
+#endif
+
+ for_each_possible_cpu(cpu) {
+ freqs.cpu = cpu;
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+
+ return ret;
+}
+
+static int arm_cpufreq_init(struct cpufreq_policy *policy)
+{
+ int ret;
+
+ printk(KERN_INFO "ARM CPU frequency driver\n");
+
+ if (policy->cpu >= num_possible_cpus())
+ return -EINVAL;
+
+ policy->cur = clk_get_rate(cpu_clk) / 1000;
+ policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min;
+ policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max;
+ policy->shared_type = CPUFREQ_SHARED_TYPE_ANY;
+ cpumask_setall(policy->cpus);
+ /* Manual states, that PLL stabilizes in two CLK32 periods */
+ policy->cpuinfo.transition_latency = trans_latency;
+
+ ret = cpufreq_frequency_table_cpuinfo(policy, arm_freq_table);
+
+ if (ret < 0) {
+ printk(KERN_ERR "%s: failed to register i.MXC CPUfreq with error code %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ cpufreq_frequency_table_get_attr(arm_freq_table, policy->cpu);
+ return 0;
+}
+
+static int arm_cpufreq_exit(struct cpufreq_policy *policy)
+{
+ cpufreq_frequency_table_put_attr(policy->cpu);
+
+ set_cpu_freq(cpu_freq_khz_max * 1000);
+ return 0;
+}
+
+static struct cpufreq_driver arm_cpufreq_driver = {
+ .flags = CPUFREQ_STICKY,
+ .verify = arm_verify_speed,
+ .target = arm_set_target,
+ .get = arm_get_speed,
+ .init = arm_cpufreq_init,
+ .exit = arm_cpufreq_exit,
+ .name = "arm",
+};
+
+static int __devinit arm_cpufreq_driver_init(void)
+{
+ struct device_node *cpu0;
+ const struct property *pp;
+ int i, ret;
+
+ cpu0 = of_find_node_by_path("/cpus/cpu at 0");
+ if (!cpu0)
+ return -ENODEV;
+
+ pp = of_find_property(cpu0, "cpu_freqs", NULL);
+ if (!pp) {
+ ret = -ENODEV;
+ goto put_node;
+ }
+ cpu_op_nr = pp->length / sizeof(u32);
+ if (!cpu_op_nr) {
+ ret = -ENODEV;
+ goto put_node;
+ }
+ ret = -ENOMEM;
+ cpu_freqs = kzalloc(sizeof(*cpu_freqs) * cpu_op_nr, GFP_KERNEL);
+ if (!cpu_freqs)
+ goto put_node;
+ of_property_read_u32_array(cpu0, "cpu_freqs", cpu_freqs, cpu_op_nr);
+
+ pp = of_find_property(cpu0, "cpu_volts", NULL);
+ if (pp) {
+ if (cpu_op_nr == pp->length / sizeof(u32)) {
+ cpu_volts = kzalloc(sizeof(*cpu_freqs) * cpu_op_nr,
+ GFP_KERNEL);
+ if (!cpu_volts)
+ goto free_cpu_freqs;
+ of_property_read_u32_array(cpu0, "cpu_volts",
+ cpu_freqs, cpu_op_nr);
+ } else
+ printk(KERN_WARNING "cpufreq: invalid cpu_volts!\n");
+ }
+
+ if (of_property_read_u32(cpu0, "trans_latency", &trans_latency))
+ trans_latency = CPUFREQ_ETERNAL;
+
+ cpu_freq_khz_min = cpu_freqs[0] / 1000;
+ cpu_freq_khz_max = cpu_freqs[0] / 1000;
+
+ arm_freq_table = kmalloc(sizeof(struct cpufreq_frequency_table)
+ * (cpu_op_nr + 1), GFP_KERNEL);
+ if (!arm_freq_table)
+ goto free_cpu_volts;
+
+ for (i = 0; i < cpu_op_nr; i++) {
+ arm_freq_table[i].index = i;
+ arm_freq_table[i].frequency = cpu_freqs[i] / 1000;
+ if ((cpu_freqs[i] / 1000) < cpu_freq_khz_min)
+ cpu_freq_khz_min = cpu_freqs[i] / 1000;
+ if ((cpu_freqs[i] / 1000) > cpu_freq_khz_max)
+ cpu_freq_khz_max = cpu_freqs[i] / 1000;
+ }
+
+ arm_freq_table[i].index = i;
+ arm_freq_table[i].frequency = CPUFREQ_TABLE_END;
+
+ cpu_clk = clk_get(NULL, "cpu");
+ if (IS_ERR(cpu_clk)) {
+ printk(KERN_ERR "%s: failed to get cpu clock\n", __func__);
+ ret = PTR_ERR(cpu_clk);
+ goto free_freq_table;
+ }
+
+ ret = cpufreq_register_driver(&arm_cpufreq_driver);
+ if (ret)
+ goto clock_put;
+
+ of_node_put(cpu0);
+
+ return 0;
+
+clock_put:
+ clk_put(cpu_clk);
+free_freq_table:
+ kfree(arm_freq_table);
+free_cpu_volts:
+ kfree(cpu_volts);
+free_cpu_freqs:
+ kfree(cpu_freqs);
+put_node:
+ of_node_put(cpu0);
+
+ return ret;
+}
+
+static void arm_cpufreq_driver_exit(void)
+{
+ cpufreq_unregister_driver(&arm_cpufreq_driver);
+ kfree(cpu_freqs);
+ kfree(cpu_volts);
+ kfree(arm_freq_table);
+ clk_put(cpu_clk);
+}
+
+module_init(arm_cpufreq_driver_init);
+module_exit(arm_cpufreq_driver_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor Inc. Richard Zhao <richard.zhao@freescale.com>");
+MODULE_DESCRIPTION("ARM SoC generic CPUFreq driver");
+MODULE_LICENSE("GPL");
--
1.7.5.4
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
@ 2011-12-15 11:16 ` Richard Zhao
0 siblings, 0 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-15 11:16 UTC (permalink / raw)
To: linux-arm-kernel, cpufreq
Cc: linux, davej, kernel, shawn.guo, eric.miao, linaro-dev, patches,
Richard Zhao
It support single core and multi-core ARM SoCs. But it assume
all cores share the same frequency and voltage.
Signed-off-by: Richard Zhao <richard.zhao@linaro.org>
---
drivers/cpufreq/Kconfig.arm | 8 ++
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/arm-cpufreq.c | 260 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 269 insertions(+), 0 deletions(-)
create mode 100644 drivers/cpufreq/arm-cpufreq.c
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 72a0044..a0f7d35 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -2,6 +2,14 @@
# ARM CPU Frequency scaling drivers
#
+config ARM_GENERIC_CPUFREQ
+ bool "ARM generic"
+ help
+ This adds ARM generic CPUFreq driver. It assumes all
+ cores of the SoC share the same clock and voltage.
+
+ If in doubt, say N.
+
config ARM_S3C64XX_CPUFREQ
bool "Samsung S3C64XX"
depends on CPU_S3C6410
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index ce75fcb..6ccf02d 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o
##################################################################################
# ARM SoC drivers
+obj-$(CONFIG_ARM_GENERIC_CPUFREQ) += arm-cpufreq.o
obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o
obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
diff --git a/drivers/cpufreq/arm-cpufreq.c b/drivers/cpufreq/arm-cpufreq.c
new file mode 100644
index 0000000..fd9759f
--- /dev/null
+++ b/drivers/cpufreq/arm-cpufreq.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/cpufreq.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <asm/cpu.h>
+#include <mach/hardware.h>
+#include <mach/clock.h>
+
+static u32 *cpu_freqs;
+static u32 *cpu_volts;
+static u32 trans_latency;
+static int cpu_op_nr;
+
+static int cpu_freq_khz_min;
+static int cpu_freq_khz_max;
+
+static struct clk *cpu_clk;
+static struct cpufreq_frequency_table *arm_freq_table;
+
+static int set_cpu_freq(int freq)
+{
+ int ret = 0;
+ int org_cpu_rate;
+
+ org_cpu_rate = clk_get_rate(cpu_clk);
+ if (org_cpu_rate == freq)
+ return ret;
+
+ ret = clk_set_rate(cpu_clk, freq);
+ if (ret != 0) {
+ printk(KERN_DEBUG "cannot set CPU clock rate\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int arm_verify_speed(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, arm_freq_table);
+}
+
+static unsigned int arm_get_speed(unsigned int cpu)
+{
+ return clk_get_rate(cpu_clk) / 1000;
+}
+
+static int arm_set_target(struct cpufreq_policy *policy,
+ unsigned int target_freq, unsigned int relation)
+{
+ struct cpufreq_freqs freqs;
+ int freq_Hz, cpu;
+ int ret = 0;
+ unsigned int index;
+
+ cpufreq_frequency_table_target(policy, arm_freq_table,
+ target_freq, relation, &index);
+ freq_Hz = arm_freq_table[index].frequency * 1000;
+
+ freqs.old = clk_get_rate(cpu_clk) / 1000;
+ freqs.new = clk_round_rate(cpu_clk, freq_Hz);
+ freqs.new = (freqs.new ? freqs.new : freq_Hz) / 1000;
+ freqs.flags = 0;
+
+ if (freqs.old == freqs.new)
+ return 0;
+
+ for_each_possible_cpu(cpu) {
+ freqs.cpu = cpu;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ }
+
+ ret = set_cpu_freq(freq_Hz);
+
+#ifdef CONFIG_SMP
+ /* loops_per_jiffy is not updated by the cpufreq core for SMP systems.
+ * So update it for all CPUs.
+ */
+ for_each_possible_cpu(cpu)
+ per_cpu(cpu_data, cpu).loops_per_jiffy =
+ cpufreq_scale(per_cpu(cpu_data, cpu).loops_per_jiffy,
+ freqs.old, freqs.new);
+#endif
+
+ for_each_possible_cpu(cpu) {
+ freqs.cpu = cpu;
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+
+ return ret;
+}
+
+static int arm_cpufreq_init(struct cpufreq_policy *policy)
+{
+ int ret;
+
+ printk(KERN_INFO "ARM CPU frequency driver\n");
+
+ if (policy->cpu >= num_possible_cpus())
+ return -EINVAL;
+
+ policy->cur = clk_get_rate(cpu_clk) / 1000;
+ policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min;
+ policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max;
+ policy->shared_type = CPUFREQ_SHARED_TYPE_ANY;
+ cpumask_setall(policy->cpus);
+ /* Manual states, that PLL stabilizes in two CLK32 periods */
+ policy->cpuinfo.transition_latency = trans_latency;
+
+ ret = cpufreq_frequency_table_cpuinfo(policy, arm_freq_table);
+
+ if (ret < 0) {
+ printk(KERN_ERR "%s: failed to register i.MXC CPUfreq with error code %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ cpufreq_frequency_table_get_attr(arm_freq_table, policy->cpu);
+ return 0;
+}
+
+static int arm_cpufreq_exit(struct cpufreq_policy *policy)
+{
+ cpufreq_frequency_table_put_attr(policy->cpu);
+
+ set_cpu_freq(cpu_freq_khz_max * 1000);
+ return 0;
+}
+
+static struct cpufreq_driver arm_cpufreq_driver = {
+ .flags = CPUFREQ_STICKY,
+ .verify = arm_verify_speed,
+ .target = arm_set_target,
+ .get = arm_get_speed,
+ .init = arm_cpufreq_init,
+ .exit = arm_cpufreq_exit,
+ .name = "arm",
+};
+
+static int __devinit arm_cpufreq_driver_init(void)
+{
+ struct device_node *cpu0;
+ const struct property *pp;
+ int i, ret;
+
+ cpu0 = of_find_node_by_path("/cpus/cpu@0");
+ if (!cpu0)
+ return -ENODEV;
+
+ pp = of_find_property(cpu0, "cpu_freqs", NULL);
+ if (!pp) {
+ ret = -ENODEV;
+ goto put_node;
+ }
+ cpu_op_nr = pp->length / sizeof(u32);
+ if (!cpu_op_nr) {
+ ret = -ENODEV;
+ goto put_node;
+ }
+ ret = -ENOMEM;
+ cpu_freqs = kzalloc(sizeof(*cpu_freqs) * cpu_op_nr, GFP_KERNEL);
+ if (!cpu_freqs)
+ goto put_node;
+ of_property_read_u32_array(cpu0, "cpu_freqs", cpu_freqs, cpu_op_nr);
+
+ pp = of_find_property(cpu0, "cpu_volts", NULL);
+ if (pp) {
+ if (cpu_op_nr == pp->length / sizeof(u32)) {
+ cpu_volts = kzalloc(sizeof(*cpu_freqs) * cpu_op_nr,
+ GFP_KERNEL);
+ if (!cpu_volts)
+ goto free_cpu_freqs;
+ of_property_read_u32_array(cpu0, "cpu_volts",
+ cpu_freqs, cpu_op_nr);
+ } else
+ printk(KERN_WARNING "cpufreq: invalid cpu_volts!\n");
+ }
+
+ if (of_property_read_u32(cpu0, "trans_latency", &trans_latency))
+ trans_latency = CPUFREQ_ETERNAL;
+
+ cpu_freq_khz_min = cpu_freqs[0] / 1000;
+ cpu_freq_khz_max = cpu_freqs[0] / 1000;
+
+ arm_freq_table = kmalloc(sizeof(struct cpufreq_frequency_table)
+ * (cpu_op_nr + 1), GFP_KERNEL);
+ if (!arm_freq_table)
+ goto free_cpu_volts;
+
+ for (i = 0; i < cpu_op_nr; i++) {
+ arm_freq_table[i].index = i;
+ arm_freq_table[i].frequency = cpu_freqs[i] / 1000;
+ if ((cpu_freqs[i] / 1000) < cpu_freq_khz_min)
+ cpu_freq_khz_min = cpu_freqs[i] / 1000;
+ if ((cpu_freqs[i] / 1000) > cpu_freq_khz_max)
+ cpu_freq_khz_max = cpu_freqs[i] / 1000;
+ }
+
+ arm_freq_table[i].index = i;
+ arm_freq_table[i].frequency = CPUFREQ_TABLE_END;
+
+ cpu_clk = clk_get(NULL, "cpu");
+ if (IS_ERR(cpu_clk)) {
+ printk(KERN_ERR "%s: failed to get cpu clock\n", __func__);
+ ret = PTR_ERR(cpu_clk);
+ goto free_freq_table;
+ }
+
+ ret = cpufreq_register_driver(&arm_cpufreq_driver);
+ if (ret)
+ goto clock_put;
+
+ of_node_put(cpu0);
+
+ return 0;
+
+clock_put:
+ clk_put(cpu_clk);
+free_freq_table:
+ kfree(arm_freq_table);
+free_cpu_volts:
+ kfree(cpu_volts);
+free_cpu_freqs:
+ kfree(cpu_freqs);
+put_node:
+ of_node_put(cpu0);
+
+ return ret;
+}
+
+static void arm_cpufreq_driver_exit(void)
+{
+ cpufreq_unregister_driver(&arm_cpufreq_driver);
+ kfree(cpu_freqs);
+ kfree(cpu_volts);
+ kfree(arm_freq_table);
+ clk_put(cpu_clk);
+}
+
+module_init(arm_cpufreq_driver_init);
+module_exit(arm_cpufreq_driver_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor Inc. Richard Zhao <richard.zhao@freescale.com>");
+MODULE_DESCRIPTION("ARM SoC generic CPUFreq driver");
+MODULE_LICENSE("GPL");
--
1.7.5.4
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [RFC V1 2/4] dts/imx6q: add cpufreq property
2011-12-15 11:16 ` Richard Zhao
(?)
@ 2011-12-15 11:16 ` Richard Zhao
2011-12-15 11:58 ` Shawn Guo
2011-12-15 18:52 ` Mark Langsdorf
-1 siblings, 2 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-15 11:16 UTC (permalink / raw)
To: linux-arm-kernel, cpufreq
Cc: linux, davej, kernel, shawn.guo, eric.miao, linaro-dev, patches,
Richard Zhao
Signed-off-by: Richard Zhao <richard.zhao@linaro.org>
---
arch/arm/boot/dts/imx6q.dtsi | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index 263e8f3..9e9943b 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -29,6 +29,8 @@
compatible = "arm,cortex-a9";
reg = <0>;
next-level-cache = <&L2>;
+ cpu_freqs = <996000000 792000000 396000000 198000000>;
+ cpu_volts = <1225000 1100000 950000 850000>;
};
cpu@1 {
--
1.7.5.4
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [RFC V1 3/4] arm/imx6q: register arm_clk as cpu to clkdev
2011-12-15 11:16 ` Richard Zhao
(?)
(?)
@ 2011-12-15 11:16 ` Richard Zhao
-1 siblings, 0 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-15 11:16 UTC (permalink / raw)
To: linux-arm-kernel, cpufreq
Cc: linux, davej, kernel, shawn.guo, eric.miao, linaro-dev, patches,
Richard Zhao
cpufreq needs cpu clock to change frequency.
Signed-off-by: Richard Zhao <richard.zhao@linaro.org>
---
arch/arm/mach-imx/clock-imx6q.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-imx/clock-imx6q.c b/arch/arm/mach-imx/clock-imx6q.c
index 039a7ab..72acbc2 100644
--- a/arch/arm/mach-imx/clock-imx6q.c
+++ b/arch/arm/mach-imx/clock-imx6q.c
@@ -1911,6 +1911,7 @@ static struct clk_lookup lookups[] = {
_REGISTER_CLOCK(NULL, "gpmi_io_clk", gpmi_io_clk),
_REGISTER_CLOCK(NULL, "usboh3_clk", usboh3_clk),
_REGISTER_CLOCK(NULL, "sata_clk", sata_clk),
+ _REGISTER_CLOCK(NULL, "cpu", arm_clk),
};
int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
--
1.7.5.4
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [RFC V1 4/4] arm/imx6q: select ARCH_HAS_CPUFREQ
2011-12-15 11:16 ` Richard Zhao
` (2 preceding siblings ...)
(?)
@ 2011-12-15 11:16 ` Richard Zhao
-1 siblings, 0 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-15 11:16 UTC (permalink / raw)
To: linux-arm-kernel, cpufreq
Cc: linux, davej, kernel, shawn.guo, eric.miao, linaro-dev, patches,
Richard Zhao
Signed-off-by: Richard Zhao <richard.zhao@linaro.org>
---
arch/arm/mach-imx/Kconfig | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index c44aa97..39cf00a 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -595,6 +595,7 @@ comment "i.MX6 family:"
config SOC_IMX6Q
bool "i.MX6 Quad support"
+ select ARCH_HAS_CPUFREQ
select ARM_GIC
select CACHE_L2X0
select CPU_V7
--
1.7.5.4
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
2011-12-15 11:16 ` Richard Zhao
@ 2011-12-15 11:19 ` Richard Zhao
-1 siblings, 0 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-15 11:19 UTC (permalink / raw)
To: linux-arm-kernel
TODO:
- add voltage change.
Thanks
Richard
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
@ 2011-12-15 11:19 ` Richard Zhao
0 siblings, 0 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-15 11:19 UTC (permalink / raw)
To: Richard Zhao
Cc: linux-arm-kernel, cpufreq, linaro-dev, patches, eric.miao, linux,
kernel, davej, shawn.guo
TODO:
- add voltage change.
Thanks
Richard
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [RFC V1 2/4] dts/imx6q: add cpufreq property
2011-12-15 11:16 ` [RFC V1 2/4] dts/imx6q: add cpufreq property Richard Zhao
@ 2011-12-15 11:58 ` Shawn Guo
2011-12-15 18:52 ` Mark Langsdorf
1 sibling, 0 replies; 25+ messages in thread
From: Shawn Guo @ 2011-12-15 11:58 UTC (permalink / raw)
To: Richard Zhao
Cc: linux-arm-kernel, cpufreq, linaro-dev, patches, eric.miao, linux,
kernel, davej, devicetree-discuss
Hi Richard,
Whenever we invent some new device tree binding support, we need to
Cc devicetree-discuss@lists.ozlabs.org (Cc-ed).
On Thu, Dec 15, 2011 at 07:16:36PM +0800, Richard Zhao wrote:
> Signed-off-by: Richard Zhao <richard.zhao@linaro.org>
> ---
> arch/arm/boot/dts/imx6q.dtsi | 2 ++
> 1 files changed, 2 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
> index 263e8f3..9e9943b 100644
> --- a/arch/arm/boot/dts/imx6q.dtsi
> +++ b/arch/arm/boot/dts/imx6q.dtsi
> @@ -29,6 +29,8 @@
> compatible = "arm,cortex-a9";
> reg = <0>;
> next-level-cache = <&L2>;
> + cpu_freqs = <996000000 792000000 396000000 198000000>;
> + cpu_volts = <1225000 1100000 950000 850000>;
For dt property name, we use '-' rather than '_'.
And I'm not sure this is correct, since these data obviously does not
belong to "arm,cortex-a9".
Regards,
Shawn
> };
>
> cpu@1 {
> --
> 1.7.5.4
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC V1 2/4] dts/imx6q: add cpufreq property
@ 2011-12-15 11:58 ` Shawn Guo
0 siblings, 0 replies; 25+ messages in thread
From: Shawn Guo @ 2011-12-15 11:58 UTC (permalink / raw)
To: linux-arm-kernel
Hi Richard,
Whenever we invent some new device tree binding support, we need to
Cc devicetree-discuss at lists.ozlabs.org (Cc-ed).
On Thu, Dec 15, 2011 at 07:16:36PM +0800, Richard Zhao wrote:
> Signed-off-by: Richard Zhao <richard.zhao@linaro.org>
> ---
> arch/arm/boot/dts/imx6q.dtsi | 2 ++
> 1 files changed, 2 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
> index 263e8f3..9e9943b 100644
> --- a/arch/arm/boot/dts/imx6q.dtsi
> +++ b/arch/arm/boot/dts/imx6q.dtsi
> @@ -29,6 +29,8 @@
> compatible = "arm,cortex-a9";
> reg = <0>;
> next-level-cache = <&L2>;
> + cpu_freqs = <996000000 792000000 396000000 198000000>;
> + cpu_volts = <1225000 1100000 950000 850000>;
For dt property name, we use '-' rather than '_'.
And I'm not sure this is correct, since these data obviously does not
belong to "arm,cortex-a9".
Regards,
Shawn
> };
>
> cpu at 1 {
> --
> 1.7.5.4
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
2011-12-15 11:16 ` Richard Zhao
@ 2011-12-15 18:50 ` Mark Langsdorf
-1 siblings, 0 replies; 25+ messages in thread
From: Mark Langsdorf @ 2011-12-15 18:50 UTC (permalink / raw)
To: linux-arm-kernel
Comments below. I tested this on the Calxeda Highbank SoC using
QEMU. I found one definite error and a few things I would change.
On 12/15/2011 05:16 AM, Richard Zhao wrote:
> It support single core and multi-core ARM SoCs. But it assume
> all cores share the same frequency and voltage.
>
> Signed-off-by: Richard Zhao<richard.zhao@linaro.org>
> ---
> diff --git a/drivers/cpufreq/arm-cpufreq.c b/drivers/cpufreq/arm-cpufreq.c
> new file mode 100644
> index 0000000..fd9759f
> --- /dev/null
> +++ b/drivers/cpufreq/arm-cpufreq.c
> @@ -0,0 +1,260 @@
> +/*
> + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
> + */
> +
> +/*
> + * The code contained herein is licensed under the GNU General Public
> + * License. You may obtain a copy of the GNU General Public License
> + * Version 2 or later at the following locations:
> + *
> + * http://www.opensource.org/licenses/gpl-license.html
> + * http://www.gnu.org/copyleft/gpl.html
> + */
> +
> +#include<linux/module.h>
> +#include<linux/cpufreq.h>
> +#include<linux/clk.h>
> +#include<linux/err.h>
> +#include<linux/slab.h>
> +#include<linux/of.h>
> +#include<asm/cpu.h>
> +#include<mach/hardware.h>
> +#include<mach/clock.h>
These should probably be replaced by <linux/of_clk.h>. See
below for details.
> +
> +static u32 *cpu_freqs;
> +static u32 *cpu_volts;
> +static u32 trans_latency;
> +static int cpu_op_nr;
> +
> +static int cpu_freq_khz_min;
> +static int cpu_freq_khz_max;
> +
> +static struct clk *cpu_clk;
> +static struct cpufreq_frequency_table *arm_freq_table;
> +
> +static int set_cpu_freq(int freq)
> +{
> + int ret = 0;
> + int org_cpu_rate;
> +
> + org_cpu_rate = clk_get_rate(cpu_clk);
> + if (org_cpu_rate == freq)
> + return ret;
> +
> + ret = clk_set_rate(cpu_clk, freq);
> + if (ret != 0) {
> + printk(KERN_DEBUG "cannot set CPU clock rate\n");
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int arm_verify_speed(struct cpufreq_policy *policy)
> +{
> + return cpufreq_frequency_table_verify(policy, arm_freq_table);
> +}
> +
> +static unsigned int arm_get_speed(unsigned int cpu)
> +{
> + return clk_get_rate(cpu_clk) / 1000;
> +}
> +
> +static int arm_set_target(struct cpufreq_policy *policy,
> + unsigned int target_freq, unsigned int relation)
> +{
> + struct cpufreq_freqs freqs;
> + int freq_Hz, cpu;
> + int ret = 0;
> + unsigned int index;
> +
> + cpufreq_frequency_table_target(policy, arm_freq_table,
> + target_freq, relation,&index);
> + freq_Hz = arm_freq_table[index].frequency * 1000;
> +
> + freqs.old = clk_get_rate(cpu_clk) / 1000;
> + freqs.new = clk_round_rate(cpu_clk, freq_Hz);
> + freqs.new = (freqs.new ? freqs.new : freq_Hz) / 1000;
> + freqs.flags = 0;
> +
> + if (freqs.old == freqs.new)
> + return 0;
> +
> + for_each_possible_cpu(cpu) {
> + freqs.cpu = cpu;
> + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
> + }
> +
> + ret = set_cpu_freq(freq_Hz);
> +
> +#ifdef CONFIG_SMP
> + /* loops_per_jiffy is not updated by the cpufreq core for SMP systems.
> + * So update it for all CPUs.
> + */
> + for_each_possible_cpu(cpu)
> + per_cpu(cpu_data, cpu).loops_per_jiffy =
> + cpufreq_scale(per_cpu(cpu_data, cpu).loops_per_jiffy,
> + freqs.old, freqs.new);
> +#endif
> +
> + for_each_possible_cpu(cpu) {
> + freqs.cpu = cpu;
> + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
> + }
> +
> + return ret;
> +}
> +
> +static int arm_cpufreq_init(struct cpufreq_policy *policy)
> +{
> + int ret;
> +
> + printk(KERN_INFO "ARM CPU frequency driver\n");
> +
> + if (policy->cpu>= num_possible_cpus())
> + return -EINVAL;
> +
> + policy->cur = clk_get_rate(cpu_clk) / 1000;
> + policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min;
> + policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max;
> + policy->shared_type = CPUFREQ_SHARED_TYPE_ANY;
> + cpumask_setall(policy->cpus);
> + /* Manual states, that PLL stabilizes in two CLK32 periods */
> + policy->cpuinfo.transition_latency = trans_latency;
> +
> + ret = cpufreq_frequency_table_cpuinfo(policy, arm_freq_table);
> +
> + if (ret< 0) {
> + printk(KERN_ERR "%s: failed to register i.MXC CPUfreq with error code %d\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + cpufreq_frequency_table_get_attr(arm_freq_table, policy->cpu);
> + return 0;
> +}
> +
> +static int arm_cpufreq_exit(struct cpufreq_policy *policy)
> +{
> + cpufreq_frequency_table_put_attr(policy->cpu);
> +
> + set_cpu_freq(cpu_freq_khz_max * 1000);
> + return 0;
> +}
> +
> +static struct cpufreq_driver arm_cpufreq_driver = {
> + .flags = CPUFREQ_STICKY,
> + .verify = arm_verify_speed,
> + .target = arm_set_target,
> + .get = arm_get_speed,
> + .init = arm_cpufreq_init,
> + .exit = arm_cpufreq_exit,
> + .name = "arm",
> +};
> +
> +static int __devinit arm_cpufreq_driver_init(void)
> +{
> + struct device_node *cpu0;
> + const struct property *pp;
> + int i, ret;
> +
> + cpu0 = of_find_node_by_path("/cpus/cpu at 0");
> + if (!cpu0)
> + return -ENODEV;
> +
> + pp = of_find_property(cpu0, "cpu_freqs", NULL);
> + if (!pp) {
> + ret = -ENODEV;
> + goto put_node;
> + }
> + cpu_op_nr = pp->length / sizeof(u32);
> + if (!cpu_op_nr) {
> + ret = -ENODEV;
> + goto put_node;
> + }
> + ret = -ENOMEM;
> + cpu_freqs = kzalloc(sizeof(*cpu_freqs) * cpu_op_nr, GFP_KERNEL);
> + if (!cpu_freqs)
> + goto put_node;
> + of_property_read_u32_array(cpu0, "cpu_freqs", cpu_freqs, cpu_op_nr);
> +
> + pp = of_find_property(cpu0, "cpu_volts", NULL);
> + if (pp) {
> + if (cpu_op_nr == pp->length / sizeof(u32)) {
> + cpu_volts = kzalloc(sizeof(*cpu_freqs) * cpu_op_nr,
> + GFP_KERNEL);
> + if (!cpu_volts)
> + goto free_cpu_freqs;
> + of_property_read_u32_array(cpu0, "cpu_volts",
> + cpu_freqs, cpu_op_nr);
cpu_freqs should clearly be cpu_volts in this instance.
> + } else
> + printk(KERN_WARNING "cpufreq: invalid cpu_volts!\n");
> + }
> +
> + if (of_property_read_u32(cpu0, "trans_latency",&trans_latency))
> + trans_latency = CPUFREQ_ETERNAL;
I'm not sure this is an appropriate default value. I suspect it will do
very strange things on actual hardware that fails to define
trans_latency in the device tree.
> +
> + cpu_freq_khz_min = cpu_freqs[0] / 1000;
> + cpu_freq_khz_max = cpu_freqs[0] / 1000;
> +
> + arm_freq_table = kmalloc(sizeof(struct cpufreq_frequency_table)
> + * (cpu_op_nr + 1), GFP_KERNEL);
> + if (!arm_freq_table)
> + goto free_cpu_volts;
> +
> + for (i = 0; i< cpu_op_nr; i++) {
> + arm_freq_table[i].index = i;
> + arm_freq_table[i].frequency = cpu_freqs[i] / 1000;
> + if ((cpu_freqs[i] / 1000)< cpu_freq_khz_min)
> + cpu_freq_khz_min = cpu_freqs[i] / 1000;
> + if ((cpu_freqs[i] / 1000)> cpu_freq_khz_max)
> + cpu_freq_khz_max = cpu_freqs[i] / 1000;
> + }
> +
> + arm_freq_table[i].index = i;
> + arm_freq_table[i].frequency = CPUFREQ_TABLE_END;
> +
> + cpu_clk = clk_get(NULL, "cpu");
> + if (IS_ERR(cpu_clk)) {
> + printk(KERN_ERR "%s: failed to get cpu clock\n", __func__);
> + ret = PTR_ERR(cpu_clk);
> + goto free_freq_table;
> + }
I'd prefer to see clk_get90 replaced with of_clk_get() and
get_this_cpu_node() from the clk-cpufreq driver by Jamie Iles that
I resubmitted yesterday. The of_clk_get() doesn't require defining
an arm_clk structure, so it's slightly more portable for mach-
definitions. Also, the of_clk_get() method doesn't require
mach/clock.h and mach/hardware.h, which is good because most
of the mach- definitions don't include them.
--Mark Langsdorf
Calxeda, Inc.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
@ 2011-12-15 18:50 ` Mark Langsdorf
0 siblings, 0 replies; 25+ messages in thread
From: Mark Langsdorf @ 2011-12-15 18:50 UTC (permalink / raw)
To: Richard Zhao
Cc: linux-arm-kernel, cpufreq, linux, davej, kernel, shawn.guo,
eric.miao, linaro-dev, patches
Comments below. I tested this on the Calxeda Highbank SoC using
QEMU. I found one definite error and a few things I would change.
On 12/15/2011 05:16 AM, Richard Zhao wrote:
> It support single core and multi-core ARM SoCs. But it assume
> all cores share the same frequency and voltage.
>
> Signed-off-by: Richard Zhao<richard.zhao@linaro.org>
> ---
> diff --git a/drivers/cpufreq/arm-cpufreq.c b/drivers/cpufreq/arm-cpufreq.c
> new file mode 100644
> index 0000000..fd9759f
> --- /dev/null
> +++ b/drivers/cpufreq/arm-cpufreq.c
> @@ -0,0 +1,260 @@
> +/*
> + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
> + */
> +
> +/*
> + * The code contained herein is licensed under the GNU General Public
> + * License. You may obtain a copy of the GNU General Public License
> + * Version 2 or later at the following locations:
> + *
> + * http://www.opensource.org/licenses/gpl-license.html
> + * http://www.gnu.org/copyleft/gpl.html
> + */
> +
> +#include<linux/module.h>
> +#include<linux/cpufreq.h>
> +#include<linux/clk.h>
> +#include<linux/err.h>
> +#include<linux/slab.h>
> +#include<linux/of.h>
> +#include<asm/cpu.h>
> +#include<mach/hardware.h>
> +#include<mach/clock.h>
These should probably be replaced by <linux/of_clk.h>. See
below for details.
> +
> +static u32 *cpu_freqs;
> +static u32 *cpu_volts;
> +static u32 trans_latency;
> +static int cpu_op_nr;
> +
> +static int cpu_freq_khz_min;
> +static int cpu_freq_khz_max;
> +
> +static struct clk *cpu_clk;
> +static struct cpufreq_frequency_table *arm_freq_table;
> +
> +static int set_cpu_freq(int freq)
> +{
> + int ret = 0;
> + int org_cpu_rate;
> +
> + org_cpu_rate = clk_get_rate(cpu_clk);
> + if (org_cpu_rate == freq)
> + return ret;
> +
> + ret = clk_set_rate(cpu_clk, freq);
> + if (ret != 0) {
> + printk(KERN_DEBUG "cannot set CPU clock rate\n");
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int arm_verify_speed(struct cpufreq_policy *policy)
> +{
> + return cpufreq_frequency_table_verify(policy, arm_freq_table);
> +}
> +
> +static unsigned int arm_get_speed(unsigned int cpu)
> +{
> + return clk_get_rate(cpu_clk) / 1000;
> +}
> +
> +static int arm_set_target(struct cpufreq_policy *policy,
> + unsigned int target_freq, unsigned int relation)
> +{
> + struct cpufreq_freqs freqs;
> + int freq_Hz, cpu;
> + int ret = 0;
> + unsigned int index;
> +
> + cpufreq_frequency_table_target(policy, arm_freq_table,
> + target_freq, relation,&index);
> + freq_Hz = arm_freq_table[index].frequency * 1000;
> +
> + freqs.old = clk_get_rate(cpu_clk) / 1000;
> + freqs.new = clk_round_rate(cpu_clk, freq_Hz);
> + freqs.new = (freqs.new ? freqs.new : freq_Hz) / 1000;
> + freqs.flags = 0;
> +
> + if (freqs.old == freqs.new)
> + return 0;
> +
> + for_each_possible_cpu(cpu) {
> + freqs.cpu = cpu;
> + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
> + }
> +
> + ret = set_cpu_freq(freq_Hz);
> +
> +#ifdef CONFIG_SMP
> + /* loops_per_jiffy is not updated by the cpufreq core for SMP systems.
> + * So update it for all CPUs.
> + */
> + for_each_possible_cpu(cpu)
> + per_cpu(cpu_data, cpu).loops_per_jiffy =
> + cpufreq_scale(per_cpu(cpu_data, cpu).loops_per_jiffy,
> + freqs.old, freqs.new);
> +#endif
> +
> + for_each_possible_cpu(cpu) {
> + freqs.cpu = cpu;
> + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
> + }
> +
> + return ret;
> +}
> +
> +static int arm_cpufreq_init(struct cpufreq_policy *policy)
> +{
> + int ret;
> +
> + printk(KERN_INFO "ARM CPU frequency driver\n");
> +
> + if (policy->cpu>= num_possible_cpus())
> + return -EINVAL;
> +
> + policy->cur = clk_get_rate(cpu_clk) / 1000;
> + policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min;
> + policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max;
> + policy->shared_type = CPUFREQ_SHARED_TYPE_ANY;
> + cpumask_setall(policy->cpus);
> + /* Manual states, that PLL stabilizes in two CLK32 periods */
> + policy->cpuinfo.transition_latency = trans_latency;
> +
> + ret = cpufreq_frequency_table_cpuinfo(policy, arm_freq_table);
> +
> + if (ret< 0) {
> + printk(KERN_ERR "%s: failed to register i.MXC CPUfreq with error code %d\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + cpufreq_frequency_table_get_attr(arm_freq_table, policy->cpu);
> + return 0;
> +}
> +
> +static int arm_cpufreq_exit(struct cpufreq_policy *policy)
> +{
> + cpufreq_frequency_table_put_attr(policy->cpu);
> +
> + set_cpu_freq(cpu_freq_khz_max * 1000);
> + return 0;
> +}
> +
> +static struct cpufreq_driver arm_cpufreq_driver = {
> + .flags = CPUFREQ_STICKY,
> + .verify = arm_verify_speed,
> + .target = arm_set_target,
> + .get = arm_get_speed,
> + .init = arm_cpufreq_init,
> + .exit = arm_cpufreq_exit,
> + .name = "arm",
> +};
> +
> +static int __devinit arm_cpufreq_driver_init(void)
> +{
> + struct device_node *cpu0;
> + const struct property *pp;
> + int i, ret;
> +
> + cpu0 = of_find_node_by_path("/cpus/cpu@0");
> + if (!cpu0)
> + return -ENODEV;
> +
> + pp = of_find_property(cpu0, "cpu_freqs", NULL);
> + if (!pp) {
> + ret = -ENODEV;
> + goto put_node;
> + }
> + cpu_op_nr = pp->length / sizeof(u32);
> + if (!cpu_op_nr) {
> + ret = -ENODEV;
> + goto put_node;
> + }
> + ret = -ENOMEM;
> + cpu_freqs = kzalloc(sizeof(*cpu_freqs) * cpu_op_nr, GFP_KERNEL);
> + if (!cpu_freqs)
> + goto put_node;
> + of_property_read_u32_array(cpu0, "cpu_freqs", cpu_freqs, cpu_op_nr);
> +
> + pp = of_find_property(cpu0, "cpu_volts", NULL);
> + if (pp) {
> + if (cpu_op_nr == pp->length / sizeof(u32)) {
> + cpu_volts = kzalloc(sizeof(*cpu_freqs) * cpu_op_nr,
> + GFP_KERNEL);
> + if (!cpu_volts)
> + goto free_cpu_freqs;
> + of_property_read_u32_array(cpu0, "cpu_volts",
> + cpu_freqs, cpu_op_nr);
cpu_freqs should clearly be cpu_volts in this instance.
> + } else
> + printk(KERN_WARNING "cpufreq: invalid cpu_volts!\n");
> + }
> +
> + if (of_property_read_u32(cpu0, "trans_latency",&trans_latency))
> + trans_latency = CPUFREQ_ETERNAL;
I'm not sure this is an appropriate default value. I suspect it will do
very strange things on actual hardware that fails to define
trans_latency in the device tree.
> +
> + cpu_freq_khz_min = cpu_freqs[0] / 1000;
> + cpu_freq_khz_max = cpu_freqs[0] / 1000;
> +
> + arm_freq_table = kmalloc(sizeof(struct cpufreq_frequency_table)
> + * (cpu_op_nr + 1), GFP_KERNEL);
> + if (!arm_freq_table)
> + goto free_cpu_volts;
> +
> + for (i = 0; i< cpu_op_nr; i++) {
> + arm_freq_table[i].index = i;
> + arm_freq_table[i].frequency = cpu_freqs[i] / 1000;
> + if ((cpu_freqs[i] / 1000)< cpu_freq_khz_min)
> + cpu_freq_khz_min = cpu_freqs[i] / 1000;
> + if ((cpu_freqs[i] / 1000)> cpu_freq_khz_max)
> + cpu_freq_khz_max = cpu_freqs[i] / 1000;
> + }
> +
> + arm_freq_table[i].index = i;
> + arm_freq_table[i].frequency = CPUFREQ_TABLE_END;
> +
> + cpu_clk = clk_get(NULL, "cpu");
> + if (IS_ERR(cpu_clk)) {
> + printk(KERN_ERR "%s: failed to get cpu clock\n", __func__);
> + ret = PTR_ERR(cpu_clk);
> + goto free_freq_table;
> + }
I'd prefer to see clk_get90 replaced with of_clk_get() and
get_this_cpu_node() from the clk-cpufreq driver by Jamie Iles that
I resubmitted yesterday. The of_clk_get() doesn't require defining
an arm_clk structure, so it's slightly more portable for mach-
definitions. Also, the of_clk_get() method doesn't require
mach/clock.h and mach/hardware.h, which is good because most
of the mach- definitions don't include them.
--Mark Langsdorf
Calxeda, Inc.
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC V1 2/4] dts/imx6q: add cpufreq property
2011-12-15 11:16 ` [RFC V1 2/4] dts/imx6q: add cpufreq property Richard Zhao
@ 2011-12-15 18:52 ` Mark Langsdorf
2011-12-15 18:52 ` Mark Langsdorf
1 sibling, 0 replies; 25+ messages in thread
From: Mark Langsdorf @ 2011-12-15 18:52 UTC (permalink / raw)
To: linux-arm-kernel
On 12/15/2011 05:16 AM, Richard Zhao wrote:
> Signed-off-by: Richard Zhao<richard.zhao@linaro.org>
> ---
> arch/arm/boot/dts/imx6q.dtsi | 2 ++
> 1 files changed, 2 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
> index 263e8f3..9e9943b 100644
> --- a/arch/arm/boot/dts/imx6q.dtsi
> +++ b/arch/arm/boot/dts/imx6q.dtsi
> @@ -29,6 +29,8 @@
> compatible = "arm,cortex-a9";
> reg =<0>;
> next-level-cache =<&L2>;
> + cpu_freqs =<996000000 792000000 396000000 198000000>;
> + cpu_volts =<1225000 1100000 950000 850000>;
+ trans_latency =<3000>;
> };
>
> cpu at 1 {
The trans_latency value definitely needs to be defined to avoid
strange behavior in the driver.
--Mark Langsdorf
Calxeda, Inc.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [RFC V1 2/4] dts/imx6q: add cpufreq property
@ 2011-12-15 18:52 ` Mark Langsdorf
0 siblings, 0 replies; 25+ messages in thread
From: Mark Langsdorf @ 2011-12-15 18:52 UTC (permalink / raw)
To: Richard Zhao
Cc: linux-arm-kernel, cpufreq, linux, davej, kernel, shawn.guo,
eric.miao, linaro-dev, patches
On 12/15/2011 05:16 AM, Richard Zhao wrote:
> Signed-off-by: Richard Zhao<richard.zhao@linaro.org>
> ---
> arch/arm/boot/dts/imx6q.dtsi | 2 ++
> 1 files changed, 2 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
> index 263e8f3..9e9943b 100644
> --- a/arch/arm/boot/dts/imx6q.dtsi
> +++ b/arch/arm/boot/dts/imx6q.dtsi
> @@ -29,6 +29,8 @@
> compatible = "arm,cortex-a9";
> reg =<0>;
> next-level-cache =<&L2>;
> + cpu_freqs =<996000000 792000000 396000000 198000000>;
> + cpu_volts =<1225000 1100000 950000 850000>;
+ trans_latency =<3000>;
> };
>
> cpu@1 {
The trans_latency value definitely needs to be defined to avoid
strange behavior in the driver.
--Mark Langsdorf
Calxeda, Inc.
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
2011-12-15 11:16 ` Richard Zhao
@ 2011-12-15 20:29 ` Russell King - ARM Linux
-1 siblings, 0 replies; 25+ messages in thread
From: Russell King - ARM Linux @ 2011-12-15 20:29 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Dec 15, 2011 at 07:16:35PM +0800, Richard Zhao wrote:
> +#ifdef CONFIG_SMP
> + /* loops_per_jiffy is not updated by the cpufreq core for SMP systems.
> + * So update it for all CPUs.
> + */
> + for_each_possible_cpu(cpu)
> + per_cpu(cpu_data, cpu).loops_per_jiffy =
> + cpufreq_scale(per_cpu(cpu_data, cpu).loops_per_jiffy,
> + freqs.old, freqs.new);
NAK. If you think this is a good solution, you're very wrong. If this
is what's in the core cpufreq code, then it too is very broken.
I've seen this exact method result in the loops_per_jiffy being totally
buggered over time by the constant scaling up and down. The only way
to do this _sensibly_ is to record the _initial_ loops_per_jiffy and
_inital_ frequency, and scale from that.
That way you get consistent results irrespective of the scalings you
do over time, rather than something which continually deteriorates with
every frequency change.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
@ 2011-12-15 20:29 ` Russell King - ARM Linux
0 siblings, 0 replies; 25+ messages in thread
From: Russell King - ARM Linux @ 2011-12-15 20:29 UTC (permalink / raw)
To: Richard Zhao
Cc: linux-arm-kernel, cpufreq, davej, kernel, shawn.guo, eric.miao,
linaro-dev, patches
On Thu, Dec 15, 2011 at 07:16:35PM +0800, Richard Zhao wrote:
> +#ifdef CONFIG_SMP
> + /* loops_per_jiffy is not updated by the cpufreq core for SMP systems.
> + * So update it for all CPUs.
> + */
> + for_each_possible_cpu(cpu)
> + per_cpu(cpu_data, cpu).loops_per_jiffy =
> + cpufreq_scale(per_cpu(cpu_data, cpu).loops_per_jiffy,
> + freqs.old, freqs.new);
NAK. If you think this is a good solution, you're very wrong. If this
is what's in the core cpufreq code, then it too is very broken.
I've seen this exact method result in the loops_per_jiffy being totally
buggered over time by the constant scaling up and down. The only way
to do this _sensibly_ is to record the _initial_ loops_per_jiffy and
_inital_ frequency, and scale from that.
That way you get consistent results irrespective of the scalings you
do over time, rather than something which continually deteriorates with
every frequency change.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [RFC V1 2/4] dts/imx6q: add cpufreq property
2011-12-15 11:58 ` Shawn Guo
@ 2011-12-16 2:47 ` Richard Zhao
-1 siblings, 0 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-16 2:47 UTC (permalink / raw)
To: Shawn Guo
Cc: Richard Zhao, linux, patches, devicetree-discuss, eric.miao,
cpufreq, kernel, davej, linaro-dev, linux-arm-kernel
On Thu, Dec 15, 2011 at 07:58:54PM +0800, Shawn Guo wrote:
> Hi Richard,
>
> Whenever we invent some new device tree binding support, we need to
> Cc devicetree-discuss@lists.ozlabs.org (Cc-ed).
Thanks for your reminder.
>
> On Thu, Dec 15, 2011 at 07:16:36PM +0800, Richard Zhao wrote:
> > Signed-off-by: Richard Zhao <richard.zhao@linaro.org>
> > ---
> > arch/arm/boot/dts/imx6q.dtsi | 2 ++
> > 1 files changed, 2 insertions(+), 0 deletions(-)
> >
> > diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
> > index 263e8f3..9e9943b 100644
> > --- a/arch/arm/boot/dts/imx6q.dtsi
> > +++ b/arch/arm/boot/dts/imx6q.dtsi
> > @@ -29,6 +29,8 @@
> > compatible = "arm,cortex-a9";
> > reg = <0>;
> > next-level-cache = <&L2>;
> > + cpu_freqs = <996000000 792000000 396000000 198000000>;
> > + cpu_volts = <1225000 1100000 950000 850000>;
>
> For dt property name, we use '-' rather than '_'.
ok.
>
> And I'm not sure this is correct, since these data obviously does not
> belong to "arm,cortex-a9".
That's why RFC.
Thanks
Richard
>
> Regards,
> Shawn
>
> > };
> >
> > cpu@1 {
> > --
> > 1.7.5.4
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC V1 2/4] dts/imx6q: add cpufreq property
@ 2011-12-16 2:47 ` Richard Zhao
0 siblings, 0 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-16 2:47 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Dec 15, 2011 at 07:58:54PM +0800, Shawn Guo wrote:
> Hi Richard,
>
> Whenever we invent some new device tree binding support, we need to
> Cc devicetree-discuss at lists.ozlabs.org (Cc-ed).
Thanks for your reminder.
>
> On Thu, Dec 15, 2011 at 07:16:36PM +0800, Richard Zhao wrote:
> > Signed-off-by: Richard Zhao <richard.zhao@linaro.org>
> > ---
> > arch/arm/boot/dts/imx6q.dtsi | 2 ++
> > 1 files changed, 2 insertions(+), 0 deletions(-)
> >
> > diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
> > index 263e8f3..9e9943b 100644
> > --- a/arch/arm/boot/dts/imx6q.dtsi
> > +++ b/arch/arm/boot/dts/imx6q.dtsi
> > @@ -29,6 +29,8 @@
> > compatible = "arm,cortex-a9";
> > reg = <0>;
> > next-level-cache = <&L2>;
> > + cpu_freqs = <996000000 792000000 396000000 198000000>;
> > + cpu_volts = <1225000 1100000 950000 850000>;
>
> For dt property name, we use '-' rather than '_'.
ok.
>
> And I'm not sure this is correct, since these data obviously does not
> belong to "arm,cortex-a9".
That's why RFC.
Thanks
Richard
>
> Regards,
> Shawn
>
> > };
> >
> > cpu at 1 {
> > --
> > 1.7.5.4
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
2011-12-15 18:50 ` Mark Langsdorf
@ 2011-12-16 3:11 ` Richard Zhao
-1 siblings, 0 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-16 3:11 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Dec 15, 2011 at 12:50:07PM -0600, Mark Langsdorf wrote:
> Comments below. I tested this on the Calxeda Highbank SoC using
> QEMU. I found one definite error and a few things I would change.
Thanks for your test.
>
> On 12/15/2011 05:16 AM, Richard Zhao wrote:
> >It support single core and multi-core ARM SoCs. But it assume
> >all cores share the same frequency and voltage.
> >
> >Signed-off-by: Richard Zhao<richard.zhao@linaro.org>
> >---
>
> >diff --git a/drivers/cpufreq/arm-cpufreq.c b/drivers/cpufreq/arm-cpufreq.c
> >new file mode 100644
> >index 0000000..fd9759f
> >--- /dev/null
> >+++ b/drivers/cpufreq/arm-cpufreq.c
> >@@ -0,0 +1,260 @@
> >+/*
> >+ * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
> >+ */
> >+
> >+/*
> >+ * The code contained herein is licensed under the GNU General Public
> >+ * License. You may obtain a copy of the GNU General Public License
> >+ * Version 2 or later at the following locations:
> >+ *
> >+ * http://www.opensource.org/licenses/gpl-license.html
> >+ * http://www.gnu.org/copyleft/gpl.html
> >+ */
> >+
> >+#include<linux/module.h>
> >+#include<linux/cpufreq.h>
> >+#include<linux/clk.h>
> >+#include<linux/err.h>
> >+#include<linux/slab.h>
> >+#include<linux/of.h>
> >+#include<asm/cpu.h>
> >+#include<mach/hardware.h>
> >+#include<mach/clock.h>
>
> These should probably be replaced by <linux/of_clk.h>. See
> below for details.
I'll remove <mach/*>.
But are you sure clk DT binding has went to -next or -rc? If yes,
I'm glad to use it. If no, I don't want to pend on it.
>
> >+
> >+static u32 *cpu_freqs;
> >+static u32 *cpu_volts;
> >+static u32 trans_latency;
> >+static int cpu_op_nr;
> >+
> >+static int cpu_freq_khz_min;
> >+static int cpu_freq_khz_max;
> >+
> >+static struct clk *cpu_clk;
> >+static struct cpufreq_frequency_table *arm_freq_table;
> >+
> >+static int set_cpu_freq(int freq)
> >+{
> >+ int ret = 0;
> >+ int org_cpu_rate;
> >+
> >+ org_cpu_rate = clk_get_rate(cpu_clk);
> >+ if (org_cpu_rate == freq)
> >+ return ret;
> >+
> >+ ret = clk_set_rate(cpu_clk, freq);
> >+ if (ret != 0) {
> >+ printk(KERN_DEBUG "cannot set CPU clock rate\n");
> >+ return ret;
> >+ }
> >+
> >+ return ret;
> >+}
> >+
> >+static int arm_verify_speed(struct cpufreq_policy *policy)
> >+{
> >+ return cpufreq_frequency_table_verify(policy, arm_freq_table);
> >+}
> >+
> >+static unsigned int arm_get_speed(unsigned int cpu)
> >+{
> >+ return clk_get_rate(cpu_clk) / 1000;
> >+}
> >+
> >+static int arm_set_target(struct cpufreq_policy *policy,
> >+ unsigned int target_freq, unsigned int relation)
> >+{
> >+ struct cpufreq_freqs freqs;
> >+ int freq_Hz, cpu;
> >+ int ret = 0;
> >+ unsigned int index;
> >+
> >+ cpufreq_frequency_table_target(policy, arm_freq_table,
> >+ target_freq, relation,&index);
> >+ freq_Hz = arm_freq_table[index].frequency * 1000;
> >+
> >+ freqs.old = clk_get_rate(cpu_clk) / 1000;
> >+ freqs.new = clk_round_rate(cpu_clk, freq_Hz);
> >+ freqs.new = (freqs.new ? freqs.new : freq_Hz) / 1000;
> >+ freqs.flags = 0;
> >+
> >+ if (freqs.old == freqs.new)
> >+ return 0;
> >+
> >+ for_each_possible_cpu(cpu) {
> >+ freqs.cpu = cpu;
> >+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
> >+ }
> >+
> >+ ret = set_cpu_freq(freq_Hz);
> >+
> >+#ifdef CONFIG_SMP
> >+ /* loops_per_jiffy is not updated by the cpufreq core for SMP systems.
> >+ * So update it for all CPUs.
> >+ */
> >+ for_each_possible_cpu(cpu)
> >+ per_cpu(cpu_data, cpu).loops_per_jiffy =
> >+ cpufreq_scale(per_cpu(cpu_data, cpu).loops_per_jiffy,
> >+ freqs.old, freqs.new);
> >+#endif
> >+
> >+ for_each_possible_cpu(cpu) {
> >+ freqs.cpu = cpu;
> >+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
> >+ }
> >+
> >+ return ret;
> >+}
> >+
> >+static int arm_cpufreq_init(struct cpufreq_policy *policy)
> >+{
> >+ int ret;
> >+
> >+ printk(KERN_INFO "ARM CPU frequency driver\n");
> >+
> >+ if (policy->cpu>= num_possible_cpus())
> >+ return -EINVAL;
> >+
> >+ policy->cur = clk_get_rate(cpu_clk) / 1000;
> >+ policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min;
> >+ policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max;
> >+ policy->shared_type = CPUFREQ_SHARED_TYPE_ANY;
> >+ cpumask_setall(policy->cpus);
> >+ /* Manual states, that PLL stabilizes in two CLK32 periods */
> >+ policy->cpuinfo.transition_latency = trans_latency;
> >+
> >+ ret = cpufreq_frequency_table_cpuinfo(policy, arm_freq_table);
> >+
> >+ if (ret< 0) {
> >+ printk(KERN_ERR "%s: failed to register i.MXC CPUfreq with error code %d\n",
> >+ __func__, ret);
> >+ return ret;
> >+ }
> >+
> >+ cpufreq_frequency_table_get_attr(arm_freq_table, policy->cpu);
> >+ return 0;
> >+}
> >+
> >+static int arm_cpufreq_exit(struct cpufreq_policy *policy)
> >+{
> >+ cpufreq_frequency_table_put_attr(policy->cpu);
> >+
> >+ set_cpu_freq(cpu_freq_khz_max * 1000);
> >+ return 0;
> >+}
> >+
> >+static struct cpufreq_driver arm_cpufreq_driver = {
> >+ .flags = CPUFREQ_STICKY,
> >+ .verify = arm_verify_speed,
> >+ .target = arm_set_target,
> >+ .get = arm_get_speed,
> >+ .init = arm_cpufreq_init,
> >+ .exit = arm_cpufreq_exit,
> >+ .name = "arm",
> >+};
> >+
> >+static int __devinit arm_cpufreq_driver_init(void)
> >+{
> >+ struct device_node *cpu0;
> >+ const struct property *pp;
> >+ int i, ret;
> >+
> >+ cpu0 = of_find_node_by_path("/cpus/cpu at 0");
> >+ if (!cpu0)
> >+ return -ENODEV;
> >+
> >+ pp = of_find_property(cpu0, "cpu_freqs", NULL);
> >+ if (!pp) {
> >+ ret = -ENODEV;
> >+ goto put_node;
> >+ }
> >+ cpu_op_nr = pp->length / sizeof(u32);
> >+ if (!cpu_op_nr) {
> >+ ret = -ENODEV;
> >+ goto put_node;
> >+ }
> >+ ret = -ENOMEM;
> >+ cpu_freqs = kzalloc(sizeof(*cpu_freqs) * cpu_op_nr, GFP_KERNEL);
> >+ if (!cpu_freqs)
> >+ goto put_node;
> >+ of_property_read_u32_array(cpu0, "cpu_freqs", cpu_freqs, cpu_op_nr);
> >+
> >+ pp = of_find_property(cpu0, "cpu_volts", NULL);
> >+ if (pp) {
> >+ if (cpu_op_nr == pp->length / sizeof(u32)) {
> >+ cpu_volts = kzalloc(sizeof(*cpu_freqs) * cpu_op_nr,
> >+ GFP_KERNEL);
> >+ if (!cpu_volts)
> >+ goto free_cpu_freqs;
> >+ of_property_read_u32_array(cpu0, "cpu_volts",
> >+ cpu_freqs, cpu_op_nr);
>
> cpu_freqs should clearly be cpu_volts in this instance.
Thanks.
>
> >+ } else
> >+ printk(KERN_WARNING "cpufreq: invalid cpu_volts!\n");
> >+ }
> >+
> >+ if (of_property_read_u32(cpu0, "trans_latency",&trans_latency))
> >+ trans_latency = CPUFREQ_ETERNAL;
>
> I'm not sure this is an appropriate default value. I suspect it will do
> very strange things on actual hardware that fails to define
> trans_latency in the device tree.
CPUFREQ_ETERNAL breaks governor ondemand and conservative. But it's the
recommended default vaule in cpufreq doc.
> >+
> >+ cpu_freq_khz_min = cpu_freqs[0] / 1000;
> >+ cpu_freq_khz_max = cpu_freqs[0] / 1000;
> >+
> >+ arm_freq_table = kmalloc(sizeof(struct cpufreq_frequency_table)
> >+ * (cpu_op_nr + 1), GFP_KERNEL);
> >+ if (!arm_freq_table)
> >+ goto free_cpu_volts;
> >+
> >+ for (i = 0; i< cpu_op_nr; i++) {
> >+ arm_freq_table[i].index = i;
> >+ arm_freq_table[i].frequency = cpu_freqs[i] / 1000;
> >+ if ((cpu_freqs[i] / 1000)< cpu_freq_khz_min)
> >+ cpu_freq_khz_min = cpu_freqs[i] / 1000;
> >+ if ((cpu_freqs[i] / 1000)> cpu_freq_khz_max)
> >+ cpu_freq_khz_max = cpu_freqs[i] / 1000;
> >+ }
> >+
> >+ arm_freq_table[i].index = i;
> >+ arm_freq_table[i].frequency = CPUFREQ_TABLE_END;
> >+
> >+ cpu_clk = clk_get(NULL, "cpu");
> >+ if (IS_ERR(cpu_clk)) {
> >+ printk(KERN_ERR "%s: failed to get cpu clock\n", __func__);
> >+ ret = PTR_ERR(cpu_clk);
> >+ goto free_freq_table;
> >+ }
>
> I'd prefer to see clk_get90 replaced with of_clk_get()
ditto
> and
> get_this_cpu_node() from the clk-cpufreq driver by Jamie Iles that
> I resubmitted yesterday.
This driver only have one instance, because all cpu cores shares the same
clk and voltage. You can see cpumask_setall(policy->cpus).
And get_this_cpu_node() adds the dependency that cpufreq_driver.init must run
on the cpu the policy indicate, which is not correct.
Thanks for your comments
Richard
> The of_clk_get() doesn't require defining
> an arm_clk structure, so it's slightly more portable for mach-
> definitions. Also, the of_clk_get() method doesn't require
> mach/clock.h and mach/hardware.h, which is good because most
> of the mach- definitions don't include them.
>
> --Mark Langsdorf
> Calxeda, Inc.
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
@ 2011-12-16 3:11 ` Richard Zhao
0 siblings, 0 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-16 3:11 UTC (permalink / raw)
To: Mark Langsdorf
Cc: Richard Zhao, linux, patches, cpufreq, eric.miao, kernel, davej,
linaro-dev, shawn.guo, linux-arm-kernel
On Thu, Dec 15, 2011 at 12:50:07PM -0600, Mark Langsdorf wrote:
> Comments below. I tested this on the Calxeda Highbank SoC using
> QEMU. I found one definite error and a few things I would change.
Thanks for your test.
>
> On 12/15/2011 05:16 AM, Richard Zhao wrote:
> >It support single core and multi-core ARM SoCs. But it assume
> >all cores share the same frequency and voltage.
> >
> >Signed-off-by: Richard Zhao<richard.zhao@linaro.org>
> >---
>
> >diff --git a/drivers/cpufreq/arm-cpufreq.c b/drivers/cpufreq/arm-cpufreq.c
> >new file mode 100644
> >index 0000000..fd9759f
> >--- /dev/null
> >+++ b/drivers/cpufreq/arm-cpufreq.c
> >@@ -0,0 +1,260 @@
> >+/*
> >+ * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
> >+ */
> >+
> >+/*
> >+ * The code contained herein is licensed under the GNU General Public
> >+ * License. You may obtain a copy of the GNU General Public License
> >+ * Version 2 or later at the following locations:
> >+ *
> >+ * http://www.opensource.org/licenses/gpl-license.html
> >+ * http://www.gnu.org/copyleft/gpl.html
> >+ */
> >+
> >+#include<linux/module.h>
> >+#include<linux/cpufreq.h>
> >+#include<linux/clk.h>
> >+#include<linux/err.h>
> >+#include<linux/slab.h>
> >+#include<linux/of.h>
> >+#include<asm/cpu.h>
> >+#include<mach/hardware.h>
> >+#include<mach/clock.h>
>
> These should probably be replaced by <linux/of_clk.h>. See
> below for details.
I'll remove <mach/*>.
But are you sure clk DT binding has went to -next or -rc? If yes,
I'm glad to use it. If no, I don't want to pend on it.
>
> >+
> >+static u32 *cpu_freqs;
> >+static u32 *cpu_volts;
> >+static u32 trans_latency;
> >+static int cpu_op_nr;
> >+
> >+static int cpu_freq_khz_min;
> >+static int cpu_freq_khz_max;
> >+
> >+static struct clk *cpu_clk;
> >+static struct cpufreq_frequency_table *arm_freq_table;
> >+
> >+static int set_cpu_freq(int freq)
> >+{
> >+ int ret = 0;
> >+ int org_cpu_rate;
> >+
> >+ org_cpu_rate = clk_get_rate(cpu_clk);
> >+ if (org_cpu_rate == freq)
> >+ return ret;
> >+
> >+ ret = clk_set_rate(cpu_clk, freq);
> >+ if (ret != 0) {
> >+ printk(KERN_DEBUG "cannot set CPU clock rate\n");
> >+ return ret;
> >+ }
> >+
> >+ return ret;
> >+}
> >+
> >+static int arm_verify_speed(struct cpufreq_policy *policy)
> >+{
> >+ return cpufreq_frequency_table_verify(policy, arm_freq_table);
> >+}
> >+
> >+static unsigned int arm_get_speed(unsigned int cpu)
> >+{
> >+ return clk_get_rate(cpu_clk) / 1000;
> >+}
> >+
> >+static int arm_set_target(struct cpufreq_policy *policy,
> >+ unsigned int target_freq, unsigned int relation)
> >+{
> >+ struct cpufreq_freqs freqs;
> >+ int freq_Hz, cpu;
> >+ int ret = 0;
> >+ unsigned int index;
> >+
> >+ cpufreq_frequency_table_target(policy, arm_freq_table,
> >+ target_freq, relation,&index);
> >+ freq_Hz = arm_freq_table[index].frequency * 1000;
> >+
> >+ freqs.old = clk_get_rate(cpu_clk) / 1000;
> >+ freqs.new = clk_round_rate(cpu_clk, freq_Hz);
> >+ freqs.new = (freqs.new ? freqs.new : freq_Hz) / 1000;
> >+ freqs.flags = 0;
> >+
> >+ if (freqs.old == freqs.new)
> >+ return 0;
> >+
> >+ for_each_possible_cpu(cpu) {
> >+ freqs.cpu = cpu;
> >+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
> >+ }
> >+
> >+ ret = set_cpu_freq(freq_Hz);
> >+
> >+#ifdef CONFIG_SMP
> >+ /* loops_per_jiffy is not updated by the cpufreq core for SMP systems.
> >+ * So update it for all CPUs.
> >+ */
> >+ for_each_possible_cpu(cpu)
> >+ per_cpu(cpu_data, cpu).loops_per_jiffy =
> >+ cpufreq_scale(per_cpu(cpu_data, cpu).loops_per_jiffy,
> >+ freqs.old, freqs.new);
> >+#endif
> >+
> >+ for_each_possible_cpu(cpu) {
> >+ freqs.cpu = cpu;
> >+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
> >+ }
> >+
> >+ return ret;
> >+}
> >+
> >+static int arm_cpufreq_init(struct cpufreq_policy *policy)
> >+{
> >+ int ret;
> >+
> >+ printk(KERN_INFO "ARM CPU frequency driver\n");
> >+
> >+ if (policy->cpu>= num_possible_cpus())
> >+ return -EINVAL;
> >+
> >+ policy->cur = clk_get_rate(cpu_clk) / 1000;
> >+ policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min;
> >+ policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max;
> >+ policy->shared_type = CPUFREQ_SHARED_TYPE_ANY;
> >+ cpumask_setall(policy->cpus);
> >+ /* Manual states, that PLL stabilizes in two CLK32 periods */
> >+ policy->cpuinfo.transition_latency = trans_latency;
> >+
> >+ ret = cpufreq_frequency_table_cpuinfo(policy, arm_freq_table);
> >+
> >+ if (ret< 0) {
> >+ printk(KERN_ERR "%s: failed to register i.MXC CPUfreq with error code %d\n",
> >+ __func__, ret);
> >+ return ret;
> >+ }
> >+
> >+ cpufreq_frequency_table_get_attr(arm_freq_table, policy->cpu);
> >+ return 0;
> >+}
> >+
> >+static int arm_cpufreq_exit(struct cpufreq_policy *policy)
> >+{
> >+ cpufreq_frequency_table_put_attr(policy->cpu);
> >+
> >+ set_cpu_freq(cpu_freq_khz_max * 1000);
> >+ return 0;
> >+}
> >+
> >+static struct cpufreq_driver arm_cpufreq_driver = {
> >+ .flags = CPUFREQ_STICKY,
> >+ .verify = arm_verify_speed,
> >+ .target = arm_set_target,
> >+ .get = arm_get_speed,
> >+ .init = arm_cpufreq_init,
> >+ .exit = arm_cpufreq_exit,
> >+ .name = "arm",
> >+};
> >+
> >+static int __devinit arm_cpufreq_driver_init(void)
> >+{
> >+ struct device_node *cpu0;
> >+ const struct property *pp;
> >+ int i, ret;
> >+
> >+ cpu0 = of_find_node_by_path("/cpus/cpu@0");
> >+ if (!cpu0)
> >+ return -ENODEV;
> >+
> >+ pp = of_find_property(cpu0, "cpu_freqs", NULL);
> >+ if (!pp) {
> >+ ret = -ENODEV;
> >+ goto put_node;
> >+ }
> >+ cpu_op_nr = pp->length / sizeof(u32);
> >+ if (!cpu_op_nr) {
> >+ ret = -ENODEV;
> >+ goto put_node;
> >+ }
> >+ ret = -ENOMEM;
> >+ cpu_freqs = kzalloc(sizeof(*cpu_freqs) * cpu_op_nr, GFP_KERNEL);
> >+ if (!cpu_freqs)
> >+ goto put_node;
> >+ of_property_read_u32_array(cpu0, "cpu_freqs", cpu_freqs, cpu_op_nr);
> >+
> >+ pp = of_find_property(cpu0, "cpu_volts", NULL);
> >+ if (pp) {
> >+ if (cpu_op_nr == pp->length / sizeof(u32)) {
> >+ cpu_volts = kzalloc(sizeof(*cpu_freqs) * cpu_op_nr,
> >+ GFP_KERNEL);
> >+ if (!cpu_volts)
> >+ goto free_cpu_freqs;
> >+ of_property_read_u32_array(cpu0, "cpu_volts",
> >+ cpu_freqs, cpu_op_nr);
>
> cpu_freqs should clearly be cpu_volts in this instance.
Thanks.
>
> >+ } else
> >+ printk(KERN_WARNING "cpufreq: invalid cpu_volts!\n");
> >+ }
> >+
> >+ if (of_property_read_u32(cpu0, "trans_latency",&trans_latency))
> >+ trans_latency = CPUFREQ_ETERNAL;
>
> I'm not sure this is an appropriate default value. I suspect it will do
> very strange things on actual hardware that fails to define
> trans_latency in the device tree.
CPUFREQ_ETERNAL breaks governor ondemand and conservative. But it's the
recommended default vaule in cpufreq doc.
> >+
> >+ cpu_freq_khz_min = cpu_freqs[0] / 1000;
> >+ cpu_freq_khz_max = cpu_freqs[0] / 1000;
> >+
> >+ arm_freq_table = kmalloc(sizeof(struct cpufreq_frequency_table)
> >+ * (cpu_op_nr + 1), GFP_KERNEL);
> >+ if (!arm_freq_table)
> >+ goto free_cpu_volts;
> >+
> >+ for (i = 0; i< cpu_op_nr; i++) {
> >+ arm_freq_table[i].index = i;
> >+ arm_freq_table[i].frequency = cpu_freqs[i] / 1000;
> >+ if ((cpu_freqs[i] / 1000)< cpu_freq_khz_min)
> >+ cpu_freq_khz_min = cpu_freqs[i] / 1000;
> >+ if ((cpu_freqs[i] / 1000)> cpu_freq_khz_max)
> >+ cpu_freq_khz_max = cpu_freqs[i] / 1000;
> >+ }
> >+
> >+ arm_freq_table[i].index = i;
> >+ arm_freq_table[i].frequency = CPUFREQ_TABLE_END;
> >+
> >+ cpu_clk = clk_get(NULL, "cpu");
> >+ if (IS_ERR(cpu_clk)) {
> >+ printk(KERN_ERR "%s: failed to get cpu clock\n", __func__);
> >+ ret = PTR_ERR(cpu_clk);
> >+ goto free_freq_table;
> >+ }
>
> I'd prefer to see clk_get90 replaced with of_clk_get()
ditto
> and
> get_this_cpu_node() from the clk-cpufreq driver by Jamie Iles that
> I resubmitted yesterday.
This driver only have one instance, because all cpu cores shares the same
clk and voltage. You can see cpumask_setall(policy->cpus).
And get_this_cpu_node() adds the dependency that cpufreq_driver.init must run
on the cpu the policy indicate, which is not correct.
Thanks for your comments
Richard
> The of_clk_get() doesn't require defining
> an arm_clk structure, so it's slightly more portable for mach-
> definitions. Also, the of_clk_get() method doesn't require
> mach/clock.h and mach/hardware.h, which is good because most
> of the mach- definitions don't include them.
>
> --Mark Langsdorf
> Calxeda, Inc.
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
2011-12-15 20:29 ` Russell King - ARM Linux
@ 2011-12-16 3:13 ` Richard Zhao
-1 siblings, 0 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-16 3:13 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Dec 15, 2011 at 08:29:11PM +0000, Russell King - ARM Linux wrote:
> On Thu, Dec 15, 2011 at 07:16:35PM +0800, Richard Zhao wrote:
> > +#ifdef CONFIG_SMP
> > + /* loops_per_jiffy is not updated by the cpufreq core for SMP systems.
> > + * So update it for all CPUs.
> > + */
> > + for_each_possible_cpu(cpu)
> > + per_cpu(cpu_data, cpu).loops_per_jiffy =
> > + cpufreq_scale(per_cpu(cpu_data, cpu).loops_per_jiffy,
> > + freqs.old, freqs.new);
>
> NAK. If you think this is a good solution, you're very wrong. If this
> is what's in the core cpufreq code, then it too is very broken.
>
> I've seen this exact method result in the loops_per_jiffy being totally
> buggered over time by the constant scaling up and down. The only way
> to do this _sensibly_ is to record the _initial_ loops_per_jiffy and
> _inital_ frequency, and scale from that.
>
> That way you get consistent results irrespective of the scalings you
> do over time, rather than something which continually deteriorates with
> every frequency change.
Thanks for your comments. I'll recalculate loops_per_jiffy always based on boot
up values.
Thanks
Richard
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
@ 2011-12-16 3:13 ` Richard Zhao
0 siblings, 0 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-16 3:13 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: Richard Zhao, linaro-dev, patches, eric.miao, cpufreq, kernel,
davej, shawn.guo, linux-arm-kernel
On Thu, Dec 15, 2011 at 08:29:11PM +0000, Russell King - ARM Linux wrote:
> On Thu, Dec 15, 2011 at 07:16:35PM +0800, Richard Zhao wrote:
> > +#ifdef CONFIG_SMP
> > + /* loops_per_jiffy is not updated by the cpufreq core for SMP systems.
> > + * So update it for all CPUs.
> > + */
> > + for_each_possible_cpu(cpu)
> > + per_cpu(cpu_data, cpu).loops_per_jiffy =
> > + cpufreq_scale(per_cpu(cpu_data, cpu).loops_per_jiffy,
> > + freqs.old, freqs.new);
>
> NAK. If you think this is a good solution, you're very wrong. If this
> is what's in the core cpufreq code, then it too is very broken.
>
> I've seen this exact method result in the loops_per_jiffy being totally
> buggered over time by the constant scaling up and down. The only way
> to do this _sensibly_ is to record the _initial_ loops_per_jiffy and
> _inital_ frequency, and scale from that.
>
> That way you get consistent results irrespective of the scalings you
> do over time, rather than something which continually deteriorates with
> every frequency change.
Thanks for your comments. I'll recalculate loops_per_jiffy always based on boot
up values.
Thanks
Richard
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
2011-12-15 18:50 ` Mark Langsdorf
@ 2011-12-16 8:24 ` Russell King - ARM Linux
-1 siblings, 0 replies; 25+ messages in thread
From: Russell King - ARM Linux @ 2011-12-16 8:24 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Dec 15, 2011 at 12:50:07PM -0600, Mark Langsdorf wrote:
> I'd prefer to see clk_get90 replaced with of_clk_get() and
> get_this_cpu_node() from the clk-cpufreq driver by Jamie Iles that
> I resubmitted yesterday.
Why isn't of_clk_get() hidden inside clk_get() ?
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
@ 2011-12-16 8:24 ` Russell King - ARM Linux
0 siblings, 0 replies; 25+ messages in thread
From: Russell King - ARM Linux @ 2011-12-16 8:24 UTC (permalink / raw)
To: Mark Langsdorf
Cc: Richard Zhao, linux-arm-kernel, cpufreq, davej, kernel,
shawn.guo, eric.miao, linaro-dev, patches
On Thu, Dec 15, 2011 at 12:50:07PM -0600, Mark Langsdorf wrote:
> I'd prefer to see clk_get90 replaced with of_clk_get() and
> get_this_cpu_node() from the clk-cpufreq driver by Jamie Iles that
> I resubmitted yesterday.
Why isn't of_clk_get() hidden inside clk_get() ?
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
2011-12-16 8:24 ` Russell King - ARM Linux
@ 2011-12-16 10:18 ` Richard Zhao
-1 siblings, 0 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-16 10:18 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, Dec 16, 2011 at 08:24:23AM +0000, Russell King - ARM Linux wrote:
> On Thu, Dec 15, 2011 at 12:50:07PM -0600, Mark Langsdorf wrote:
> > I'd prefer to see clk_get90 replaced with of_clk_get() and
> > get_this_cpu_node() from the clk-cpufreq driver by Jamie Iles that
> > I resubmitted yesterday.
>
> Why isn't of_clk_get() hidden inside clk_get() ?
Good point.
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver
@ 2011-12-16 10:18 ` Richard Zhao
0 siblings, 0 replies; 25+ messages in thread
From: Richard Zhao @ 2011-12-16 10:18 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: Mark Langsdorf, linaro-dev, patches, cpufreq, eric.miao, kernel,
davej, shawn.guo, Richard Zhao, linux-arm-kernel
On Fri, Dec 16, 2011 at 08:24:23AM +0000, Russell King - ARM Linux wrote:
> On Thu, Dec 15, 2011 at 12:50:07PM -0600, Mark Langsdorf wrote:
> > I'd prefer to see clk_get90 replaced with of_clk_get() and
> > get_this_cpu_node() from the clk-cpufreq driver by Jamie Iles that
> > I resubmitted yesterday.
>
> Why isn't of_clk_get() hidden inside clk_get() ?
Good point.
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2011-12-16 10:18 UTC | newest]
Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-12-15 11:16 [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver Richard Zhao
2011-12-15 11:16 ` Richard Zhao
2011-12-15 11:16 ` [RFC V1 2/4] dts/imx6q: add cpufreq property Richard Zhao
2011-12-15 11:58 ` Shawn Guo
2011-12-15 11:58 ` Shawn Guo
2011-12-16 2:47 ` Richard Zhao
2011-12-16 2:47 ` Richard Zhao
2011-12-15 18:52 ` Mark Langsdorf
2011-12-15 18:52 ` Mark Langsdorf
2011-12-15 11:16 ` [RFC V1 3/4] arm/imx6q: register arm_clk as cpu to clkdev Richard Zhao
2011-12-15 11:16 ` [RFC V1 4/4] arm/imx6q: select ARCH_HAS_CPUFREQ Richard Zhao
2011-12-15 11:19 ` [RFC V1 1/4] cpufreq: add arm soc generic cpufreq driver Richard Zhao
2011-12-15 11:19 ` Richard Zhao
2011-12-15 18:50 ` Mark Langsdorf
2011-12-15 18:50 ` Mark Langsdorf
2011-12-16 3:11 ` Richard Zhao
2011-12-16 3:11 ` Richard Zhao
2011-12-16 8:24 ` Russell King - ARM Linux
2011-12-16 8:24 ` Russell King - ARM Linux
2011-12-16 10:18 ` Richard Zhao
2011-12-16 10:18 ` Richard Zhao
2011-12-15 20:29 ` Russell King - ARM Linux
2011-12-15 20:29 ` Russell King - ARM Linux
2011-12-16 3:13 ` Richard Zhao
2011-12-16 3:13 ` Richard Zhao
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.