1 From 175329015c8a0b480240da222822d2f8316f074d Mon Sep 17 00:00:00 2001
2 From: Stephen Boyd <sboyd@codeaurora.org>
3 Date: Mon, 1 Jun 2015 18:47:58 -0700
4 Subject: cpufreq-dt: Handle OPP voltage adjust events
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.
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
19 Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
21 drivers/cpufreq/cpufreq-dt.c | 72 ++++++++++++++++++++++++++++++++++++++++----
22 1 file changed, 66 insertions(+), 6 deletions(-)
24 --- a/drivers/cpufreq/cpufreq-dt.c
25 +++ b/drivers/cpufreq/cpufreq-dt.c
26 @@ -34,6 +34,9 @@ struct private_data {
27 struct regulator *cpu_reg;
28 struct thermal_cooling_device *cdev;
29 unsigned int voltage_tolerance; /* in percentage */
30 + struct notifier_block opp_nb;
32 + unsigned long opp_freq;
35 static struct freq_attr *cpufreq_dt_attr[] = {
36 @@ -42,6 +45,42 @@ static struct freq_attr *cpufreq_dt_attr
40 +static int opp_notifier(struct notifier_block *nb, unsigned long event,
43 + struct dev_pm_opp *opp = data;
44 + struct private_data *priv = container_of(nb, struct private_data,
46 + struct device *cpu_dev = priv->cpu_dev;
47 + struct regulator *cpu_reg = priv->cpu_reg;
48 + unsigned long volt, tol, freq;
52 + case OPP_EVENT_ADJUST_VOLTAGE:
53 + volt = dev_pm_opp_get_voltage(opp);
54 + freq = dev_pm_opp_get_freq(opp);
55 + tol = volt * priv->voltage_tolerance / 100;
57 + mutex_lock(&priv->lock);
58 + if (freq == priv->opp_freq)
59 + ret = regulator_set_voltage_tol(cpu_reg, volt,
61 + mutex_unlock(&priv->lock);
64 + "failed to scale voltage up: %d\n",
76 static int set_target(struct cpufreq_policy *policy, unsigned int index)
78 struct dev_pm_opp *opp;
79 @@ -53,6 +92,7 @@ static int set_target(struct cpufreq_pol
80 unsigned long volt = 0, volt_old = 0, tol = 0;
81 unsigned int old_freq, new_freq;
82 long freq_Hz, freq_exact;
83 + unsigned long opp_freq = 0;
86 freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000);
87 @@ -63,8 +103,8 @@ static int set_target(struct cpufreq_pol
88 new_freq = freq_Hz / 1000;
89 old_freq = clk_get_rate(cpu_clk) / 1000;
91 + mutex_lock(&priv->lock);
92 if (!IS_ERR(cpu_reg)) {
93 - unsigned long opp_freq;
96 opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
97 @@ -72,7 +112,8 @@ static int set_target(struct cpufreq_pol
99 dev_err(cpu_dev, "failed to find OPP for %ld\n",
101 - return PTR_ERR(opp);
102 + ret = PTR_ERR(opp);
105 volt = dev_pm_opp_get_voltage(opp);
106 opp_freq = dev_pm_opp_get_freq(opp);
107 @@ -93,7 +134,7 @@ static int set_target(struct cpufreq_pol
109 dev_err(cpu_dev, "failed to scale voltage up: %d\n",
116 @@ -102,7 +143,7 @@ static int set_target(struct cpufreq_pol
117 dev_err(cpu_dev, "failed to set clock rate: %d\n", ret);
118 if (!IS_ERR(cpu_reg) && volt_old > 0)
119 regulator_set_voltage_tol(cpu_reg, volt_old, tol);
124 /* scaling down? scale voltage after frequency */
125 @@ -112,9 +153,12 @@ static int set_target(struct cpufreq_pol
126 dev_err(cpu_dev, "failed to scale voltage down: %d\n",
128 clk_set_rate(cpu_clk, old_freq * 1000);
133 + priv->opp_freq = opp_freq;
135 + mutex_unlock(&priv->lock);
139 @@ -201,6 +245,7 @@ static int cpufreq_init(struct cpufreq_p
140 unsigned int transition_latency;
141 bool need_update = false;
143 + struct srcu_notifier_head *opp_srcu_head;
145 ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk);
147 @@ -277,6 +322,19 @@ static int cpufreq_init(struct cpufreq_p
151 + mutex_init(&priv->lock);
153 + opp_srcu_head = dev_pm_opp_get_notifier(cpu_dev);
154 + if (IS_ERR(opp_srcu_head)) {
155 + ret = PTR_ERR(opp_srcu_head);
156 + goto out_free_priv;
159 + priv->opp_nb.notifier_call = opp_notifier;
160 + ret = srcu_notifier_chain_register(opp_srcu_head, &priv->opp_nb);
162 + goto out_free_priv;
164 of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance);
166 if (!transition_latency)
167 @@ -326,7 +384,7 @@ static int cpufreq_init(struct cpufreq_p
168 ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
170 pr_err("failed to init cpufreq table: %d\n", ret);
171 - goto out_free_priv;
172 + goto out_unregister_nb;
175 priv->cpu_dev = cpu_dev;
176 @@ -365,6 +423,8 @@ static int cpufreq_init(struct cpufreq_p
178 out_free_cpufreq_table:
179 dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
181 + srcu_notifier_chain_unregister(opp_srcu_head, &priv->opp_nb);