ipq806x: opp/core: update patch allowing adjusting of OPP voltages at runtime
[openwrt/staging/ldir.git] / target / linux / ipq806x / patches-4.19 / 0049-PM-OPP-Support-adjusting-OPP-voltages-at-runtime.patch
1 From: Sylwester Nawrocki <s.nawrocki@samsung.com>
2 To: krzk@kernel.org, vireshk@kernel.org, robh+dt@kernel.org
3 Cc: sboyd@kernel.org, roger.lu@mediatek.com,
4 linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
5 linux-samsung-soc@vger.kernel.org, devicetree@vger.kernel.org,
6 b.zolnierkie@samsung.com, m.szyprowski@samsung.com,
7 Stephen Boyd <sboyd@codeaurora.org>,
8 Sylwester Nawrocki <s.nawrocki@samsung.com>
9 Subject: [PATCH v5 1/4] PM / OPP: Support adjusting OPP voltages at runtime
10 Date: Wed, 16 Oct 2019 16:57:53 +0200
11 Message-ID: <20191016145756.16004-2-s.nawrocki@samsung.com> (raw)
12 In-Reply-To: <20191016145756.16004-1-s.nawrocki@samsung.com>
13
14 From: Stephen Boyd <sboyd@codeaurora.org>
15
16 On some SoCs the Adaptive Voltage Scaling (AVS) technique is
17 employed to optimize the operating voltage of a device. At a
18 given frequency, the hardware monitors dynamic factors and either
19 makes a suggestion for how much to adjust a voltage for the
20 current frequency, or it automatically adjusts the voltage
21 without software intervention. Add an API to the OPP library for
22 the former case, so that AVS type devices can update the voltages
23 for an OPP when the hardware determines the voltage should
24 change. The assumption is that drivers like CPUfreq or devfreq
25 will register for the OPP notifiers and adjust the voltage
26 according to suggestions that AVS makes.
27
28 This patch is derived from [1] submitted by Stephen.
29 [1] https://lore.kernel.org/patchwork/patch/599279/
30
31 Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
32 Signed-off-by: Roger Lu <roger.lu@mediatek.com>
33 [s.nawrocki@samsung.com: added handling of OPP min/max voltage]
34 Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
35 ---
36 drivers/opp/core.c | 69 ++++++++++++++++++++++++++++++++++++++++++
37 include/linux/pm_opp.h | 13 ++++++++
38 2 files changed, 82 insertions(+)
39
40 diff --git a/drivers/opp/core.c b/drivers/opp/core.c
41 index 3b7ffd0234e9..f38b3be85072 100644
42 --- a/drivers/opp/core.c
43 +++ b/drivers/opp/core.c
44 @@ -2112,6 +2112,75 @@ static int _opp_set_availability(struct device *dev, unsigned long freq,
45 return r;
46 }
47
48 +/**
49 + * dev_pm_opp_adjust_voltage() - helper to change the voltage of an OPP
50 + * @dev: device for which we do this operation
51 + * @freq: OPP frequency to adjust voltage of
52 + * @u_volt: new OPP target voltage
53 + * @u_volt_min: new OPP min voltage
54 + * @u_volt_max: new OPP max voltage
55 + *
56 + * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the
57 + * copy operation, returns 0 if no modifcation was done OR modification was
58 + * successful.
59 + */
60 +int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
61 + unsigned long u_volt, unsigned long u_volt_min,
62 + unsigned long u_volt_max)
63 +
64 +{
65 + struct opp_table *opp_table;
66 + struct dev_pm_opp *tmp_opp, *opp = ERR_PTR(-ENODEV);
67 + int r = 0;
68 +
69 + /* Find the opp_table */
70 + opp_table = _find_opp_table(dev);
71 + if (IS_ERR(opp_table)) {
72 + r = PTR_ERR(opp_table);
73 + dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r);
74 + return r;
75 + }
76 +
77 + mutex_lock(&opp_table->lock);
78 +
79 + /* Do we have the frequency? */
80 + list_for_each_entry(tmp_opp, &opp_table->opp_list, node) {
81 + if (tmp_opp->rate == freq) {
82 + opp = tmp_opp;
83 + break;
84 + }
85 + }
86 +
87 + if (IS_ERR(opp)) {
88 + r = PTR_ERR(opp);
89 + goto adjust_unlock;
90 + }
91 +
92 + /* Is update really needed? */
93 + if (opp->supplies->u_volt == u_volt)
94 + goto adjust_unlock;
95 +
96 + opp->supplies->u_volt = u_volt;
97 + opp->supplies->u_volt_min = u_volt_min;
98 + opp->supplies->u_volt_max = u_volt_max;
99 +
100 + dev_pm_opp_get(opp);
101 + mutex_unlock(&opp_table->lock);
102 +
103 + /* Notify the voltage change of the OPP */
104 + blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADJUST_VOLTAGE,
105 + opp);
106 +
107 + dev_pm_opp_put(opp);
108 + goto adjust_put_table;
109 +
110 +adjust_unlock:
111 + mutex_unlock(&opp_table->lock);
112 +adjust_put_table:
113 + dev_pm_opp_put_opp_table(opp_table);
114 + return r;
115 +}
116 +
117 /**
118 * dev_pm_opp_enable() - Enable a specific OPP
119 * @dev: device for which we do this operation
120 diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
121 index b8197ab014f2..747861816f4f 100644
122 --- a/include/linux/pm_opp.h
123 +++ b/include/linux/pm_opp.h
124 @@ -22,6 +22,7 @@ struct opp_table;
125
126 enum dev_pm_opp_event {
127 OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
128 + OPP_EVENT_ADJUST_VOLTAGE,
129 };
130
131 /**
132 @@ -113,6 +114,10 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq,
133 void dev_pm_opp_remove(struct device *dev, unsigned long freq);
134 void dev_pm_opp_remove_all_dynamic(struct device *dev);
135
136 +int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
137 + unsigned long u_volt, unsigned long u_volt_min,
138 + unsigned long u_volt_max);
139 +
140 int dev_pm_opp_enable(struct device *dev, unsigned long freq);
141
142 int dev_pm_opp_disable(struct device *dev, unsigned long freq);
143 @@ -242,6 +247,14 @@ static inline void dev_pm_opp_remove_all_dynamic(struct device *dev)
144 {
145 }
146
147 +static inline int
148 +dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
149 + unsigned long u_volt, unsigned long u_volt_min,
150 + unsigned long u_volt_max)
151 +{
152 + return 0;
153 +}
154 +
155 static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq)
156 {
157 return 0;