ef48e9f01b89826912e9ebcb22b7c392013c2d06
[openwrt/openwrt.git] / target / linux / ipq806x / patches-4.14 / 0049-PM-OPP-Support-adjusting-OPP-voltages-at-runtime.patch
1 From c949f08cf20fe82971fbdb4015daa38210da492e Mon Sep 17 00:00:00 2001
2 From: Stephen Boyd <sboyd@codeaurora.org>
3 Date: Fri, 18 Sep 2015 17:52:06 -0700
4 Subject: [PATCH 49/69] PM / OPP: Support adjusting OPP voltages at runtime
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. Add an API to the OPP library for
12 the former case, so that AVS type devices can update the voltages
13 for an OPP when the hardware determines the voltage should
14 change. The assumption is that drivers like CPUfreq or devfreq
15 will register for the OPP notifiers and adjust the voltage
16 according to suggestions that AVS makes.
17
18 Cc: Nishanth Menon <nm@ti.com>
19 Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
20 Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
21 Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
22 Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
23 ---
24 drivers/base/power/opp/core.c | 77 +++++++++++++++++++++++++++++++++++++++++++
25 include/linux/pm_opp.h | 11 +++++++
26 2 files changed, 88 insertions(+)
27
28 --- a/drivers/base/power/opp/core.c
29 +++ b/drivers/base/power/opp/core.c
30 @@ -1608,6 +1608,83 @@ put_table:
31 }
32
33 /**
34 + * dev_pm_opp_adjust_voltage() - helper to change the voltage of an OPP
35 + * @dev: device for which we do this operation
36 + * @freq: OPP frequency to adjust voltage of
37 + * @u_volt: new OPP voltage
38 + *
39 + * Change the voltage of an OPP with an RCU operation.
40 + *
41 + * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the
42 + * copy operation, returns 0 if no modifcation was done OR modification was
43 + * successful.
44 + *
45 + * Locking: The internal device_opp and opp structures are RCU protected.
46 + * Hence this function internally uses RCU updater strategy with mutex locks to
47 + * keep the integrity of the internal data structures. Callers should ensure
48 + * that this function is *NOT* called under RCU protection or in contexts where
49 + * mutex locking or synchronize_rcu() blocking calls cannot be used.
50 + */
51 +int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
52 + unsigned long u_volt)
53 +{
54 + struct opp_table *opp_table;
55 + struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV);
56 + int r = 0;
57 +
58 + /* keep the node allocated */
59 + new_opp = kmalloc(sizeof(*new_opp), GFP_KERNEL);
60 + if (!new_opp)
61 + return -ENOMEM;
62 +
63 + mutex_lock(&opp_table_lock);
64 +
65 + /* Find the opp_table */
66 + opp_table = _find_opp_table(dev);
67 + if (IS_ERR(opp_table)) {
68 + r = PTR_ERR(opp_table);
69 + dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r);
70 + goto unlock;
71 + }
72 +
73 + /* Do we have the frequency? */
74 + list_for_each_entry(tmp_opp, &opp_table->opp_list, node) {
75 + if (tmp_opp->rate == freq) {
76 + opp = tmp_opp;
77 + break;
78 + }
79 + }
80 + if (IS_ERR(opp)) {
81 + r = PTR_ERR(opp);
82 + goto unlock;
83 + }
84 +
85 + /* Is update really needed? */
86 + if (opp->supplies[0].u_volt == u_volt)
87 + goto unlock;
88 + /* copy the old data over */
89 + *new_opp = *opp;
90 +
91 + /* plug in new node */
92 + new_opp->supplies[0].u_volt = u_volt;
93 +
94 + list_replace(&opp->node, &new_opp->node);
95 + mutex_unlock(&opp_table_lock);
96 + kfree(opp);
97 +
98 + /* Notify the change of the OPP */
99 + blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADJUST_VOLTAGE,
100 + new_opp);
101 +
102 + return 0;
103 +
104 +unlock:
105 + mutex_unlock(&opp_table_lock);
106 + kfree(new_opp);
107 + return r;
108 +}
109 +
110 +/**
111 * dev_pm_opp_enable() - Enable a specific OPP
112 * @dev: device for which we do this operation
113 * @freq: OPP frequency to enable
114 --- a/include/linux/pm_opp.h
115 +++ b/include/linux/pm_opp.h
116 @@ -25,6 +25,7 @@ struct opp_table;
117
118 enum dev_pm_opp_event {
119 OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
120 + OPP_EVENT_ADJUST_VOLTAGE,
121 };
122
123 /**
124 @@ -108,6 +109,9 @@ int dev_pm_opp_add(struct device *dev, u
125 unsigned long u_volt);
126 void dev_pm_opp_remove(struct device *dev, unsigned long freq);
127
128 +int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
129 + unsigned long u_volt);
130 +
131 int dev_pm_opp_enable(struct device *dev, unsigned long freq);
132
133 int dev_pm_opp_disable(struct device *dev, unsigned long freq);
134 @@ -208,6 +212,13 @@ static inline void dev_pm_opp_remove(str
135 {
136 }
137
138 +static inline int
139 +dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
140 + unsigned long u_volt)
141 +{
142 + return 0;
143 +}
144 +
145 static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq)
146 {
147 return 0;