ipq806x: refresh 4.19 patches
[openwrt/openwrt.git] / target / linux / ipq806x / patches-4.19 / 0054-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch
1 From 10577f74c35bd395951d1b2382c8d821089b5745 Mon Sep 17 00:00:00 2001
2 From: Stephen Boyd <sboyd@codeaurora.org>
3 Date: Fri, 18 Sep 2015 17:52:08 -0700
4 Subject: [PATCH 54/69] cpufreq-dt: Handle OPP voltage adjust events
5
6 On some SoCs the Adaptive Voltage Scaling (AVS) technique is
7 employed to optimize the operating voltage of a device. At a
8 given frequency, the hardware monitors dynamic factors and either
9 makes a suggestion for how much to adjust a voltage for the
10 current frequency, or it automatically adjusts the voltage
11 without software intervention.
12
13 In the former case, an AVS driver will call
14 dev_pm_opp_modify_voltage() and update the voltage for the
15 particular OPP the CPUs are using. Add an OPP notifier to
16 cpufreq-dt so that we can adjust the voltage of the CPU when AVS
17 updates the OPP.
18
19 Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
20 Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
21 Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
22 ---
23 drivers/cpufreq/cpufreq-dt.c | 68 ++++++++++++++++++++++++++++++++++++++++++--
24 1 file changed, 65 insertions(+), 3 deletions(-)
25
26 --- a/drivers/cpufreq/cpufreq-dt.c
27 +++ b/drivers/cpufreq/cpufreq-dt.c
28 @@ -32,6 +32,9 @@ struct private_data {
29 struct device *cpu_dev;
30 struct thermal_cooling_device *cdev;
31 const char *reg_name;
32 + struct notifier_block opp_nb;
33 + struct mutex lock;
34 + unsigned long opp_freq;
35 bool have_static_opps;
36 };
37
38 @@ -47,12 +50,15 @@ static int set_target(struct cpufreq_pol
39 unsigned long freq = policy->freq_table[index].frequency;
40 int ret;
41
42 + mutex_lock(&priv->lock);
43 ret = dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000);
44
45 if (!ret) {
46 + priv->opp_freq = freq * 1000;
47 arch_set_freq_scale(policy->related_cpus, freq,
48 policy->cpuinfo.max_freq);
49 }
50 + mutex_unlock(&priv->lock);
51
52 return ret;
53 }
54 @@ -95,6 +101,39 @@ node_put:
55 return name;
56 }
57
58 +static int opp_notifier(struct notifier_block *nb, unsigned long event,
59 + void *data)
60 +{
61 + struct dev_pm_opp *opp = data;
62 + struct private_data *priv = container_of(nb, struct private_data,
63 + opp_nb);
64 + struct device *cpu_dev = priv->cpu_dev;
65 + struct regulator *cpu_reg;
66 + unsigned long volt, freq;
67 + int ret = 0;
68 +
69 + if (event == OPP_EVENT_ADJUST_VOLTAGE) {
70 + cpu_reg = dev_pm_opp_get_regulator(cpu_dev);
71 + if (IS_ERR(cpu_reg)) {
72 + ret = PTR_ERR(cpu_reg);
73 + goto out;
74 + }
75 + volt = dev_pm_opp_get_voltage(opp);
76 + freq = dev_pm_opp_get_freq(opp);
77 +
78 + mutex_lock(&priv->lock);
79 + if (freq == priv->opp_freq) {
80 + ret = regulator_set_voltage_triplet(cpu_reg, volt, volt, volt);
81 + }
82 + mutex_unlock(&priv->lock);
83 + if (ret)
84 + dev_err(cpu_dev, "failed to scale voltage: %d\n", ret);
85 + }
86 +
87 +out:
88 + return notifier_from_errno(ret);
89 +}
90 +
91 static int resources_available(void)
92 {
93 struct device *cpu_dev;
94 @@ -161,6 +200,7 @@ static int cpufreq_init(struct cpufreq_p
95 bool fallback = false;
96 const char *name;
97 int ret;
98 + struct srcu_notifier_head *opp_srcu_head;
99
100 cpu_dev = get_cpu_device(policy->cpu);
101 if (!cpu_dev) {
102 @@ -254,10 +294,13 @@ static int cpufreq_init(struct cpufreq_p
103 __func__, ret);
104 }
105
106 + mutex_init(&priv->lock);
107 + dev_pm_opp_register_notifier(cpu_dev, &priv->opp_nb);
108 +
109 ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
110 if (ret) {
111 dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
112 - goto out_free_opp;
113 + goto out_unregister_nb;
114 }
115
116 priv->cpu_dev = cpu_dev;
117 @@ -287,6 +330,8 @@ static int cpufreq_init(struct cpufreq_p
118
119 out_free_cpufreq_table:
120 dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
121 +out_unregister_nb:
122 + dev_pm_opp_unregister_notifier(cpu_dev, &priv->opp_nb);
123 out_free_opp:
124 if (priv->have_static_opps)
125 dev_pm_opp_of_cpumask_remove_table(policy->cpus);