147 lines
4.2 KiB
Diff
147 lines
4.2 KiB
Diff
diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c
|
|
index 2a82c36aec21..1747b03e3059 100644
|
|
--- a/drivers/cpufreq/mediatek-cpufreq.c
|
|
+++ b/drivers/cpufreq/mediatek-cpufreq.c
|
|
@@ -43,6 +43,10 @@ struct mtk_cpu_dvfs_info {
|
|
int intermediate_voltage;
|
|
bool need_voltage_tracking;
|
|
int old_vproc;
|
|
+ struct mutex lock; /* avoid notify and policy race condition */
|
|
+ struct notifier_block opp_nb;
|
|
+ int opp_cpu;
|
|
+ unsigned long opp_freq;
|
|
};
|
|
|
|
static LIST_HEAD(dvfs_info_list);
|
|
@@ -239,6 +243,7 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
|
|
vproc = dev_pm_opp_get_voltage(opp);
|
|
dev_pm_opp_put(opp);
|
|
|
|
+ mutex_lock(&info->lock);
|
|
/*
|
|
* If the new voltage or the intermediate voltage is higher than the
|
|
* current voltage, scale up voltage first.
|
|
@@ -250,6 +255,7 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
|
|
pr_err("cpu%d: failed to scale up voltage!\n",
|
|
policy->cpu);
|
|
mtk_cpufreq_set_voltage(info, old_vproc);
|
|
+ mutex_unlock(&info->lock);
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -261,6 +267,7 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
|
|
policy->cpu);
|
|
mtk_cpufreq_set_voltage(info, old_vproc);
|
|
WARN_ON(1);
|
|
+ mutex_unlock(&info->lock);
|
|
return ret;
|
|
}
|
|
|
|
@@ -271,6 +278,7 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
|
|
policy->cpu);
|
|
clk_set_parent(cpu_clk, armpll);
|
|
mtk_cpufreq_set_voltage(info, old_vproc);
|
|
+ mutex_unlock(&info->lock);
|
|
return ret;
|
|
}
|
|
|
|
@@ -281,6 +289,7 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
|
|
policy->cpu);
|
|
mtk_cpufreq_set_voltage(info, inter_vproc);
|
|
WARN_ON(1);
|
|
+ mutex_unlock(&info->lock);
|
|
return ret;
|
|
}
|
|
|
|
@@ -296,15 +305,69 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
|
|
clk_set_parent(cpu_clk, info->inter_clk);
|
|
clk_set_rate(armpll, old_freq_hz);
|
|
clk_set_parent(cpu_clk, armpll);
|
|
+ mutex_unlock(&info->lock);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
+ info->opp_freq = freq_hz;
|
|
+ mutex_unlock(&info->lock);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
#define DYNAMIC_POWER "dynamic-power-coefficient"
|
|
|
|
+static int mtk_cpufreq_opp_notifier(struct notifier_block *nb,
|
|
+ unsigned long event, void *data)
|
|
+{
|
|
+ struct dev_pm_opp *opp = data;
|
|
+ struct dev_pm_opp *new_opp;
|
|
+ struct mtk_cpu_dvfs_info *info;
|
|
+ unsigned long freq, volt;
|
|
+ struct cpufreq_policy *policy;
|
|
+ int ret = 0;
|
|
+
|
|
+ info = container_of(nb, struct mtk_cpu_dvfs_info, opp_nb);
|
|
+
|
|
+ if (event == OPP_EVENT_ADJUST_VOLTAGE) {
|
|
+ freq = dev_pm_opp_get_freq(opp);
|
|
+
|
|
+ mutex_lock(&info->lock);
|
|
+ if (info->opp_freq == freq) {
|
|
+ volt = dev_pm_opp_get_voltage(opp);
|
|
+ ret = mtk_cpufreq_set_voltage(info, volt);
|
|
+ if (ret)
|
|
+ dev_err(info->cpu_dev, "failed to scale voltage: %d\n",
|
|
+ ret);
|
|
+ }
|
|
+ mutex_unlock(&info->lock);
|
|
+ } else if (event == OPP_EVENT_DISABLE) {
|
|
+ freq = dev_pm_opp_get_freq(opp);
|
|
+ /* case of current opp item is disabled */
|
|
+ if (info->opp_freq == freq) {
|
|
+ freq = 1;
|
|
+ new_opp = dev_pm_opp_find_freq_ceil(info->cpu_dev,
|
|
+ &freq);
|
|
+ if (!IS_ERR(new_opp)) {
|
|
+ dev_pm_opp_put(new_opp);
|
|
+ policy = cpufreq_cpu_get(info->opp_cpu);
|
|
+ if (policy) {
|
|
+ cpufreq_driver_target(policy,
|
|
+ freq / 1000,
|
|
+ CPUFREQ_RELATION_L);
|
|
+ cpufreq_cpu_put(policy);
|
|
+ }
|
|
+ } else {
|
|
+ pr_err("%s: all opp items are disabled\n",
|
|
+ __func__);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return notifier_from_errno(ret);
|
|
+}
|
|
+
|
|
static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
|
|
{
|
|
struct device *cpu_dev;
|
|
@@ -400,11 +463,21 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
|
|
info->intermediate_voltage = dev_pm_opp_get_voltage(opp);
|
|
dev_pm_opp_put(opp);
|
|
|
|
+ info->opp_cpu = cpu;
|
|
+ info->opp_nb.notifier_call = mtk_cpufreq_opp_notifier;
|
|
+ ret = dev_pm_opp_register_notifier(cpu_dev, &info->opp_nb);
|
|
+ if (ret) {
|
|
+ pr_warn("cannot register opp notification\n");
|
|
+ goto out_disable_inter_clock;
|
|
+ }
|
|
+
|
|
+ mutex_init(&info->lock);
|
|
info->cpu_dev = cpu_dev;
|
|
info->proc_reg = proc_reg;
|
|
info->sram_reg = IS_ERR(sram_reg) ? NULL : sram_reg;
|
|
info->cpu_clk = cpu_clk;
|
|
info->inter_clk = inter_clk;
|
|
+ info->opp_freq = clk_get_rate(cpu_clk);
|
|
|
|
/*
|
|
* If SRAM regulator is present, software "voltage tracking" is needed
|