Mirror only - Please move to https://github.com/immortalwrt/immortalwrt
修订版 | 3a911b8c2d97293f04288775599843ab5cb77045 (tree) |
---|---|
时间 | 2022-10-12 04:28:42 |
作者 | Christian Marangi <ansuelsmth@gmai...> |
Commiter | Christian Marangi |
ipq806x: 5.15: backport devfreq new cpufreq based PASSIVE governor
Backport devfreq new cpufreq based PASSIVE governor needed for devfreq
based fab and cache scaling.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
@@ -0,0 +1,113 @@ | ||
1 | +From 713472e53e6e53c985e283782b0fd76b8ecfd47e Mon Sep 17 00:00:00 2001 | |
2 | +From: Chanwoo Choi <cw00.choi@samsung.com> | |
3 | +Date: Mon, 1 Mar 2021 02:07:29 +0900 | |
4 | +Subject: [PATCH 1/5] PM / devfreq: Export devfreq_get_freq_range symbol within | |
5 | + devfreq | |
6 | + | |
7 | +In order to get frequency range within devfreq governors, | |
8 | +export devfreq_get_freq_range symbol within devfreq. | |
9 | + | |
10 | +Reviewed-by: Matthias Kaehlcke <mka@chromium.org> | |
11 | +Tested-by: Chen-Yu Tsai <wenst@chromium.org> | |
12 | +Tested-by: Johnson Wang <johnson.wang@mediatek.com> | |
13 | +Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> | |
14 | +--- | |
15 | + drivers/devfreq/devfreq.c | 20 ++++++++++++-------- | |
16 | + drivers/devfreq/governor.h | 2 ++ | |
17 | + 2 files changed, 14 insertions(+), 8 deletions(-) | |
18 | + | |
19 | +--- a/drivers/devfreq/devfreq.c | |
20 | ++++ b/drivers/devfreq/devfreq.c | |
21 | +@@ -112,16 +112,16 @@ static unsigned long find_available_max_ | |
22 | + } | |
23 | + | |
24 | + /** | |
25 | +- * get_freq_range() - Get the current freq range | |
26 | ++ * devfreq_get_freq_range() - Get the current freq range | |
27 | + * @devfreq: the devfreq instance | |
28 | + * @min_freq: the min frequency | |
29 | + * @max_freq: the max frequency | |
30 | + * | |
31 | + * This takes into consideration all constraints. | |
32 | + */ | |
33 | +-static void get_freq_range(struct devfreq *devfreq, | |
34 | +- unsigned long *min_freq, | |
35 | +- unsigned long *max_freq) | |
36 | ++void devfreq_get_freq_range(struct devfreq *devfreq, | |
37 | ++ unsigned long *min_freq, | |
38 | ++ unsigned long *max_freq) | |
39 | + { | |
40 | + unsigned long *freq_table = devfreq->profile->freq_table; | |
41 | + s32 qos_min_freq, qos_max_freq; | |
42 | +@@ -158,6 +158,7 @@ static void get_freq_range(struct devfre | |
43 | + if (*min_freq > *max_freq) | |
44 | + *min_freq = *max_freq; | |
45 | + } | |
46 | ++EXPORT_SYMBOL(devfreq_get_freq_range); | |
47 | + | |
48 | + /** | |
49 | + * devfreq_get_freq_level() - Lookup freq_table for the frequency | |
50 | +@@ -418,7 +419,7 @@ int devfreq_update_target(struct devfreq | |
51 | + err = devfreq->governor->get_target_freq(devfreq, &freq); | |
52 | + if (err) | |
53 | + return err; | |
54 | +- get_freq_range(devfreq, &min_freq, &max_freq); | |
55 | ++ devfreq_get_freq_range(devfreq, &min_freq, &max_freq); | |
56 | + | |
57 | + if (freq < min_freq) { | |
58 | + freq = min_freq; | |
59 | +@@ -785,6 +786,7 @@ struct devfreq *devfreq_add_device(struc | |
60 | + { | |
61 | + struct devfreq *devfreq; | |
62 | + struct devfreq_governor *governor; | |
63 | ++ unsigned long min_freq, max_freq; | |
64 | + int err = 0; | |
65 | + | |
66 | + if (!dev || !profile || !governor_name) { | |
67 | +@@ -849,6 +851,8 @@ struct devfreq *devfreq_add_device(struc | |
68 | + goto err_dev; | |
69 | + } | |
70 | + | |
71 | ++ devfreq_get_freq_range(devfreq, &min_freq, &max_freq); | |
72 | ++ | |
73 | + devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev); | |
74 | + devfreq->opp_table = dev_pm_opp_get_opp_table(dev); | |
75 | + if (IS_ERR(devfreq->opp_table)) | |
76 | +@@ -1561,7 +1565,7 @@ static ssize_t min_freq_show(struct devi | |
77 | + unsigned long min_freq, max_freq; | |
78 | + | |
79 | + mutex_lock(&df->lock); | |
80 | +- get_freq_range(df, &min_freq, &max_freq); | |
81 | ++ devfreq_get_freq_range(df, &min_freq, &max_freq); | |
82 | + mutex_unlock(&df->lock); | |
83 | + | |
84 | + return sprintf(buf, "%lu\n", min_freq); | |
85 | +@@ -1615,7 +1619,7 @@ static ssize_t max_freq_show(struct devi | |
86 | + unsigned long min_freq, max_freq; | |
87 | + | |
88 | + mutex_lock(&df->lock); | |
89 | +- get_freq_range(df, &min_freq, &max_freq); | |
90 | ++ devfreq_get_freq_range(df, &min_freq, &max_freq); | |
91 | + mutex_unlock(&df->lock); | |
92 | + | |
93 | + return sprintf(buf, "%lu\n", max_freq); | |
94 | +@@ -1929,7 +1933,7 @@ static int devfreq_summary_show(struct s | |
95 | + | |
96 | + mutex_lock(&devfreq->lock); | |
97 | + cur_freq = devfreq->previous_freq; | |
98 | +- get_freq_range(devfreq, &min_freq, &max_freq); | |
99 | ++ devfreq_get_freq_range(devfreq, &min_freq, &max_freq); | |
100 | + timer = devfreq->profile->timer; | |
101 | + | |
102 | + if (IS_SUPPORTED_ATTR(devfreq->governor->attrs, POLLING_INTERVAL)) | |
103 | +--- a/drivers/devfreq/governor.h | |
104 | ++++ b/drivers/devfreq/governor.h | |
105 | +@@ -86,6 +86,8 @@ int devfreq_remove_governor(struct devfr | |
106 | + | |
107 | + int devfreq_update_status(struct devfreq *devfreq, unsigned long freq); | |
108 | + int devfreq_update_target(struct devfreq *devfreq, unsigned long freq); | |
109 | ++void devfreq_get_freq_range(struct devfreq *devfreq, unsigned long *min_freq, | |
110 | ++ unsigned long *max_freq); | |
111 | + | |
112 | + static inline int devfreq_update_stats(struct devfreq *df) | |
113 | + { |
@@ -0,0 +1,461 @@ | ||
1 | +From a03dacb0316f74400846aaf144d6c73f4217ca08 Mon Sep 17 00:00:00 2001 | |
2 | +From: Saravana Kannan <skannan@codeaurora.org> | |
3 | +Date: Tue, 2 Mar 2021 15:58:21 +0900 | |
4 | +Subject: [PATCH 2/5] PM / devfreq: Add cpu based scaling support to passive | |
5 | + governor | |
6 | + | |
7 | +Many CPU architectures have caches that can scale independent of the | |
8 | +CPUs. Frequency scaling of the caches is necessary to make sure that the | |
9 | +cache is not a performance bottleneck that leads to poor performance and | |
10 | +power. The same idea applies for RAM/DDR. | |
11 | + | |
12 | +To achieve this, this patch adds support for cpu based scaling to the | |
13 | +passive governor. This is accomplished by taking the current frequency | |
14 | +of each CPU frequency domain and then adjust the frequency of the cache | |
15 | +(or any devfreq device) based on the frequency of the CPUs. It listens | |
16 | +to CPU frequency transition notifiers to keep itself up to date on the | |
17 | +current CPU frequency. | |
18 | + | |
19 | +To decide the frequency of the device, the governor does one of the | |
20 | +following: | |
21 | +* Derives the optimal devfreq device opp from required-opps property of | |
22 | + the parent cpu opp_table. | |
23 | + | |
24 | +* Scales the device frequency in proportion to the CPU frequency. So, if | |
25 | + the CPUs are running at their max frequency, the device runs at its | |
26 | + max frequency. If the CPUs are running at their min frequency, the | |
27 | + device runs at its min frequency. It is interpolated for frequencies | |
28 | + in between. | |
29 | + | |
30 | +Tested-by: Chen-Yu Tsai <wenst@chromium.org> | |
31 | +Tested-by: Johnson Wang <johnson.wang@mediatek.com> | |
32 | +Signed-off-by: Saravana Kannan <skannan@codeaurora.org> | |
33 | +[Sibi: Integrated cpu-freqmap governor into passive_governor] | |
34 | +Signed-off-by: Sibi Sankar <sibis@codeaurora.org> | |
35 | +[Chanwoo: Fix conflict with latest code and cleanup code] | |
36 | +Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> | |
37 | +--- | |
38 | + drivers/devfreq/governor.h | 22 +++ | |
39 | + drivers/devfreq/governor_passive.c | 298 +++++++++++++++++++++++++++-- | |
40 | + include/linux/devfreq.h | 17 +- | |
41 | + 3 files changed, 323 insertions(+), 14 deletions(-) | |
42 | + | |
43 | +--- a/drivers/devfreq/governor.h | |
44 | ++++ b/drivers/devfreq/governor.h | |
45 | +@@ -48,6 +48,28 @@ | |
46 | + #define DEVFREQ_GOV_ATTR_TIMER BIT(1) | |
47 | + | |
48 | + /** | |
49 | ++ * struct devfreq_cpu_data - Hold the per-cpu data | |
50 | ++ * @dev: reference to cpu device. | |
51 | ++ * @first_cpu: the cpumask of the first cpu of a policy. | |
52 | ++ * @opp_table: reference to cpu opp table. | |
53 | ++ * @cur_freq: the current frequency of the cpu. | |
54 | ++ * @min_freq: the min frequency of the cpu. | |
55 | ++ * @max_freq: the max frequency of the cpu. | |
56 | ++ * | |
57 | ++ * This structure stores the required cpu_data of a cpu. | |
58 | ++ * This is auto-populated by the governor. | |
59 | ++ */ | |
60 | ++struct devfreq_cpu_data { | |
61 | ++ struct device *dev; | |
62 | ++ unsigned int first_cpu; | |
63 | ++ | |
64 | ++ struct opp_table *opp_table; | |
65 | ++ unsigned int cur_freq; | |
66 | ++ unsigned int min_freq; | |
67 | ++ unsigned int max_freq; | |
68 | ++}; | |
69 | ++ | |
70 | ++/** | |
71 | + * struct devfreq_governor - Devfreq policy governor | |
72 | + * @node: list node - contains registered devfreq governors | |
73 | + * @name: Governor's name | |
74 | +--- a/drivers/devfreq/governor_passive.c | |
75 | ++++ b/drivers/devfreq/governor_passive.c | |
76 | +@@ -8,11 +8,85 @@ | |
77 | + */ | |
78 | + | |
79 | + #include <linux/module.h> | |
80 | ++#include <linux/cpu.h> | |
81 | ++#include <linux/cpufreq.h> | |
82 | ++#include <linux/cpumask.h> | |
83 | ++#include <linux/slab.h> | |
84 | + #include <linux/device.h> | |
85 | + #include <linux/devfreq.h> | |
86 | + #include "governor.h" | |
87 | + | |
88 | +-static int devfreq_passive_get_target_freq(struct devfreq *devfreq, | |
89 | ++#define HZ_PER_KHZ 1000 | |
90 | ++ | |
91 | ++static unsigned long get_target_freq_by_required_opp(struct device *p_dev, | |
92 | ++ struct opp_table *p_opp_table, | |
93 | ++ struct opp_table *opp_table, | |
94 | ++ unsigned long *freq) | |
95 | ++{ | |
96 | ++ struct dev_pm_opp *opp = NULL, *p_opp = NULL; | |
97 | ++ unsigned long target_freq; | |
98 | ++ | |
99 | ++ if (!p_dev || !p_opp_table || !opp_table || !freq) | |
100 | ++ return 0; | |
101 | ++ | |
102 | ++ p_opp = devfreq_recommended_opp(p_dev, freq, 0); | |
103 | ++ if (IS_ERR(p_opp)) | |
104 | ++ return 0; | |
105 | ++ | |
106 | ++ opp = dev_pm_opp_xlate_required_opp(p_opp_table, opp_table, p_opp); | |
107 | ++ dev_pm_opp_put(p_opp); | |
108 | ++ | |
109 | ++ if (IS_ERR(opp)) | |
110 | ++ return 0; | |
111 | ++ | |
112 | ++ target_freq = dev_pm_opp_get_freq(opp); | |
113 | ++ dev_pm_opp_put(opp); | |
114 | ++ | |
115 | ++ return target_freq; | |
116 | ++} | |
117 | ++ | |
118 | ++static int get_target_freq_with_cpufreq(struct devfreq *devfreq, | |
119 | ++ unsigned long *target_freq) | |
120 | ++{ | |
121 | ++ struct devfreq_passive_data *p_data = | |
122 | ++ (struct devfreq_passive_data *)devfreq->data; | |
123 | ++ struct devfreq_cpu_data *parent_cpu_data; | |
124 | ++ unsigned long cpu, cpu_cur, cpu_min, cpu_max, cpu_percent; | |
125 | ++ unsigned long dev_min, dev_max; | |
126 | ++ unsigned long freq = 0; | |
127 | ++ | |
128 | ++ for_each_online_cpu(cpu) { | |
129 | ++ parent_cpu_data = p_data->parent_cpu_data[cpu]; | |
130 | ++ if (!parent_cpu_data || parent_cpu_data->first_cpu != cpu) | |
131 | ++ continue; | |
132 | ++ | |
133 | ++ /* Get target freq via required opps */ | |
134 | ++ cpu_cur = parent_cpu_data->cur_freq * HZ_PER_KHZ; | |
135 | ++ freq = get_target_freq_by_required_opp(parent_cpu_data->dev, | |
136 | ++ parent_cpu_data->opp_table, | |
137 | ++ devfreq->opp_table, &cpu_cur); | |
138 | ++ if (freq) { | |
139 | ++ *target_freq = max(freq, *target_freq); | |
140 | ++ continue; | |
141 | ++ } | |
142 | ++ | |
143 | ++ /* Use interpolation if required opps is not available */ | |
144 | ++ devfreq_get_freq_range(devfreq, &dev_min, &dev_max); | |
145 | ++ | |
146 | ++ cpu_min = parent_cpu_data->min_freq; | |
147 | ++ cpu_max = parent_cpu_data->max_freq; | |
148 | ++ cpu_cur = parent_cpu_data->cur_freq; | |
149 | ++ | |
150 | ++ cpu_percent = ((cpu_cur - cpu_min) * 100) / (cpu_max - cpu_min); | |
151 | ++ freq = dev_min + mult_frac(dev_max - dev_min, cpu_percent, 100); | |
152 | ++ | |
153 | ++ *target_freq = max(freq, *target_freq); | |
154 | ++ } | |
155 | ++ | |
156 | ++ return 0; | |
157 | ++} | |
158 | ++ | |
159 | ++static int get_target_freq_with_devfreq(struct devfreq *devfreq, | |
160 | + unsigned long *freq) | |
161 | + { | |
162 | + struct devfreq_passive_data *p_data | |
163 | +@@ -99,6 +173,181 @@ no_required_opp: | |
164 | + return 0; | |
165 | + } | |
166 | + | |
167 | ++static int devfreq_passive_get_target_freq(struct devfreq *devfreq, | |
168 | ++ unsigned long *freq) | |
169 | ++{ | |
170 | ++ struct devfreq_passive_data *p_data = | |
171 | ++ (struct devfreq_passive_data *)devfreq->data; | |
172 | ++ int ret; | |
173 | ++ | |
174 | ++ if (!p_data) | |
175 | ++ return -EINVAL; | |
176 | ++ | |
177 | ++ /* | |
178 | ++ * If the devfreq device with passive governor has the specific method | |
179 | ++ * to determine the next frequency, should use the get_target_freq() | |
180 | ++ * of struct devfreq_passive_data. | |
181 | ++ */ | |
182 | ++ if (p_data->get_target_freq) | |
183 | ++ return p_data->get_target_freq(devfreq, freq); | |
184 | ++ | |
185 | ++ switch (p_data->parent_type) { | |
186 | ++ case DEVFREQ_PARENT_DEV: | |
187 | ++ ret = get_target_freq_with_devfreq(devfreq, freq); | |
188 | ++ break; | |
189 | ++ case CPUFREQ_PARENT_DEV: | |
190 | ++ ret = get_target_freq_with_cpufreq(devfreq, freq); | |
191 | ++ break; | |
192 | ++ default: | |
193 | ++ ret = -EINVAL; | |
194 | ++ dev_err(&devfreq->dev, "Invalid parent type\n"); | |
195 | ++ break; | |
196 | ++ } | |
197 | ++ | |
198 | ++ return ret; | |
199 | ++} | |
200 | ++ | |
201 | ++static int cpufreq_passive_notifier_call(struct notifier_block *nb, | |
202 | ++ unsigned long event, void *ptr) | |
203 | ++{ | |
204 | ++ struct devfreq_passive_data *p_data = | |
205 | ++ container_of(nb, struct devfreq_passive_data, nb); | |
206 | ++ struct devfreq *devfreq = (struct devfreq *)p_data->this; | |
207 | ++ struct devfreq_cpu_data *parent_cpu_data; | |
208 | ++ struct cpufreq_freqs *freqs = ptr; | |
209 | ++ unsigned int cur_freq; | |
210 | ++ int ret; | |
211 | ++ | |
212 | ++ if (event != CPUFREQ_POSTCHANGE || !freqs || | |
213 | ++ !p_data->parent_cpu_data[freqs->policy->cpu]) | |
214 | ++ return 0; | |
215 | ++ | |
216 | ++ parent_cpu_data = p_data->parent_cpu_data[freqs->policy->cpu]; | |
217 | ++ if (parent_cpu_data->cur_freq == freqs->new) | |
218 | ++ return 0; | |
219 | ++ | |
220 | ++ cur_freq = parent_cpu_data->cur_freq; | |
221 | ++ parent_cpu_data->cur_freq = freqs->new; | |
222 | ++ | |
223 | ++ mutex_lock(&devfreq->lock); | |
224 | ++ ret = devfreq_update_target(devfreq, freqs->new); | |
225 | ++ mutex_unlock(&devfreq->lock); | |
226 | ++ if (ret) { | |
227 | ++ parent_cpu_data->cur_freq = cur_freq; | |
228 | ++ dev_err(&devfreq->dev, "failed to update the frequency.\n"); | |
229 | ++ return ret; | |
230 | ++ } | |
231 | ++ | |
232 | ++ return 0; | |
233 | ++} | |
234 | ++ | |
235 | ++static int cpufreq_passive_unregister_notifier(struct devfreq *devfreq) | |
236 | ++{ | |
237 | ++ struct devfreq_passive_data *p_data | |
238 | ++ = (struct devfreq_passive_data *)devfreq->data; | |
239 | ++ struct devfreq_cpu_data *parent_cpu_data; | |
240 | ++ int cpu, ret; | |
241 | ++ | |
242 | ++ if (p_data->nb.notifier_call) { | |
243 | ++ ret = cpufreq_unregister_notifier(&p_data->nb, | |
244 | ++ CPUFREQ_TRANSITION_NOTIFIER); | |
245 | ++ if (ret < 0) | |
246 | ++ return ret; | |
247 | ++ } | |
248 | ++ | |
249 | ++ for_each_possible_cpu(cpu) { | |
250 | ++ parent_cpu_data = p_data->parent_cpu_data[cpu]; | |
251 | ++ if (!parent_cpu_data) | |
252 | ++ continue; | |
253 | ++ | |
254 | ++ if (parent_cpu_data->opp_table) | |
255 | ++ dev_pm_opp_put_opp_table(parent_cpu_data->opp_table); | |
256 | ++ kfree(parent_cpu_data); | |
257 | ++ } | |
258 | ++ | |
259 | ++ return 0; | |
260 | ++} | |
261 | ++ | |
262 | ++static int cpufreq_passive_register_notifier(struct devfreq *devfreq) | |
263 | ++{ | |
264 | ++ struct devfreq_passive_data *p_data | |
265 | ++ = (struct devfreq_passive_data *)devfreq->data; | |
266 | ++ struct device *dev = devfreq->dev.parent; | |
267 | ++ struct opp_table *opp_table = NULL; | |
268 | ++ struct devfreq_cpu_data *parent_cpu_data; | |
269 | ++ struct cpufreq_policy *policy; | |
270 | ++ struct device *cpu_dev; | |
271 | ++ unsigned int cpu; | |
272 | ++ int ret; | |
273 | ++ | |
274 | ++ p_data->nb.notifier_call = cpufreq_passive_notifier_call; | |
275 | ++ ret = cpufreq_register_notifier(&p_data->nb, CPUFREQ_TRANSITION_NOTIFIER); | |
276 | ++ if (ret) { | |
277 | ++ dev_err(dev, "failed to register cpufreq notifier\n"); | |
278 | ++ p_data->nb.notifier_call = NULL; | |
279 | ++ goto err; | |
280 | ++ } | |
281 | ++ | |
282 | ++ for_each_possible_cpu(cpu) { | |
283 | ++ if (p_data->parent_cpu_data[cpu]) | |
284 | ++ continue; | |
285 | ++ | |
286 | ++ policy = cpufreq_cpu_get(cpu); | |
287 | ++ if (!policy) { | |
288 | ++ ret = -EPROBE_DEFER; | |
289 | ++ goto err; | |
290 | ++ } | |
291 | ++ | |
292 | ++ parent_cpu_data = kzalloc(sizeof(*parent_cpu_data), | |
293 | ++ GFP_KERNEL); | |
294 | ++ if (!parent_cpu_data) { | |
295 | ++ ret = -ENOMEM; | |
296 | ++ goto err_put_policy; | |
297 | ++ } | |
298 | ++ | |
299 | ++ cpu_dev = get_cpu_device(cpu); | |
300 | ++ if (!cpu_dev) { | |
301 | ++ dev_err(dev, "failed to get cpu device\n"); | |
302 | ++ ret = -ENODEV; | |
303 | ++ goto err_free_cpu_data; | |
304 | ++ } | |
305 | ++ | |
306 | ++ opp_table = dev_pm_opp_get_opp_table(cpu_dev); | |
307 | ++ if (IS_ERR(opp_table)) { | |
308 | ++ dev_err(dev, "failed to get opp_table of cpu%d\n", cpu); | |
309 | ++ ret = PTR_ERR(opp_table); | |
310 | ++ goto err_free_cpu_data; | |
311 | ++ } | |
312 | ++ | |
313 | ++ parent_cpu_data->dev = cpu_dev; | |
314 | ++ parent_cpu_data->opp_table = opp_table; | |
315 | ++ parent_cpu_data->first_cpu = cpumask_first(policy->related_cpus); | |
316 | ++ parent_cpu_data->cur_freq = policy->cur; | |
317 | ++ parent_cpu_data->min_freq = policy->cpuinfo.min_freq; | |
318 | ++ parent_cpu_data->max_freq = policy->cpuinfo.max_freq; | |
319 | ++ | |
320 | ++ p_data->parent_cpu_data[cpu] = parent_cpu_data; | |
321 | ++ cpufreq_cpu_put(policy); | |
322 | ++ } | |
323 | ++ | |
324 | ++ mutex_lock(&devfreq->lock); | |
325 | ++ ret = devfreq_update_target(devfreq, 0L); | |
326 | ++ mutex_unlock(&devfreq->lock); | |
327 | ++ if (ret) | |
328 | ++ dev_err(dev, "failed to update the frequency\n"); | |
329 | ++ | |
330 | ++ return ret; | |
331 | ++ | |
332 | ++err_free_cpu_data: | |
333 | ++ kfree(parent_cpu_data); | |
334 | ++err_put_policy: | |
335 | ++ cpufreq_cpu_put(policy); | |
336 | ++err: | |
337 | ++ WARN_ON(cpufreq_passive_unregister_notifier(devfreq)); | |
338 | ++ | |
339 | ++ return ret; | |
340 | ++} | |
341 | ++ | |
342 | + static int devfreq_passive_notifier_call(struct notifier_block *nb, | |
343 | + unsigned long event, void *ptr) | |
344 | + { | |
345 | +@@ -131,30 +380,55 @@ static int devfreq_passive_notifier_call | |
346 | + return NOTIFY_DONE; | |
347 | + } | |
348 | + | |
349 | +-static int devfreq_passive_event_handler(struct devfreq *devfreq, | |
350 | +- unsigned int event, void *data) | |
351 | ++static int devfreq_passive_unregister_notifier(struct devfreq *devfreq) | |
352 | ++{ | |
353 | ++ struct devfreq_passive_data *p_data | |
354 | ++ = (struct devfreq_passive_data *)devfreq->data; | |
355 | ++ struct devfreq *parent = (struct devfreq *)p_data->parent; | |
356 | ++ struct notifier_block *nb = &p_data->nb; | |
357 | ++ | |
358 | ++ return devfreq_unregister_notifier(parent, nb, DEVFREQ_TRANSITION_NOTIFIER); | |
359 | ++} | |
360 | ++ | |
361 | ++static int devfreq_passive_register_notifier(struct devfreq *devfreq) | |
362 | + { | |
363 | + struct devfreq_passive_data *p_data | |
364 | + = (struct devfreq_passive_data *)devfreq->data; | |
365 | + struct devfreq *parent = (struct devfreq *)p_data->parent; | |
366 | + struct notifier_block *nb = &p_data->nb; | |
367 | +- int ret = 0; | |
368 | + | |
369 | + if (!parent) | |
370 | + return -EPROBE_DEFER; | |
371 | + | |
372 | ++ nb->notifier_call = devfreq_passive_notifier_call; | |
373 | ++ return devfreq_register_notifier(parent, nb, DEVFREQ_TRANSITION_NOTIFIER); | |
374 | ++} | |
375 | ++ | |
376 | ++static int devfreq_passive_event_handler(struct devfreq *devfreq, | |
377 | ++ unsigned int event, void *data) | |
378 | ++{ | |
379 | ++ struct devfreq_passive_data *p_data | |
380 | ++ = (struct devfreq_passive_data *)devfreq->data; | |
381 | ++ int ret = -EINVAL; | |
382 | ++ | |
383 | ++ if (!p_data) | |
384 | ++ return -EINVAL; | |
385 | ++ | |
386 | ++ if (!p_data->this) | |
387 | ++ p_data->this = devfreq; | |
388 | ++ | |
389 | + switch (event) { | |
390 | + case DEVFREQ_GOV_START: | |
391 | +- if (!p_data->this) | |
392 | +- p_data->this = devfreq; | |
393 | +- | |
394 | +- nb->notifier_call = devfreq_passive_notifier_call; | |
395 | +- ret = devfreq_register_notifier(parent, nb, | |
396 | +- DEVFREQ_TRANSITION_NOTIFIER); | |
397 | ++ if (p_data->parent_type == DEVFREQ_PARENT_DEV) | |
398 | ++ ret = devfreq_passive_register_notifier(devfreq); | |
399 | ++ else if (p_data->parent_type == CPUFREQ_PARENT_DEV) | |
400 | ++ ret = cpufreq_passive_register_notifier(devfreq); | |
401 | + break; | |
402 | + case DEVFREQ_GOV_STOP: | |
403 | +- WARN_ON(devfreq_unregister_notifier(parent, nb, | |
404 | +- DEVFREQ_TRANSITION_NOTIFIER)); | |
405 | ++ if (p_data->parent_type == DEVFREQ_PARENT_DEV) | |
406 | ++ WARN_ON(devfreq_passive_unregister_notifier(devfreq)); | |
407 | ++ else if (p_data->parent_type == CPUFREQ_PARENT_DEV) | |
408 | ++ WARN_ON(cpufreq_passive_unregister_notifier(devfreq)); | |
409 | + break; | |
410 | + default: | |
411 | + break; | |
412 | +--- a/include/linux/devfreq.h | |
413 | ++++ b/include/linux/devfreq.h | |
414 | +@@ -38,6 +38,7 @@ enum devfreq_timer { | |
415 | + | |
416 | + struct devfreq; | |
417 | + struct devfreq_governor; | |
418 | ++struct devfreq_cpu_data; | |
419 | + struct thermal_cooling_device; | |
420 | + | |
421 | + /** | |
422 | +@@ -288,6 +289,11 @@ struct devfreq_simple_ondemand_data { | |
423 | + #endif | |
424 | + | |
425 | + #if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE) | |
426 | ++enum devfreq_parent_dev_type { | |
427 | ++ DEVFREQ_PARENT_DEV, | |
428 | ++ CPUFREQ_PARENT_DEV, | |
429 | ++}; | |
430 | ++ | |
431 | + /** | |
432 | + * struct devfreq_passive_data - ``void *data`` fed to struct devfreq | |
433 | + * and devfreq_add_device | |
434 | +@@ -299,8 +305,11 @@ struct devfreq_simple_ondemand_data { | |
435 | + * using governors except for passive governor. | |
436 | + * If the devfreq device has the specific method to decide | |
437 | + * the next frequency, should use this callback. | |
438 | +- * @this: the devfreq instance of own device. | |
439 | +- * @nb: the notifier block for DEVFREQ_TRANSITION_NOTIFIER list | |
440 | ++ * @parent_type: the parent type of the device. | |
441 | ++ * @this: the devfreq instance of own device. | |
442 | ++ * @nb: the notifier block for DEVFREQ_TRANSITION_NOTIFIER or | |
443 | ++ * CPUFREQ_TRANSITION_NOTIFIER list. | |
444 | ++ * @parent_cpu_data: the state min/max/current frequency of all online cpu's. | |
445 | + * | |
446 | + * The devfreq_passive_data have to set the devfreq instance of parent | |
447 | + * device with governors except for the passive governor. But, don't need to | |
448 | +@@ -314,9 +323,13 @@ struct devfreq_passive_data { | |
449 | + /* Optional callback to decide the next frequency of passvice device */ | |
450 | + int (*get_target_freq)(struct devfreq *this, unsigned long *freq); | |
451 | + | |
452 | ++ /* Should set the type of parent device */ | |
453 | ++ enum devfreq_parent_dev_type parent_type; | |
454 | ++ | |
455 | + /* For passive governor's internal use. Don't need to set them */ | |
456 | + struct devfreq *this; | |
457 | + struct notifier_block nb; | |
458 | ++ struct devfreq_cpu_data *parent_cpu_data[NR_CPUS]; | |
459 | + }; | |
460 | + #endif | |
461 | + |
@@ -0,0 +1,110 @@ | ||
1 | +From 05723e71234b60a1a47313ea1a889797ec648f1c Mon Sep 17 00:00:00 2001 | |
2 | +From: Chanwoo Choi <cw00.choi@samsung.com> | |
3 | +Date: Tue, 2 Mar 2021 17:22:50 +0900 | |
4 | +Subject: [PATCH 3/5] PM / devfreq: passive: Reduce duplicate code when | |
5 | + passive_devfreq case | |
6 | + | |
7 | +In order to keep the consistent coding style between passive_devfreq | |
8 | +and passive_cpufreq, use common code for handling required opp property. | |
9 | +Also remove the unneed conditional statement and unify the comment | |
10 | +of both passive_devfreq and passive_cpufreq when getting the target frequency. | |
11 | + | |
12 | +Tested-by: Chen-Yu Tsai <wenst@chromium.org> | |
13 | +Tested-by: Johnson Wang <johnson.wang@mediatek.com> | |
14 | +Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> | |
15 | +--- | |
16 | + drivers/devfreq/governor_passive.c | 66 ++++-------------------------- | |
17 | + 1 file changed, 8 insertions(+), 58 deletions(-) | |
18 | + | |
19 | +--- a/drivers/devfreq/governor_passive.c | |
20 | ++++ b/drivers/devfreq/governor_passive.c | |
21 | +@@ -93,65 +93,16 @@ static int get_target_freq_with_devfreq( | |
22 | + = (struct devfreq_passive_data *)devfreq->data; | |
23 | + struct devfreq *parent_devfreq = (struct devfreq *)p_data->parent; | |
24 | + unsigned long child_freq = ULONG_MAX; | |
25 | +- struct dev_pm_opp *opp, *p_opp; | |
26 | + int i, count; | |
27 | + | |
28 | +- /* | |
29 | +- * If the devfreq device with passive governor has the specific method | |
30 | +- * to determine the next frequency, should use the get_target_freq() | |
31 | +- * of struct devfreq_passive_data. | |
32 | +- */ | |
33 | +- if (p_data->get_target_freq) | |
34 | +- return p_data->get_target_freq(devfreq, freq); | |
35 | +- | |
36 | +- /* | |
37 | +- * If the parent and passive devfreq device uses the OPP table, | |
38 | +- * get the next frequency by using the OPP table. | |
39 | +- */ | |
40 | +- | |
41 | +- /* | |
42 | +- * - parent devfreq device uses the governors except for passive. | |
43 | +- * - passive devfreq device uses the passive governor. | |
44 | +- * | |
45 | +- * Each devfreq has the OPP table. After deciding the new frequency | |
46 | +- * from the governor of parent devfreq device, the passive governor | |
47 | +- * need to get the index of new frequency on OPP table of parent | |
48 | +- * device. And then the index is used for getting the suitable | |
49 | +- * new frequency for passive devfreq device. | |
50 | +- */ | |
51 | +- if (!devfreq->profile || !devfreq->profile->freq_table | |
52 | +- || devfreq->profile->max_state <= 0) | |
53 | +- return -EINVAL; | |
54 | +- | |
55 | +- /* | |
56 | +- * The passive governor have to get the correct frequency from OPP | |
57 | +- * list of parent device. Because in this case, *freq is temporary | |
58 | +- * value which is decided by ondemand governor. | |
59 | +- */ | |
60 | +- if (devfreq->opp_table && parent_devfreq->opp_table) { | |
61 | +- p_opp = devfreq_recommended_opp(parent_devfreq->dev.parent, | |
62 | +- freq, 0); | |
63 | +- if (IS_ERR(p_opp)) | |
64 | +- return PTR_ERR(p_opp); | |
65 | +- | |
66 | +- opp = dev_pm_opp_xlate_required_opp(parent_devfreq->opp_table, | |
67 | +- devfreq->opp_table, p_opp); | |
68 | +- dev_pm_opp_put(p_opp); | |
69 | +- | |
70 | +- if (IS_ERR(opp)) | |
71 | +- goto no_required_opp; | |
72 | +- | |
73 | +- *freq = dev_pm_opp_get_freq(opp); | |
74 | +- dev_pm_opp_put(opp); | |
75 | +- | |
76 | +- return 0; | |
77 | +- } | |
78 | ++ /* Get target freq via required opps */ | |
79 | ++ child_freq = get_target_freq_by_required_opp(parent_devfreq->dev.parent, | |
80 | ++ parent_devfreq->opp_table, | |
81 | ++ devfreq->opp_table, freq); | |
82 | ++ if (child_freq) | |
83 | ++ goto out; | |
84 | + | |
85 | +-no_required_opp: | |
86 | +- /* | |
87 | +- * Get the OPP table's index of decided frequency by governor | |
88 | +- * of parent device. | |
89 | +- */ | |
90 | ++ /* Use interpolation if required opps is not available */ | |
91 | + for (i = 0; i < parent_devfreq->profile->max_state; i++) | |
92 | + if (parent_devfreq->profile->freq_table[i] == *freq) | |
93 | + break; | |
94 | +@@ -159,7 +110,6 @@ no_required_opp: | |
95 | + if (i == parent_devfreq->profile->max_state) | |
96 | + return -EINVAL; | |
97 | + | |
98 | +- /* Get the suitable frequency by using index of parent device. */ | |
99 | + if (i < devfreq->profile->max_state) { | |
100 | + child_freq = devfreq->profile->freq_table[i]; | |
101 | + } else { | |
102 | +@@ -167,7 +117,7 @@ no_required_opp: | |
103 | + child_freq = devfreq->profile->freq_table[count - 1]; | |
104 | + } | |
105 | + | |
106 | +- /* Return the suitable frequency for passive device. */ | |
107 | ++out: | |
108 | + *freq = child_freq; | |
109 | + | |
110 | + return 0; |
@@ -0,0 +1,232 @@ | ||
1 | +From 26984d9d581e5049bd75091d2e789b9cc3ea12e0 Mon Sep 17 00:00:00 2001 | |
2 | +From: Chanwoo Choi <cw00.choi@samsung.com> | |
3 | +Date: Wed, 27 Apr 2022 03:49:19 +0900 | |
4 | +Subject: [PATCH 4/5] PM / devfreq: passive: Keep cpufreq_policy for possible | |
5 | + cpus | |
6 | + | |
7 | +The passive governor requires the cpu data to get the next target frequency | |
8 | +of devfreq device if depending on cpu. In order to reduce the unnecessary | |
9 | +memory data, keep cpufreq_policy data for possible cpus instead of NR_CPU. | |
10 | + | |
11 | +Tested-by: Chen-Yu Tsai <wenst@chromium.org> | |
12 | +Tested-by: Johnson Wang <johnson.wang@mediatek.com> | |
13 | +Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> | |
14 | +--- | |
15 | + drivers/devfreq/governor.h | 3 ++ | |
16 | + drivers/devfreq/governor_passive.c | 75 +++++++++++++++++++++++------- | |
17 | + include/linux/devfreq.h | 4 +- | |
18 | + 3 files changed, 64 insertions(+), 18 deletions(-) | |
19 | + | |
20 | +--- a/drivers/devfreq/governor.h | |
21 | ++++ b/drivers/devfreq/governor.h | |
22 | +@@ -49,6 +49,7 @@ | |
23 | + | |
24 | + /** | |
25 | + * struct devfreq_cpu_data - Hold the per-cpu data | |
26 | ++ * @node: list node | |
27 | + * @dev: reference to cpu device. | |
28 | + * @first_cpu: the cpumask of the first cpu of a policy. | |
29 | + * @opp_table: reference to cpu opp table. | |
30 | +@@ -60,6 +61,8 @@ | |
31 | + * This is auto-populated by the governor. | |
32 | + */ | |
33 | + struct devfreq_cpu_data { | |
34 | ++ struct list_head node; | |
35 | ++ | |
36 | + struct device *dev; | |
37 | + unsigned int first_cpu; | |
38 | + | |
39 | +--- a/drivers/devfreq/governor_passive.c | |
40 | ++++ b/drivers/devfreq/governor_passive.c | |
41 | +@@ -1,4 +1,4 @@ | |
42 | +-// SPDX-License-Identifier: GPL-2.0-only | |
43 | ++ // SPDX-License-Identifier: GPL-2.0-only | |
44 | + /* | |
45 | + * linux/drivers/devfreq/governor_passive.c | |
46 | + * | |
47 | +@@ -18,6 +18,22 @@ | |
48 | + | |
49 | + #define HZ_PER_KHZ 1000 | |
50 | + | |
51 | ++static struct devfreq_cpu_data * | |
52 | ++get_parent_cpu_data(struct devfreq_passive_data *p_data, | |
53 | ++ struct cpufreq_policy *policy) | |
54 | ++{ | |
55 | ++ struct devfreq_cpu_data *parent_cpu_data; | |
56 | ++ | |
57 | ++ if (!p_data || !policy) | |
58 | ++ return NULL; | |
59 | ++ | |
60 | ++ list_for_each_entry(parent_cpu_data, &p_data->cpu_data_list, node) | |
61 | ++ if (parent_cpu_data->first_cpu == cpumask_first(policy->related_cpus)) | |
62 | ++ return parent_cpu_data; | |
63 | ++ | |
64 | ++ return NULL; | |
65 | ++} | |
66 | ++ | |
67 | + static unsigned long get_target_freq_by_required_opp(struct device *p_dev, | |
68 | + struct opp_table *p_opp_table, | |
69 | + struct opp_table *opp_table, | |
70 | +@@ -51,14 +67,24 @@ static int get_target_freq_with_cpufreq( | |
71 | + struct devfreq_passive_data *p_data = | |
72 | + (struct devfreq_passive_data *)devfreq->data; | |
73 | + struct devfreq_cpu_data *parent_cpu_data; | |
74 | ++ struct cpufreq_policy *policy; | |
75 | + unsigned long cpu, cpu_cur, cpu_min, cpu_max, cpu_percent; | |
76 | + unsigned long dev_min, dev_max; | |
77 | + unsigned long freq = 0; | |
78 | ++ int ret = 0; | |
79 | + | |
80 | + for_each_online_cpu(cpu) { | |
81 | +- parent_cpu_data = p_data->parent_cpu_data[cpu]; | |
82 | +- if (!parent_cpu_data || parent_cpu_data->first_cpu != cpu) | |
83 | ++ policy = cpufreq_cpu_get(cpu); | |
84 | ++ if (!policy) { | |
85 | ++ ret = -EINVAL; | |
86 | ++ continue; | |
87 | ++ } | |
88 | ++ | |
89 | ++ parent_cpu_data = get_parent_cpu_data(p_data, policy); | |
90 | ++ if (!parent_cpu_data) { | |
91 | ++ cpufreq_cpu_put(policy); | |
92 | + continue; | |
93 | ++ } | |
94 | + | |
95 | + /* Get target freq via required opps */ | |
96 | + cpu_cur = parent_cpu_data->cur_freq * HZ_PER_KHZ; | |
97 | +@@ -67,6 +93,7 @@ static int get_target_freq_with_cpufreq( | |
98 | + devfreq->opp_table, &cpu_cur); | |
99 | + if (freq) { | |
100 | + *target_freq = max(freq, *target_freq); | |
101 | ++ cpufreq_cpu_put(policy); | |
102 | + continue; | |
103 | + } | |
104 | + | |
105 | +@@ -81,9 +108,10 @@ static int get_target_freq_with_cpufreq( | |
106 | + freq = dev_min + mult_frac(dev_max - dev_min, cpu_percent, 100); | |
107 | + | |
108 | + *target_freq = max(freq, *target_freq); | |
109 | ++ cpufreq_cpu_put(policy); | |
110 | + } | |
111 | + | |
112 | +- return 0; | |
113 | ++ return ret; | |
114 | + } | |
115 | + | |
116 | + static int get_target_freq_with_devfreq(struct devfreq *devfreq, | |
117 | +@@ -168,12 +196,11 @@ static int cpufreq_passive_notifier_call | |
118 | + unsigned int cur_freq; | |
119 | + int ret; | |
120 | + | |
121 | +- if (event != CPUFREQ_POSTCHANGE || !freqs || | |
122 | +- !p_data->parent_cpu_data[freqs->policy->cpu]) | |
123 | ++ if (event != CPUFREQ_POSTCHANGE || !freqs) | |
124 | + return 0; | |
125 | + | |
126 | +- parent_cpu_data = p_data->parent_cpu_data[freqs->policy->cpu]; | |
127 | +- if (parent_cpu_data->cur_freq == freqs->new) | |
128 | ++ parent_cpu_data = get_parent_cpu_data(p_data, freqs->policy); | |
129 | ++ if (!parent_cpu_data || parent_cpu_data->cur_freq == freqs->new) | |
130 | + return 0; | |
131 | + | |
132 | + cur_freq = parent_cpu_data->cur_freq; | |
133 | +@@ -196,7 +223,7 @@ static int cpufreq_passive_unregister_no | |
134 | + struct devfreq_passive_data *p_data | |
135 | + = (struct devfreq_passive_data *)devfreq->data; | |
136 | + struct devfreq_cpu_data *parent_cpu_data; | |
137 | +- int cpu, ret; | |
138 | ++ int cpu, ret = 0; | |
139 | + | |
140 | + if (p_data->nb.notifier_call) { | |
141 | + ret = cpufreq_unregister_notifier(&p_data->nb, | |
142 | +@@ -206,16 +233,26 @@ static int cpufreq_passive_unregister_no | |
143 | + } | |
144 | + | |
145 | + for_each_possible_cpu(cpu) { | |
146 | +- parent_cpu_data = p_data->parent_cpu_data[cpu]; | |
147 | +- if (!parent_cpu_data) | |
148 | ++ struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); | |
149 | ++ if (!policy) { | |
150 | ++ ret = -EINVAL; | |
151 | ++ continue; | |
152 | ++ } | |
153 | ++ | |
154 | ++ parent_cpu_data = get_parent_cpu_data(p_data, policy); | |
155 | ++ if (!parent_cpu_data) { | |
156 | ++ cpufreq_cpu_put(policy); | |
157 | + continue; | |
158 | ++ } | |
159 | + | |
160 | ++ list_del(&parent_cpu_data->node); | |
161 | + if (parent_cpu_data->opp_table) | |
162 | + dev_pm_opp_put_opp_table(parent_cpu_data->opp_table); | |
163 | + kfree(parent_cpu_data); | |
164 | ++ cpufreq_cpu_put(policy); | |
165 | + } | |
166 | + | |
167 | +- return 0; | |
168 | ++ return ret; | |
169 | + } | |
170 | + | |
171 | + static int cpufreq_passive_register_notifier(struct devfreq *devfreq) | |
172 | +@@ -230,6 +267,9 @@ static int cpufreq_passive_register_noti | |
173 | + unsigned int cpu; | |
174 | + int ret; | |
175 | + | |
176 | ++ p_data->cpu_data_list | |
177 | ++ = (struct list_head)LIST_HEAD_INIT(p_data->cpu_data_list); | |
178 | ++ | |
179 | + p_data->nb.notifier_call = cpufreq_passive_notifier_call; | |
180 | + ret = cpufreq_register_notifier(&p_data->nb, CPUFREQ_TRANSITION_NOTIFIER); | |
181 | + if (ret) { | |
182 | +@@ -239,15 +279,18 @@ static int cpufreq_passive_register_noti | |
183 | + } | |
184 | + | |
185 | + for_each_possible_cpu(cpu) { | |
186 | +- if (p_data->parent_cpu_data[cpu]) | |
187 | +- continue; | |
188 | +- | |
189 | + policy = cpufreq_cpu_get(cpu); | |
190 | + if (!policy) { | |
191 | + ret = -EPROBE_DEFER; | |
192 | + goto err; | |
193 | + } | |
194 | + | |
195 | ++ parent_cpu_data = get_parent_cpu_data(p_data, policy); | |
196 | ++ if (parent_cpu_data) { | |
197 | ++ cpufreq_cpu_put(policy); | |
198 | ++ continue; | |
199 | ++ } | |
200 | ++ | |
201 | + parent_cpu_data = kzalloc(sizeof(*parent_cpu_data), | |
202 | + GFP_KERNEL); | |
203 | + if (!parent_cpu_data) { | |
204 | +@@ -276,7 +319,7 @@ static int cpufreq_passive_register_noti | |
205 | + parent_cpu_data->min_freq = policy->cpuinfo.min_freq; | |
206 | + parent_cpu_data->max_freq = policy->cpuinfo.max_freq; | |
207 | + | |
208 | +- p_data->parent_cpu_data[cpu] = parent_cpu_data; | |
209 | ++ list_add_tail(&parent_cpu_data->node, &p_data->cpu_data_list); | |
210 | + cpufreq_cpu_put(policy); | |
211 | + } | |
212 | + | |
213 | +--- a/include/linux/devfreq.h | |
214 | ++++ b/include/linux/devfreq.h | |
215 | +@@ -309,7 +309,7 @@ enum devfreq_parent_dev_type { | |
216 | + * @this: the devfreq instance of own device. | |
217 | + * @nb: the notifier block for DEVFREQ_TRANSITION_NOTIFIER or | |
218 | + * CPUFREQ_TRANSITION_NOTIFIER list. | |
219 | +- * @parent_cpu_data: the state min/max/current frequency of all online cpu's. | |
220 | ++ * @cpu_data_list: the list of cpu frequency data for all cpufreq_policy. | |
221 | + * | |
222 | + * The devfreq_passive_data have to set the devfreq instance of parent | |
223 | + * device with governors except for the passive governor. But, don't need to | |
224 | +@@ -329,7 +329,7 @@ struct devfreq_passive_data { | |
225 | + /* For passive governor's internal use. Don't need to set them */ | |
226 | + struct devfreq *this; | |
227 | + struct notifier_block nb; | |
228 | +- struct devfreq_cpu_data *parent_cpu_data[NR_CPUS]; | |
229 | ++ struct list_head cpu_data_list; | |
230 | + }; | |
231 | + #endif | |
232 | + |
@@ -0,0 +1,31 @@ | ||
1 | +From 42d2607d91c4ec37ea1970899c2d614824f3014b Mon Sep 17 00:00:00 2001 | |
2 | +From: Chanwoo Choi <cw00.choi@samsung.com> | |
3 | +Date: Thu, 19 May 2022 10:07:53 +0900 | |
4 | +Subject: [PATCH 5/5] PM / devfreq: passive: Return non-error when | |
5 | + not-supported event is required | |
6 | + | |
7 | +Each devfreq governor specifies the supported governor event | |
8 | +such as GOV_START and GOV_STOP. When not-supported event is required, | |
9 | +just return non-error. But, commit ce9a0d88d97a ("PM / devfreq: Add | |
10 | +cpu based scaling support to passive governor") returned the error | |
11 | +value. So that return non-error value when not-supported event is required. | |
12 | + | |
13 | +Fixes: ce9a0d88d97a ("PM / devfreq: Add cpu based scaling support to passive governor") | |
14 | +Reported-by: Marek Szyprowski <m.szyprowski@samsung.com> | |
15 | +Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> | |
16 | +Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> | |
17 | +--- | |
18 | + drivers/devfreq/governor_passive.c | 2 +- | |
19 | + 1 file changed, 1 insertion(+), 1 deletion(-) | |
20 | + | |
21 | +--- a/drivers/devfreq/governor_passive.c | |
22 | ++++ b/drivers/devfreq/governor_passive.c | |
23 | +@@ -402,7 +402,7 @@ static int devfreq_passive_event_handler | |
24 | + { | |
25 | + struct devfreq_passive_data *p_data | |
26 | + = (struct devfreq_passive_data *)devfreq->data; | |
27 | +- int ret = -EINVAL; | |
28 | ++ int ret = 0; | |
29 | + | |
30 | + if (!p_data) | |
31 | + return -EINVAL; |
@@ -0,0 +1,31 @@ | ||
1 | +From 82c66d2bbbeda9e493487e7413769087a0b46250 Mon Sep 17 00:00:00 2001 | |
2 | +From: Christian Marangi <ansuelsmth@gmail.com> | |
3 | +Date: Mon, 20 Jun 2022 00:29:39 +0200 | |
4 | +Subject: [PATCH 1/1] PM / devfreq: Fix kernel warning with cpufreq passive | |
5 | + register fail | |
6 | + | |
7 | +Remove cpufreq_passive_unregister_notifier from | |
8 | +cpufreq_passive_register_notifier in case of error as devfreq core | |
9 | +already call unregister on GOV_START fail. | |
10 | + | |
11 | +This fix the kernel always printing a WARN on governor PROBE_DEFER as | |
12 | +cpufreq_passive_unregister_notifier is called two times and return | |
13 | +error on the second call as the cpufreq is already unregistered. | |
14 | + | |
15 | +Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor") | |
16 | +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | |
17 | +Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> | |
18 | +--- | |
19 | + drivers/devfreq/governor_passive.c | 1 - | |
20 | + 1 file changed, 1 deletion(-) | |
21 | + | |
22 | +--- a/drivers/devfreq/governor_passive.c | |
23 | ++++ b/drivers/devfreq/governor_passive.c | |
24 | +@@ -336,7 +336,6 @@ err_free_cpu_data: | |
25 | + err_put_policy: | |
26 | + cpufreq_cpu_put(policy); | |
27 | + err: | |
28 | +- WARN_ON(cpufreq_passive_unregister_notifier(devfreq)); | |
29 | + | |
30 | + return ret; | |
31 | + } |
@@ -0,0 +1,85 @@ | ||
1 | +From 8953603eb5447be52f6fc3d8fcae1b3ce9899189 Mon Sep 17 00:00:00 2001 | |
2 | +From: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com> | |
3 | +Date: Mon, 6 Jun 2022 11:58:49 +0200 | |
4 | +Subject: [PATCH v4 1/4] PM / devfreq: Fix cpufreq passive unregister erroring | |
5 | + on PROBE_DEFER | |
6 | + | |
7 | +With the passive governor, the cpu based scaling can PROBE_DEFER due to | |
8 | +the fact that CPU policy are not ready. | |
9 | +The cpufreq passive unregister notifier is called both from the | |
10 | +GOV_START errors and for the GOV_STOP and assume the notifier is | |
11 | +successfully registred every time. With GOV_START failing it's wrong to | |
12 | +loop over each possible CPU since the register path has failed for | |
13 | +some CPU policy not ready. Change the logic and unregister the notifer | |
14 | +based on the current allocated parent_cpu_data list to correctly handle | |
15 | +errors and the governor unregister path. | |
16 | + | |
17 | +Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor") | |
18 | +Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com> | |
19 | +--- | |
20 | + drivers/devfreq/governor_passive.c | 39 +++++++++++++----------------- | |
21 | + 1 file changed, 17 insertions(+), 22 deletions(-) | |
22 | + | |
23 | +--- a/drivers/devfreq/governor_passive.c | |
24 | ++++ b/drivers/devfreq/governor_passive.c | |
25 | +@@ -34,6 +34,20 @@ get_parent_cpu_data(struct devfreq_passi | |
26 | + return NULL; | |
27 | + } | |
28 | + | |
29 | ++static void delete_parent_cpu_data(struct devfreq_passive_data *p_data) | |
30 | ++{ | |
31 | ++ struct devfreq_cpu_data *parent_cpu_data, *tmp; | |
32 | ++ | |
33 | ++ list_for_each_entry_safe(parent_cpu_data, tmp, &p_data->cpu_data_list, node) { | |
34 | ++ list_del(&parent_cpu_data->node); | |
35 | ++ | |
36 | ++ if (parent_cpu_data->opp_table) | |
37 | ++ dev_pm_opp_put_opp_table(parent_cpu_data->opp_table); | |
38 | ++ | |
39 | ++ kfree(parent_cpu_data); | |
40 | ++ } | |
41 | ++} | |
42 | ++ | |
43 | + static unsigned long get_target_freq_by_required_opp(struct device *p_dev, | |
44 | + struct opp_table *p_opp_table, | |
45 | + struct opp_table *opp_table, | |
46 | +@@ -222,8 +236,7 @@ static int cpufreq_passive_unregister_no | |
47 | + { | |
48 | + struct devfreq_passive_data *p_data | |
49 | + = (struct devfreq_passive_data *)devfreq->data; | |
50 | +- struct devfreq_cpu_data *parent_cpu_data; | |
51 | +- int cpu, ret = 0; | |
52 | ++ int ret; | |
53 | + | |
54 | + if (p_data->nb.notifier_call) { | |
55 | + ret = cpufreq_unregister_notifier(&p_data->nb, | |
56 | +@@ -232,27 +245,9 @@ static int cpufreq_passive_unregister_no | |
57 | + return ret; | |
58 | + } | |
59 | + | |
60 | +- for_each_possible_cpu(cpu) { | |
61 | +- struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); | |
62 | +- if (!policy) { | |
63 | +- ret = -EINVAL; | |
64 | +- continue; | |
65 | +- } | |
66 | +- | |
67 | +- parent_cpu_data = get_parent_cpu_data(p_data, policy); | |
68 | +- if (!parent_cpu_data) { | |
69 | +- cpufreq_cpu_put(policy); | |
70 | +- continue; | |
71 | +- } | |
72 | +- | |
73 | +- list_del(&parent_cpu_data->node); | |
74 | +- if (parent_cpu_data->opp_table) | |
75 | +- dev_pm_opp_put_opp_table(parent_cpu_data->opp_table); | |
76 | +- kfree(parent_cpu_data); | |
77 | +- cpufreq_cpu_put(policy); | |
78 | +- } | |
79 | ++ delete_parent_cpu_data(p_data); | |
80 | + | |
81 | +- return ret; | |
82 | ++ return 0; | |
83 | + } | |
84 | + | |
85 | + static int cpufreq_passive_register_notifier(struct devfreq *devfreq) |
@@ -0,0 +1,39 @@ | ||
1 | +From 57e00b40033a376de3f3cf0bb9bf7590d2dd679d Mon Sep 17 00:00:00 2001 | |
2 | +From: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com> | |
3 | +Date: Tue, 14 Jun 2022 13:06:59 +0200 | |
4 | +Subject: [PATCH 1/1] PM / devfreq: Fix kernel panic with cpu based scaling to | |
5 | + passive gov | |
6 | + | |
7 | +The cpufreq passive register notifier can PROBE_DEFER and the devfreq | |
8 | +struct is freed and then reallocaed on probe retry. | |
9 | +The current logic assume that the code can't PROBE_DEFER so the devfreq | |
10 | +struct in the this variable in devfreq_passive_data is assumed to be | |
11 | +(if already set) always correct. | |
12 | +This cause kernel panic as the code try to access the wrong address. | |
13 | +To correctly handle this, update the this variable in | |
14 | +devfreq_passive_data to the devfreq reallocated struct. | |
15 | + | |
16 | +Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor") | |
17 | +Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com> | |
18 | +Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> | |
19 | +--- | |
20 | + drivers/devfreq/governor_passive.c | 3 +-- | |
21 | + 1 file changed, 1 insertion(+), 2 deletions(-) | |
22 | + | |
23 | +diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c | |
24 | +index 72c67979ebe1..091a69e1f487 100644 | |
25 | +--- a/drivers/devfreq/governor_passive.c | |
26 | ++++ b/drivers/devfreq/governor_passive.c | |
27 | +@@ -407,8 +407,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq, | |
28 | + if (!p_data) | |
29 | + return -EINVAL; | |
30 | + | |
31 | +- if (!p_data->this) | |
32 | +- p_data->this = devfreq; | |
33 | ++ p_data->this = devfreq; | |
34 | + | |
35 | + switch (event) { | |
36 | + case DEVFREQ_GOV_START: | |
37 | +-- | |
38 | +2.37.2 | |
39 | + |
@@ -0,0 +1,269 @@ | ||
1 | +From 46d05776a1a5dd8eb479e868f5ff4f4b97d68238 Mon Sep 17 00:00:00 2001 | |
2 | +From: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com> | |
3 | +Date: Mon, 6 Jun 2022 12:39:19 +0200 | |
4 | +Subject: [PATCH v4 3/4] PM / devfreq: Rework freq_table to be local to devfreq | |
5 | + struct | |
6 | + | |
7 | +Currently we reference the freq_table to the profile defined one and we | |
8 | +make changes on it. Devfreq never supported PROBE_DEFER before the cpu | |
9 | +based scaling support to the passive governor and assumed that a devfreq | |
10 | +device could only had error and be done with it. | |
11 | +Now that a device can PROBE_DEFER a rework to the freq_table logic is | |
12 | +required. | |
13 | + | |
14 | +If a device PROBE_DEFER on the GOV_START, the freq_table is already set | |
15 | +in the device profile struct and its init is skipped. This is due to the | |
16 | +fact that it's common for devs to declare this kind of struct static. | |
17 | +This cause the devfreq logic to find a freq table declared (freq_table | |
18 | +not NULL) with random data and poiting to the old addrs freed by devm. | |
19 | + | |
20 | +This problem CAN be solved by devs by clearing the freq_table in their | |
21 | +profile struct on driver exit path but it should not be trusted and it | |
22 | +looks to use a flawed logic. | |
23 | + | |
24 | +A better solution is to move the freq_table and max_state to the | |
25 | +devfreq struct and never change the profile struct. | |
26 | +This permit to correctly handle PROBE_DEFER since the devfreq struct is | |
27 | +reallocated and contains new values. | |
28 | +Also the profile struct should only be used to init the driver and should | |
29 | +not be used by the devfreq to write the freq_table if it's not provided | |
30 | +by the driver. | |
31 | + | |
32 | +Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor") | |
33 | +Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com> | |
34 | +--- | |
35 | + drivers/devfreq/devfreq.c | 71 ++++++++++++++---------------- | |
36 | + drivers/devfreq/governor_passive.c | 14 +++--- | |
37 | + include/linux/devfreq.h | 4 ++ | |
38 | + 3 files changed, 45 insertions(+), 44 deletions(-) | |
39 | + | |
40 | +--- a/drivers/devfreq/devfreq.c | |
41 | ++++ b/drivers/devfreq/devfreq.c | |
42 | +@@ -123,7 +123,7 @@ void devfreq_get_freq_range(struct devfr | |
43 | + unsigned long *min_freq, | |
44 | + unsigned long *max_freq) | |
45 | + { | |
46 | +- unsigned long *freq_table = devfreq->profile->freq_table; | |
47 | ++ unsigned long *freq_table = devfreq->freq_table; | |
48 | + s32 qos_min_freq, qos_max_freq; | |
49 | + | |
50 | + lockdep_assert_held(&devfreq->lock); | |
51 | +@@ -133,11 +133,11 @@ void devfreq_get_freq_range(struct devfr | |
52 | + * The devfreq drivers can initialize this in either ascending or | |
53 | + * descending order and devfreq core supports both. | |
54 | + */ | |
55 | +- if (freq_table[0] < freq_table[devfreq->profile->max_state - 1]) { | |
56 | ++ if (freq_table[0] < freq_table[devfreq->max_state - 1]) { | |
57 | + *min_freq = freq_table[0]; | |
58 | +- *max_freq = freq_table[devfreq->profile->max_state - 1]; | |
59 | ++ *max_freq = freq_table[devfreq->max_state - 1]; | |
60 | + } else { | |
61 | +- *min_freq = freq_table[devfreq->profile->max_state - 1]; | |
62 | ++ *min_freq = freq_table[devfreq->max_state - 1]; | |
63 | + *max_freq = freq_table[0]; | |
64 | + } | |
65 | + | |
66 | +@@ -169,8 +169,8 @@ static int devfreq_get_freq_level(struct | |
67 | + { | |
68 | + int lev; | |
69 | + | |
70 | +- for (lev = 0; lev < devfreq->profile->max_state; lev++) | |
71 | +- if (freq == devfreq->profile->freq_table[lev]) | |
72 | ++ for (lev = 0; lev < devfreq->max_state; lev++) | |
73 | ++ if (freq == devfreq->freq_table[lev]) | |
74 | + return lev; | |
75 | + | |
76 | + return -EINVAL; | |
77 | +@@ -178,7 +178,6 @@ static int devfreq_get_freq_level(struct | |
78 | + | |
79 | + static int set_freq_table(struct devfreq *devfreq) | |
80 | + { | |
81 | +- struct devfreq_dev_profile *profile = devfreq->profile; | |
82 | + struct dev_pm_opp *opp; | |
83 | + unsigned long freq; | |
84 | + int i, count; | |
85 | +@@ -188,25 +187,22 @@ static int set_freq_table(struct devfreq | |
86 | + if (count <= 0) | |
87 | + return -EINVAL; | |
88 | + | |
89 | +- profile->max_state = count; | |
90 | +- profile->freq_table = devm_kcalloc(devfreq->dev.parent, | |
91 | +- profile->max_state, | |
92 | +- sizeof(*profile->freq_table), | |
93 | +- GFP_KERNEL); | |
94 | +- if (!profile->freq_table) { | |
95 | +- profile->max_state = 0; | |
96 | ++ devfreq->max_state = count; | |
97 | ++ devfreq->freq_table = devm_kcalloc(devfreq->dev.parent, | |
98 | ++ devfreq->max_state, | |
99 | ++ sizeof(*devfreq->freq_table), | |
100 | ++ GFP_KERNEL); | |
101 | ++ if (!devfreq->freq_table) | |
102 | + return -ENOMEM; | |
103 | +- } | |
104 | + | |
105 | +- for (i = 0, freq = 0; i < profile->max_state; i++, freq++) { | |
106 | ++ for (i = 0, freq = 0; i < devfreq->max_state; i++, freq++) { | |
107 | + opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &freq); | |
108 | + if (IS_ERR(opp)) { | |
109 | +- devm_kfree(devfreq->dev.parent, profile->freq_table); | |
110 | +- profile->max_state = 0; | |
111 | ++ devm_kfree(devfreq->dev.parent, devfreq->freq_table); | |
112 | + return PTR_ERR(opp); | |
113 | + } | |
114 | + dev_pm_opp_put(opp); | |
115 | +- profile->freq_table[i] = freq; | |
116 | ++ devfreq->freq_table[i] = freq; | |
117 | + } | |
118 | + | |
119 | + return 0; | |
120 | +@@ -246,7 +242,7 @@ int devfreq_update_status(struct devfreq | |
121 | + | |
122 | + if (lev != prev_lev) { | |
123 | + devfreq->stats.trans_table[ | |
124 | +- (prev_lev * devfreq->profile->max_state) + lev]++; | |
125 | ++ (prev_lev * devfreq->max_state) + lev]++; | |
126 | + devfreq->stats.total_trans++; | |
127 | + } | |
128 | + | |
129 | +@@ -835,6 +831,9 @@ struct devfreq *devfreq_add_device(struc | |
130 | + if (err < 0) | |
131 | + goto err_dev; | |
132 | + mutex_lock(&devfreq->lock); | |
133 | ++ } else { | |
134 | ++ devfreq->freq_table = devfreq->profile->freq_table; | |
135 | ++ devfreq->max_state = devfreq->profile->max_state; | |
136 | + } | |
137 | + | |
138 | + devfreq->scaling_min_freq = find_available_min_freq(devfreq); | |
139 | +@@ -870,8 +869,8 @@ struct devfreq *devfreq_add_device(struc | |
140 | + | |
141 | + devfreq->stats.trans_table = devm_kzalloc(&devfreq->dev, | |
142 | + array3_size(sizeof(unsigned int), | |
143 | +- devfreq->profile->max_state, | |
144 | +- devfreq->profile->max_state), | |
145 | ++ devfreq->max_state, | |
146 | ++ devfreq->max_state), | |
147 | + GFP_KERNEL); | |
148 | + if (!devfreq->stats.trans_table) { | |
149 | + mutex_unlock(&devfreq->lock); | |
150 | +@@ -880,7 +879,7 @@ struct devfreq *devfreq_add_device(struc | |
151 | + } | |
152 | + | |
153 | + devfreq->stats.time_in_state = devm_kcalloc(&devfreq->dev, | |
154 | +- devfreq->profile->max_state, | |
155 | ++ devfreq->max_state, | |
156 | + sizeof(*devfreq->stats.time_in_state), | |
157 | + GFP_KERNEL); | |
158 | + if (!devfreq->stats.time_in_state) { | |
159 | +@@ -1639,9 +1638,9 @@ static ssize_t available_frequencies_sho | |
160 | + | |
161 | + mutex_lock(&df->lock); | |
162 | + | |
163 | +- for (i = 0; i < df->profile->max_state; i++) | |
164 | ++ for (i = 0; i < df->max_state; i++) | |
165 | + count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), | |
166 | +- "%lu ", df->profile->freq_table[i]); | |
167 | ++ "%lu ", df->freq_table[i]); | |
168 | + | |
169 | + mutex_unlock(&df->lock); | |
170 | + /* Truncate the trailing space */ | |
171 | +@@ -1664,7 +1663,7 @@ static ssize_t trans_stat_show(struct de | |
172 | + | |
173 | + if (!df->profile) | |
174 | + return -EINVAL; | |
175 | +- max_state = df->profile->max_state; | |
176 | ++ max_state = df->max_state; | |
177 | + | |
178 | + if (max_state == 0) | |
179 | + return sprintf(buf, "Not Supported.\n"); | |
180 | +@@ -1681,19 +1680,17 @@ static ssize_t trans_stat_show(struct de | |
181 | + len += sprintf(buf + len, " :"); | |
182 | + for (i = 0; i < max_state; i++) | |
183 | + len += sprintf(buf + len, "%10lu", | |
184 | +- df->profile->freq_table[i]); | |
185 | ++ df->freq_table[i]); | |
186 | + | |
187 | + len += sprintf(buf + len, " time(ms)\n"); | |
188 | + | |
189 | + for (i = 0; i < max_state; i++) { | |
190 | +- if (df->profile->freq_table[i] | |
191 | +- == df->previous_freq) { | |
192 | ++ if (df->freq_table[i] == df->previous_freq) | |
193 | + len += sprintf(buf + len, "*"); | |
194 | +- } else { | |
195 | ++ else | |
196 | + len += sprintf(buf + len, " "); | |
197 | +- } | |
198 | +- len += sprintf(buf + len, "%10lu:", | |
199 | +- df->profile->freq_table[i]); | |
200 | ++ | |
201 | ++ len += sprintf(buf + len, "%10lu:", df->freq_table[i]); | |
202 | + for (j = 0; j < max_state; j++) | |
203 | + len += sprintf(buf + len, "%10u", | |
204 | + df->stats.trans_table[(i * max_state) + j]); | |
205 | +@@ -1717,7 +1714,7 @@ static ssize_t trans_stat_store(struct d | |
206 | + if (!df->profile) | |
207 | + return -EINVAL; | |
208 | + | |
209 | +- if (df->profile->max_state == 0) | |
210 | ++ if (df->max_state == 0) | |
211 | + return count; | |
212 | + | |
213 | + err = kstrtoint(buf, 10, &value); | |
214 | +@@ -1725,11 +1722,11 @@ static ssize_t trans_stat_store(struct d | |
215 | + return -EINVAL; | |
216 | + | |
217 | + mutex_lock(&df->lock); | |
218 | +- memset(df->stats.time_in_state, 0, (df->profile->max_state * | |
219 | ++ memset(df->stats.time_in_state, 0, (df->max_state * | |
220 | + sizeof(*df->stats.time_in_state))); | |
221 | + memset(df->stats.trans_table, 0, array3_size(sizeof(unsigned int), | |
222 | +- df->profile->max_state, | |
223 | +- df->profile->max_state)); | |
224 | ++ df->max_state, | |
225 | ++ df->max_state)); | |
226 | + df->stats.total_trans = 0; | |
227 | + df->stats.last_update = get_jiffies_64(); | |
228 | + mutex_unlock(&df->lock); | |
229 | +--- a/drivers/devfreq/governor_passive.c | |
230 | ++++ b/drivers/devfreq/governor_passive.c | |
231 | +@@ -145,18 +145,18 @@ static int get_target_freq_with_devfreq( | |
232 | + goto out; | |
233 | + | |
234 | + /* Use interpolation if required opps is not available */ | |
235 | +- for (i = 0; i < parent_devfreq->profile->max_state; i++) | |
236 | +- if (parent_devfreq->profile->freq_table[i] == *freq) | |
237 | ++ for (i = 0; i < parent_devfreq->max_state; i++) | |
238 | ++ if (parent_devfreq->freq_table[i] == *freq) | |
239 | + break; | |
240 | + | |
241 | +- if (i == parent_devfreq->profile->max_state) | |
242 | ++ if (i == parent_devfreq->max_state) | |
243 | + return -EINVAL; | |
244 | + | |
245 | +- if (i < devfreq->profile->max_state) { | |
246 | +- child_freq = devfreq->profile->freq_table[i]; | |
247 | ++ if (i < devfreq->max_state) { | |
248 | ++ child_freq = devfreq->freq_table[i]; | |
249 | + } else { | |
250 | +- count = devfreq->profile->max_state; | |
251 | +- child_freq = devfreq->profile->freq_table[count - 1]; | |
252 | ++ count = devfreq->max_state; | |
253 | ++ child_freq = devfreq->freq_table[count - 1]; | |
254 | + } | |
255 | + | |
256 | + out: | |
257 | +--- a/include/linux/devfreq.h | |
258 | ++++ b/include/linux/devfreq.h | |
259 | +@@ -185,6 +185,10 @@ struct devfreq { | |
260 | + struct notifier_block nb; | |
261 | + struct delayed_work work; | |
262 | + | |
263 | ++ /* devfreq local freq_table */ | |
264 | ++ unsigned long *freq_table; | |
265 | ++ unsigned int max_state; | |
266 | ++ | |
267 | + unsigned long previous_freq; | |
268 | + struct devfreq_dev_status last_status; | |
269 | + |
@@ -0,0 +1,28 @@ | ||
1 | +From eee9f767c41b03a2744d4b0f0c1a144e4ff41e78 Mon Sep 17 00:00:00 2001 | |
2 | +From: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com> | |
3 | +Date: Mon, 6 Jun 2022 13:01:02 +0200 | |
4 | +Subject: [PATCH v4 4/4] PM / devfreq: Mute warning on governor PROBE_DEFER | |
5 | + | |
6 | +Don't print warning when a governor PROBE_DEFER as it's not a real | |
7 | +GOV_START fail. | |
8 | + | |
9 | +Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor") | |
10 | +Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com> | |
11 | +--- | |
12 | + drivers/devfreq/devfreq.c | 5 +++-- | |
13 | + 1 file changed, 3 insertions(+), 2 deletions(-) | |
14 | + | |
15 | +--- a/drivers/devfreq/devfreq.c | |
16 | ++++ b/drivers/devfreq/devfreq.c | |
17 | +@@ -931,8 +931,9 @@ struct devfreq *devfreq_add_device(struc | |
18 | + err = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_START, | |
19 | + NULL); | |
20 | + if (err) { | |
21 | +- dev_err(dev, "%s: Unable to start governor for the device\n", | |
22 | +- __func__); | |
23 | ++ dev_err_probe(dev, err, | |
24 | ++ "%s: Unable to start governor for the device\n", | |
25 | ++ __func__); | |
26 | + goto err_init; | |
27 | + } | |
28 | + create_sysfs_files(devfreq, devfreq->governor); |