From 045ee45c4ff2422a6f47e9fad7dd6cb537de940c Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 24 Oct 2014 15:05:55 +0200 Subject: cpufreq: cpufreq-dt: disable unsupported OPPs If the regulator connected to the CPU voltage plane doesn't support an OPP specified voltage with the acceptable tolerance it's better to just disable the OPP instead of constantly failing the voltage scaling later on. Includes a fix to move initialization of opp_freq outside the loop to avoid an endless loop from Geert Uytterhoeven. Signed-off-by: Geert Uytterhoeven Signed-off-by: Lucas Stach Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-dt.c | 66 +++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 25 deletions(-) (limited to 'drivers/cpufreq/cpufreq-dt.c') diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 92c162af5045..7789affa7eb8 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -187,6 +187,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) struct device *cpu_dev; struct regulator *cpu_reg; struct clk *cpu_clk; + unsigned long min_uV = ~0, max_uV = 0; unsigned int transition_latency; int ret; @@ -206,16 +207,10 @@ static int cpufreq_init(struct cpufreq_policy *policy) /* OPPs might be populated at runtime, don't check for error here */ of_init_opp_table(cpu_dev); - ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); - if (ret) { - dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); - goto out_put_node; - } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { ret = -ENOMEM; - goto out_free_table; + goto out_put_node; } of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance); @@ -224,30 +219,51 @@ static int cpufreq_init(struct cpufreq_policy *policy) transition_latency = CPUFREQ_ETERNAL; if (!IS_ERR(cpu_reg)) { - struct dev_pm_opp *opp; - unsigned long min_uV, max_uV; - int i; + unsigned long opp_freq = 0; /* - * OPP is maintained in order of increasing frequency, and - * freq_table initialised from OPP is therefore sorted in the - * same order. + * Disable any OPPs where the connected regulator isn't able to + * provide the specified voltage and record minimum and maximum + * voltage levels. */ - for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) - ; - rcu_read_lock(); - opp = dev_pm_opp_find_freq_exact(cpu_dev, - freq_table[0].frequency * 1000, true); - min_uV = dev_pm_opp_get_voltage(opp); - opp = dev_pm_opp_find_freq_exact(cpu_dev, - freq_table[i-1].frequency * 1000, true); - max_uV = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); + while (1) { + struct dev_pm_opp *opp; + unsigned long opp_uV, tol_uV; + + rcu_read_lock(); + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &opp_freq); + if (IS_ERR(opp)) { + rcu_read_unlock(); + break; + } + opp_uV = dev_pm_opp_get_voltage(opp); + rcu_read_unlock(); + + tol_uV = opp_uV * priv->voltage_tolerance / 100; + if (regulator_is_supported_voltage(cpu_reg, opp_uV, + opp_uV + tol_uV)) { + if (opp_uV < min_uV) + min_uV = opp_uV; + if (opp_uV > max_uV) + max_uV = opp_uV; + } else { + dev_pm_opp_disable(cpu_dev, opp_freq); + } + + opp_freq++; + } + ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV); if (ret > 0) transition_latency += ret * 1000; } + ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); + if (ret) { + pr_err("failed to init cpufreq table: %d\n", ret); + goto out_free_priv; + } + /* * For now, just loading the cooling device; * thermal DT code takes care of matching them. @@ -286,9 +302,9 @@ static int cpufreq_init(struct cpufreq_policy *policy) out_cooling_unregister: cpufreq_cooling_unregister(priv->cdev); - kfree(priv); -out_free_table: dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); +out_free_priv: + kfree(priv); out_put_node: of_node_put(np); out_put_reg_clk: -- cgit v1.2.1 From c81407fe573d8ac3c7150f5373475598c59197de Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 27 Oct 2014 14:44:40 +0100 Subject: cpufreq: cpufreq-dt: Restore default cpumask_setall(policy->cpus) Commit 34e5a5273d6aa0ee ("cpufreq: cpufreq-dt: extend with platform_data") changed cpufreq_init() to only call cpumask_setall(policy->cpus) if the platform data indicates that all CPUs share the same clock. Before, cpufreq_generic_init() did this unconditionally. This causes a crash on r8a7791/koelsch when resuming from s2ram: Enabling non-boot CPUs ... CPU1: Booted secondary processor Unable to handle kernel NULL pointer dereference at virtual address 0000003c pgd = ee71f980 [0000003c] *pgd=6eeb6003, *pmd=6e0e9003, *pte=00000000 Internal error: Oops: a07 [#1] SMP ARM Modules linked in: CPU: 0 PID: 1397 Comm: s2ram Tainted: G W 3.18.0-rc2-koelsch-00762-g7eed2a4e61d2d978 #581 task: ee6b76c0 ti: ee7f0000 task.ti: ee7f0000 PC is at __cpufreq_add_dev.isra.24+0x24c/0x77c LR is at __cpufreq_add_dev.isra.24+0x244/0x77c pc : [] lr : [] psr: 60000153 sp : ee7f1d48 ip : ee7f1d48 fp : ee7f1d84 r10: c04e8448 r9 : 00000000 r8 : 00000001 r7 : c054a8c4 r6 : 00000001 r5 : 00000001 r4 : 00000000 r3 : 00000000 r2 : 00000000 r1 : 20000153 r0 : c054a950 Flags: nZCv IRQs on FIQs off Mode SVC_32 ISA ARM Segment user Control: 30c5307d Table: 6e71f980 DAC: fffffffd Process s2ram (pid: 1397, stack limit = 0xee7f0240) ... Backtrace: [] (__cpufreq_add_dev.isra.24) from [] (cpufreq_cpu_callback+0x6c/0x74) r10:eec75240 r9:c04e8448 r8:c04ef3a0 r7:00000001 r6:00000012 r5:00000000 r4:00000012 [] (cpufreq_cpu_callback) from [] (notifier_call_chain+0x48/0x70) r4:ffffffdd r3:c029e5b4 [] (notifier_call_chain) from [] (__raw_notifier_call_chain+0x1c/0x24) r8:00000001 r7:00000010 r6:00000000 r5:00000000 r4:00000012 r3:ffffffff [] (__raw_notifier_call_chain) from [] (__cpu_notify+0x34/0x50) [] (__cpu_notify) from [] (cpu_notify+0x18/0x1c) r4:00000001 [] (cpu_notify) from [] (_cpu_up+0x108/0x144) [] (_cpu_up) from [] (enable_nonboot_cpus+0x68/0xb8) r10:00000000 r9:c04e8ee6 r8:00000000 r7:00000003 r6:c04e8528 r5:c0506248 r4:00000001 [] (enable_nonboot_cpus) from [] (suspend_devices_and_enter+0x29c/0x3e8) r6:c0506e70 r5:00000000 r4:00000000 r3:60000153 Restore the old default of calling cpumask_setall(policy->cpus) if no platform data is available to fix this. Fixes: 34e5a5273d6aa0ee (cpufreq: cpufreq-dt: extend with platform_data) Signed-off-by: Geert Uytterhoeven Reviewed-by: Thomas Petazzoni Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-dt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/cpufreq/cpufreq-dt.c') diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 7789affa7eb8..23aaf40cf37f 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -293,7 +293,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = transition_latency; pd = cpufreq_get_driver_data(); - if (pd && !pd->independent_clocks) + if (!pd || !pd->independent_clocks) cpumask_setall(policy->cpus); of_node_put(np); -- cgit v1.2.1