From b4573d1d657aae28bedf3a9a1f5367e09c80d1d6 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Thu, 19 Dec 2013 09:16:47 -0500 Subject: cpufreq: imx6q: correct VDDSOC/PU voltage scaling when cpufreq is changed on i.MX6Q, cpu freq change need to follow below flows: 1. each setpoint has different VDDARM, VDDSOC/PU voltage, get the setpoint table from dts; 2. when cpu freq is scaling up, need to increase VDDSOC/PU voltage before VDDARM, if VDDPU is off, no need to change it; 3. when cpu freq is scaling down, need to decrease VDDARM voltage before VDDSOC/PU, if VDDPU is off, no need to change it; normally dts will pass vddsoc/pu freq/volt info to kernel, if not, will use fixed value for vddsoc/pu voltage setting. Signed-off-by: Anson Huang Acked-by: Shawn Guo Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/imx6q-cpufreq.c | 106 +++++++++++++++++++++++++++++----------- 1 file changed, 77 insertions(+), 29 deletions(-) (limited to 'drivers/cpufreq/imx6q-cpufreq.c') diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 4b3f18e5f36b..c29198fbc9b7 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -35,6 +35,9 @@ static struct device *cpu_dev; static struct cpufreq_frequency_table *freq_table; static unsigned int transition_latency; +static u32 *imx6_soc_volt; +static u32 soc_opp_count; + static unsigned int imx6q_get_speed(unsigned int cpu) { return clk_get_rate(arm_clk) / 1000; @@ -69,23 +72,22 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) /* scaling up? scale voltage before frequency */ if (new_freq > old_freq) { + ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0); + if (ret) { + dev_err(cpu_dev, "failed to scale vddpu up: %d\n", ret); + return ret; + } + ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0); + if (ret) { + dev_err(cpu_dev, "failed to scale vddsoc up: %d\n", ret); + return ret; + } ret = regulator_set_voltage_tol(arm_reg, volt, 0); if (ret) { dev_err(cpu_dev, "failed to scale vddarm up: %d\n", ret); return ret; } - - /* - * Need to increase vddpu and vddsoc for safety - * if we are about to run at 1.2 GHz. - */ - if (new_freq == FREQ_1P2_GHZ / 1000) { - regulator_set_voltage_tol(pu_reg, - PU_SOC_VOLTAGE_HIGH, 0); - regulator_set_voltage_tol(soc_reg, - PU_SOC_VOLTAGE_HIGH, 0); - } } /* @@ -120,12 +122,15 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) "failed to scale vddarm down: %d\n", ret); ret = 0; } - - if (old_freq == FREQ_1P2_GHZ / 1000) { - regulator_set_voltage_tol(pu_reg, - PU_SOC_VOLTAGE_NORMAL, 0); - regulator_set_voltage_tol(soc_reg, - PU_SOC_VOLTAGE_NORMAL, 0); + ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0); + if (ret) { + dev_warn(cpu_dev, "failed to scale vddsoc down: %d\n", ret); + ret = 0; + } + ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0); + if (ret) { + dev_warn(cpu_dev, "failed to scale vddpu down: %d\n", ret); + ret = 0; } } @@ -153,6 +158,9 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) struct dev_pm_opp *opp; unsigned long min_volt, max_volt; int num, ret; + const struct property *prop; + const __be32 *val; + u32 nr, i, j; cpu_dev = get_cpu_device(0); if (!cpu_dev) { @@ -201,9 +209,61 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) goto put_node; } + /* Make imx6_soc_volt array's size same as arm opp number */ + imx6_soc_volt = devm_kzalloc(cpu_dev, sizeof(*imx6_soc_volt) * num, GFP_KERNEL); + if (imx6_soc_volt == NULL) { + ret = -ENOMEM; + goto free_freq_table; + } + + prop = of_find_property(np, "fsl,soc-operating-points", NULL); + if (!prop || !prop->value) + goto soc_opp_out; + + /* + * Each OPP is a set of tuples consisting of frequency and + * voltage like . + */ + nr = prop->length / sizeof(u32); + if (nr % 2 || (nr / 2) < num) + goto soc_opp_out; + + for (j = 0; j < num; j++) { + val = prop->value; + for (i = 0; i < nr / 2; i++) { + unsigned long freq = be32_to_cpup(val++); + unsigned long volt = be32_to_cpup(val++); + if (freq_table[j].frequency == freq) { + imx6_soc_volt[soc_opp_count++] = volt; + break; + } + } + } + +soc_opp_out: + /* use fixed soc opp volt if no valid soc opp info found in dtb */ + if (soc_opp_count != num) { + dev_warn(cpu_dev, "can NOT find valid fsl,soc-operating-points property in dtb, use default value!\n"); + for (j = 0; j < num; j++) + imx6_soc_volt[j] = PU_SOC_VOLTAGE_NORMAL; + if (freq_table[num - 1].frequency * 1000 == FREQ_1P2_GHZ) + imx6_soc_volt[num - 1] = PU_SOC_VOLTAGE_HIGH; + } + if (of_property_read_u32(np, "clock-latency", &transition_latency)) transition_latency = CPUFREQ_ETERNAL; + /* + * Calculate the ramp time for max voltage change in the + * VDDSOC and VDDPU regulators. + */ + ret = regulator_set_voltage_time(soc_reg, imx6_soc_volt[0], imx6_soc_volt[num - 1]); + if (ret > 0) + transition_latency += ret * 1000; + ret = regulator_set_voltage_time(pu_reg, imx6_soc_volt[0], imx6_soc_volt[num - 1]); + if (ret > 0) + transition_latency += ret * 1000; + /* * OPP is maintained in order of increasing frequency, and * freq_table initialised from OPP is therefore sorted in the @@ -221,18 +281,6 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) if (ret > 0) transition_latency += ret * 1000; - /* Count vddpu and vddsoc latency in for 1.2 GHz support */ - if (freq_table[num].frequency == FREQ_1P2_GHZ / 1000) { - ret = regulator_set_voltage_time(pu_reg, PU_SOC_VOLTAGE_NORMAL, - PU_SOC_VOLTAGE_HIGH); - if (ret > 0) - transition_latency += ret * 1000; - ret = regulator_set_voltage_time(soc_reg, PU_SOC_VOLTAGE_NORMAL, - PU_SOC_VOLTAGE_HIGH); - if (ret > 0) - transition_latency += ret * 1000; - } - ret = cpufreq_register_driver(&imx6q_cpufreq_driver); if (ret) { dev_err(cpu_dev, "failed register driver: %d\n", ret); -- cgit v1.2.1 From 20b7cbe2981013cbe449cc642cefc101e23d0f8e Mon Sep 17 00:00:00 2001 From: John Tobias Date: Thu, 19 Dec 2013 22:56:28 -0800 Subject: cpufreq: imx6q: add of_init_opp_table Add a routine check to see if the platform supplied the OPP table. Incase there's no OPP table exist, it will try to initialise it. It's been tested on iMX6SL board where the platform doesn't have an OPP table. Signed-off-by: John Tobias Acked-by: Shawn Guo Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/imx6q-cpufreq.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/cpufreq/imx6q-cpufreq.c') diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index c29198fbc9b7..564a26523ebc 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -195,12 +195,25 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) goto put_node; } - /* We expect an OPP table supplied by platform */ + /* + * We expect an OPP table supplied by platform. + * Just, incase the platform did not supply the OPP + * table, it will try to get it. + */ num = dev_pm_opp_get_opp_count(cpu_dev); if (num < 0) { - ret = num; - dev_err(cpu_dev, "no OPP table is found: %d\n", ret); - goto put_node; + ret = of_init_opp_table(cpu_dev); + if (ret < 0) { + dev_err(cpu_dev, "failed to init OPP table: %d\n", ret); + goto put_node; + } + + num = dev_pm_opp_get_opp_count(cpu_dev); + if (num < 0) { + ret = num; + dev_err(cpu_dev, "no OPP table is found: %d\n", ret); + goto put_node; + } } ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); -- cgit v1.2.1 From ae6b427132ba39d023e332e7d920e9931ff05313 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 3 Dec 2013 11:20:45 +0530 Subject: cpufreq: Mark ARM drivers with CPUFREQ_NEED_INITIAL_FREQ_CHECK flag Sometimes boot loaders set CPU frequency to a value outside of frequency table present with cpufreq core. In such cases CPU might be unstable if it has to run on that frequency for long duration of time and so its better to set it to a frequency which is specified in frequency table. On some systems we can't really say what frequency we're running at the moment and so for these we shouldn't check if we are running at a frequency present in frequency table. And so we really can't force this for all the cpufreq drivers. Hence we are created another flag here: CPUFREQ_NEED_INITIAL_FREQ_CHECK that will be marked by platforms which want to go for this check at boot time. Initially this is done for all ARM platforms but others may follow if required. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/imx6q-cpufreq.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/cpufreq/imx6q-cpufreq.c') diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 564a26523ebc..2938257b8c19 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -143,6 +143,7 @@ static int imx6q_cpufreq_init(struct cpufreq_policy *policy) } static struct cpufreq_driver imx6q_cpufreq_driver = { + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = imx6q_set_target, .get = imx6q_get_speed, -- cgit v1.2.1 From 652ed95d5fa6074b3c4ea245deb0691f1acb6656 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 9 Jan 2014 20:38:43 +0530 Subject: cpufreq: introduce cpufreq_generic_get() routine CPUFreq drivers that use clock frameworks interface,i.e. clk_get_rate(), to get CPUs clk rate, have similar sort of code used in most of them. This patch adds a generic ->get() which will do the same thing for them. All those drivers are required to now is to set .get to cpufreq_generic_get() and set their clk pointer in policy->clk during ->init(). Acked-by: Hans-Christian Egtvedt Acked-by: Shawn Guo Acked-by: Linus Walleij Acked-by: Shawn Guo Acked-by: Stephen Warren Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/imx6q-cpufreq.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/cpufreq/imx6q-cpufreq.c') diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 2938257b8c19..ce69059be1fc 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -38,11 +38,6 @@ static unsigned int transition_latency; static u32 *imx6_soc_volt; static u32 soc_opp_count; -static unsigned int imx6q_get_speed(unsigned int cpu) -{ - return clk_get_rate(arm_clk) / 1000; -} - static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) { struct dev_pm_opp *opp; @@ -139,6 +134,7 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) static int imx6q_cpufreq_init(struct cpufreq_policy *policy) { + policy->clk = arm_clk; return cpufreq_generic_init(policy, freq_table, transition_latency); } @@ -146,7 +142,7 @@ static struct cpufreq_driver imx6q_cpufreq_driver = { .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = imx6q_set_target, - .get = imx6q_get_speed, + .get = cpufreq_generic_get, .init = imx6q_cpufreq_init, .exit = cpufreq_generic_exit, .name = "imx6q-cpufreq", -- cgit v1.2.1