@@ -59,6 +59,7 @@ struct qcom_cpufreq_data {
bool per_core_dcvs;
unsigned long cpu_hw_rate;
unsigned long xo_rate;
+ int opp_token;
};
static bool icc_scaling_enabled;
@@ -162,6 +163,15 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy,
return policy->freq_table[index].frequency;
}
+static int qcom_cpufreq_hw_config_clks_nop(struct device *dev,
+ struct opp_table *opp_table,
+ struct dev_pm_opp *opp, void *data,
+ bool scaling_down)
+{
+ /* We want to skip clk configuration via dev_pm_opp_set_opp() */
+ return 0;
+}
+
static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
struct cpufreq_policy *policy)
{
@@ -173,11 +183,23 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
int ret;
struct qcom_cpufreq_data *drv_data = policy->driver_data;
const struct qcom_cpufreq_soc_data *soc_data = drv_data->soc_data;
+ const char * const clk_names[] = { "xo", NULL };
+ struct dev_pm_opp_config config = {
+ .clk_names = clk_names,
+ .config_clks = qcom_cpufreq_hw_config_clks_nop,
+ };
table = kcalloc(LUT_MAX_ENTRIES + 1, sizeof(*table), GFP_KERNEL);
if (!table)
return -ENOMEM;
+ ret = dev_pm_opp_set_config(cpu_dev, &config);
+ if (ret < 0) {
+ dev_err(cpu_dev, "Failed to set OPP config: %d\n", ret);
+ goto free_table;
+ }
+ drv_data->opp_token = ret;
+
ret = dev_pm_opp_of_add_table(cpu_dev);
if (!ret) {
/* Disable all opps and cross-validate against LUT later */
@@ -192,7 +214,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
}
} else if (ret != -ENODEV) {
dev_err(cpu_dev, "Invalid opp table in device tree\n");
- return ret;
+ goto clear_config;
} else {
policy->fast_switch_possible = true;
icc_scaling_enabled = false;
@@ -260,6 +282,13 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
return 0;
+
+clear_config:
+ dev_pm_opp_clear_config(drv_data->opp_token);
+
+free_table:
+ kfree(table);
+ return ret;
}
static void qcom_get_related_cpus(int index, struct cpumask *m)
@@ -614,6 +643,7 @@ static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy)
dev_pm_opp_remove_all_dynamic(cpu_dev);
dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
qcom_cpufreq_hw_lmh_exit(data);
+ dev_pm_opp_clear_config(data->opp_token);
kfree(policy->freq_table);
kfree(data);
iounmap(base);
There is a corner case with Qcom, where we want to skip clk configuration that happens via dev_pm_opp_set_opp(), but still want the OPP core to read the "opp-hz" property so we can find the right OPP via freq finding helpers. The OPP core provides support for the platforms to provide config_clks helpers now, lets use that to provide an empty callback to skip clock configuration. The "table" wasn't getting freed properly on error, fix it as well which we are updating the code. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> --- drivers/cpufreq/qcom-cpufreq-hw.c | 32 ++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)