OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 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 | @@ -44,9 +47,16 @@ static struct freq_attr *cpufreq_dt_attr |
||
39 | static int set_target(struct cpufreq_policy *policy, unsigned int index) |
||
40 | { |
||
41 | struct private_data *priv = policy->driver_data; |
||
42 | + int ret; |
||
43 | + unsigned long target_freq = policy->freq_table[index].frequency * 1000; |
||
44 | + |
||
45 | + mutex_lock(&priv->lock); |
||
46 | + ret = dev_pm_opp_set_rate(priv->cpu_dev, target_freq); |
||
47 | + if (!ret) |
||
48 | + priv->opp_freq = target_freq; |
||
49 | + mutex_unlock(&priv->lock); |
||
50 | |||
51 | - return dev_pm_opp_set_rate(priv->cpu_dev, |
||
52 | - policy->freq_table[index].frequency * 1000); |
||
53 | + return ret; |
||
54 | } |
||
55 | |||
56 | /* |
||
57 | @@ -87,6 +97,39 @@ node_put: |
||
58 | return name; |
||
59 | } |
||
60 | |||
61 | +static int opp_notifier(struct notifier_block *nb, unsigned long event, |
||
62 | + void *data) |
||
63 | +{ |
||
64 | + struct dev_pm_opp *opp = data; |
||
65 | + struct private_data *priv = container_of(nb, struct private_data, |
||
66 | + opp_nb); |
||
67 | + struct device *cpu_dev = priv->cpu_dev; |
||
68 | + struct regulator *cpu_reg; |
||
69 | + unsigned long volt, freq; |
||
70 | + int ret = 0; |
||
71 | + |
||
72 | + if (event == OPP_EVENT_ADJUST_VOLTAGE) { |
||
73 | + cpu_reg = dev_pm_opp_get_regulator(cpu_dev); |
||
74 | + if (IS_ERR(cpu_reg)) { |
||
75 | + ret = PTR_ERR(cpu_reg); |
||
76 | + goto out; |
||
77 | + } |
||
78 | + volt = dev_pm_opp_get_voltage(opp); |
||
79 | + freq = dev_pm_opp_get_freq(opp); |
||
80 | + |
||
81 | + mutex_lock(&priv->lock); |
||
82 | + if (freq == priv->opp_freq) { |
||
83 | + ret = regulator_set_voltage_triplet(cpu_reg, volt, volt, volt); |
||
84 | + } |
||
85 | + mutex_unlock(&priv->lock); |
||
86 | + if (ret) |
||
87 | + dev_err(cpu_dev, "failed to scale voltage: %d\n", ret); |
||
88 | + } |
||
89 | + |
||
90 | +out: |
||
91 | + return notifier_from_errno(ret); |
||
92 | +} |
||
93 | + |
||
94 | static int resources_available(void) |
||
95 | { |
||
96 | struct device *cpu_dev; |
||
97 | @@ -153,6 +196,7 @@ static int cpufreq_init(struct cpufreq_p |
||
98 | bool fallback = false; |
||
99 | const char *name; |
||
100 | int ret; |
||
101 | + struct srcu_notifier_head *opp_srcu_head; |
||
102 | |||
103 | cpu_dev = get_cpu_device(policy->cpu); |
||
104 | if (!cpu_dev) { |
||
105 | @@ -246,10 +290,13 @@ static int cpufreq_init(struct cpufreq_p |
||
106 | __func__, ret); |
||
107 | } |
||
108 | |||
109 | + mutex_init(&priv->lock); |
||
110 | + dev_pm_opp_register_notifier(cpu_dev, &priv->opp_nb); |
||
111 | + |
||
112 | ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); |
||
113 | if (ret) { |
||
114 | dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); |
||
115 | - goto out_free_opp; |
||
116 | + goto out_unregister_nb; |
||
117 | } |
||
118 | |||
119 | priv->cpu_dev = cpu_dev; |
||
120 | @@ -285,6 +332,8 @@ static int cpufreq_init(struct cpufreq_p |
||
121 | |||
122 | out_free_cpufreq_table: |
||
123 | dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); |
||
124 | +out_unregister_nb: |
||
125 | + dev_pm_opp_unregister_notifier(cpu_dev, &priv->opp_nb); |
||
126 | out_free_opp: |
||
127 | if (priv->have_static_opps) |
||
128 | dev_pm_opp_of_cpumask_remove_table(policy->cpus); |