1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#include <linux/clk.h>
23#include <linux/cpufreq.h>
24#include <linux/device.h>
25#include <linux/err.h>
26#include <linux/idr.h>
27#include <linux/mutex.h>
28#include <linux/pm_opp.h>
29#include <linux/slab.h>
30#include <linux/thermal.h>
31#include <linux/clock_cooling.h>
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55struct clock_cooling_device {
56 int id;
57 struct device *dev;
58 struct thermal_cooling_device *cdev;
59 struct notifier_block clk_rate_change_nb;
60 struct cpufreq_frequency_table *freq_table;
61 unsigned long clock_state;
62 unsigned long clock_val;
63 struct clk *clk;
64 struct mutex lock;
65};
66#define to_clock_cooling_device(x) \
67 container_of(x, struct clock_cooling_device, clk_rate_change_nb)
68static DEFINE_IDA(clock_ida);
69
70
71
72enum clock_cooling_property {
73 GET_LEVEL,
74 GET_FREQ,
75 GET_MAXL,
76};
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97static int clock_cooling_get_property(struct clock_cooling_device *ccdev,
98 unsigned long input,
99 unsigned long *output,
100 enum clock_cooling_property property)
101{
102 int i;
103 unsigned long max_level = 0, level = 0;
104 unsigned int freq = CPUFREQ_ENTRY_INVALID;
105 int descend = -1;
106 struct cpufreq_frequency_table *pos, *table = ccdev->freq_table;
107
108 if (!output)
109 return -EINVAL;
110
111 if (!table)
112 return -EINVAL;
113
114 cpufreq_for_each_valid_entry(pos, table) {
115
116 if (freq == pos->frequency)
117 continue;
118
119
120 if (freq != CPUFREQ_ENTRY_INVALID && descend == -1)
121 descend = freq > pos->frequency;
122
123 freq = pos->frequency;
124 max_level++;
125 }
126
127
128 if (max_level == 0)
129 return -EINVAL;
130
131
132 max_level--;
133
134
135 if (property == GET_MAXL) {
136 *output = max_level;
137 return 0;
138 }
139
140 if (property == GET_FREQ)
141 level = descend ? input : (max_level - input);
142
143 i = 0;
144 cpufreq_for_each_valid_entry(pos, table) {
145
146 if (freq == pos->frequency)
147 continue;
148
149
150 freq = pos->frequency;
151
152 if (property == GET_LEVEL && (unsigned int)input == freq) {
153
154 *output = descend ? i : (max_level - i);
155 return 0;
156 }
157 if (property == GET_FREQ && level == i) {
158
159 *output = freq;
160 return 0;
161 }
162 i++;
163 }
164
165 return -EINVAL;
166}
167
168
169
170
171
172
173
174
175
176
177
178
179unsigned long clock_cooling_get_level(struct thermal_cooling_device *cdev,
180 unsigned long freq)
181{
182 struct clock_cooling_device *ccdev = cdev->devdata;
183 unsigned long val;
184
185 if (clock_cooling_get_property(ccdev, (unsigned long)freq, &val,
186 GET_LEVEL))
187 return THERMAL_CSTATE_INVALID;
188
189 return val;
190}
191EXPORT_SYMBOL_GPL(clock_cooling_get_level);
192
193
194
195
196
197
198
199
200
201
202
203
204
205static unsigned long
206clock_cooling_get_frequency(struct clock_cooling_device *ccdev,
207 unsigned long level)
208{
209 int ret = 0;
210 unsigned long freq;
211
212 ret = clock_cooling_get_property(ccdev, level, &freq, GET_FREQ);
213 if (ret)
214 return 0;
215
216 return freq;
217}
218
219
220
221
222
223
224
225
226
227
228
229
230
231static int clock_cooling_apply(struct clock_cooling_device *ccdev,
232 unsigned long cooling_state)
233{
234 unsigned long clip_freq, cur_freq;
235 int ret = 0;
236
237
238
239 if (ccdev->clock_state == cooling_state)
240 return 0;
241
242 clip_freq = clock_cooling_get_frequency(ccdev, cooling_state);
243 if (!clip_freq)
244 return -EINVAL;
245
246 cur_freq = clk_get_rate(ccdev->clk);
247
248 mutex_lock(&ccdev->lock);
249 ccdev->clock_state = cooling_state;
250 ccdev->clock_val = clip_freq;
251
252 if (cur_freq > clip_freq)
253 ret = clk_set_rate(ccdev->clk, clip_freq);
254 mutex_unlock(&ccdev->lock);
255
256 return ret;
257}
258
259
260
261
262
263
264
265
266
267
268
269
270
271static int clock_cooling_clock_notifier(struct notifier_block *nb,
272 unsigned long event, void *data)
273{
274 struct clk_notifier_data *ndata = data;
275 struct clock_cooling_device *ccdev = to_clock_cooling_device(nb);
276
277 switch (event) {
278 case PRE_RATE_CHANGE:
279
280
281
282
283
284
285 if (ndata->new_rate > ccdev->clock_val)
286 return NOTIFY_BAD;
287
288 case POST_RATE_CHANGE:
289 case ABORT_RATE_CHANGE:
290 default:
291 return NOTIFY_DONE;
292 }
293}
294
295
296
297
298
299
300
301
302
303
304
305
306
307static int clock_cooling_get_max_state(struct thermal_cooling_device *cdev,
308 unsigned long *state)
309{
310 struct clock_cooling_device *ccdev = cdev->devdata;
311 unsigned long count = 0;
312 int ret;
313
314 ret = clock_cooling_get_property(ccdev, 0, &count, GET_MAXL);
315 if (!ret)
316 *state = count;
317
318 return ret;
319}
320
321
322
323
324
325
326
327
328
329
330
331static int clock_cooling_get_cur_state(struct thermal_cooling_device *cdev,
332 unsigned long *state)
333{
334 struct clock_cooling_device *ccdev = cdev->devdata;
335
336 *state = ccdev->clock_state;
337
338 return 0;
339}
340
341
342
343
344
345
346
347
348
349
350
351static int clock_cooling_set_cur_state(struct thermal_cooling_device *cdev,
352 unsigned long state)
353{
354 struct clock_cooling_device *clock_device = cdev->devdata;
355
356 return clock_cooling_apply(clock_device, state);
357}
358
359
360static struct thermal_cooling_device_ops const clock_cooling_ops = {
361 .get_max_state = clock_cooling_get_max_state,
362 .get_cur_state = clock_cooling_get_cur_state,
363 .set_cur_state = clock_cooling_set_cur_state,
364};
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382struct thermal_cooling_device *
383clock_cooling_register(struct device *dev, const char *clock_name)
384{
385 struct thermal_cooling_device *cdev;
386 struct clock_cooling_device *ccdev = NULL;
387 char dev_name[THERMAL_NAME_LENGTH];
388 int ret = 0;
389
390 ccdev = devm_kzalloc(dev, sizeof(*ccdev), GFP_KERNEL);
391 if (!ccdev)
392 return ERR_PTR(-ENOMEM);
393
394 mutex_init(&ccdev->lock);
395 ccdev->dev = dev;
396 ccdev->clk = devm_clk_get(dev, clock_name);
397 if (IS_ERR(ccdev->clk))
398 return ERR_CAST(ccdev->clk);
399
400 ret = ida_simple_get(&clock_ida, 0, 0, GFP_KERNEL);
401 if (ret < 0)
402 return ERR_PTR(ret);
403 ccdev->id = ret;
404
405 snprintf(dev_name, sizeof(dev_name), "thermal-clock-%d", ccdev->id);
406
407 cdev = thermal_cooling_device_register(dev_name, ccdev,
408 &clock_cooling_ops);
409 if (IS_ERR(cdev)) {
410 ida_simple_remove(&clock_ida, ccdev->id);
411 return ERR_PTR(-EINVAL);
412 }
413 ccdev->cdev = cdev;
414 ccdev->clk_rate_change_nb.notifier_call = clock_cooling_clock_notifier;
415
416
417 ret = dev_pm_opp_init_cpufreq_table(dev, &ccdev->freq_table);
418 if (ret) {
419 ida_simple_remove(&clock_ida, ccdev->id);
420 return ERR_PTR(ret);
421 }
422 ccdev->clock_state = 0;
423 ccdev->clock_val = clock_cooling_get_frequency(ccdev, 0);
424
425 clk_notifier_register(ccdev->clk, &ccdev->clk_rate_change_nb);
426
427 return cdev;
428}
429EXPORT_SYMBOL_GPL(clock_cooling_register);
430
431
432
433
434
435
436
437void clock_cooling_unregister(struct thermal_cooling_device *cdev)
438{
439 struct clock_cooling_device *ccdev;
440
441 if (!cdev)
442 return;
443
444 ccdev = cdev->devdata;
445
446 clk_notifier_unregister(ccdev->clk, &ccdev->clk_rate_change_nb);
447 dev_pm_opp_free_cpufreq_table(ccdev->dev, &ccdev->freq_table);
448
449 thermal_cooling_device_unregister(ccdev->cdev);
450 ida_simple_remove(&clock_ida, ccdev->id);
451}
452EXPORT_SYMBOL_GPL(clock_cooling_unregister);
453