1 /*
2 * Copyright (c) 2017 Fuzhou Rockchip Electronics Co., Ltd
3 *
4 * SPDX-License-Identifier: GPL-2.0+
5 */
6 #include <linux/clk.h>
7 #include <linux/cpufreq.h>
8 #include <linux/devfreq.h>
9 #include <linux/mfd/syscon.h>
10 #include <linux/module.h>
11 #include <linux/nvmem-consumer.h>
12 #include <linux/regmap.h>
13 #include <linux/regulator/consumer.h>
14 #include <linux/slab.h>
15 #include <linux/soc/rockchip/pvtm.h>
16 #include <linux/thermal.h>
17 #include <linux/pm_opp.h>
18 #include <linux/version.h>
19 #include <soc/rockchip/rockchip_opp_select.h>
20
21 #include "../../clk/rockchip/clk.h"
22 #include "../../opp/opp.h"
23 #include "../../devfreq/governor.h"
24
25 #define MAX_PROP_NAME_LEN 6
26 #define SEL_TABLE_END ~1
27 #define AVS_DELETE_OPP 0
28 #define AVS_SCALING_RATE 1
29
30 #define LEAKAGE_V1 1
31 #define LEAKAGE_V2 2
32 #define LEAKAGE_V3 3
33
34 #define to_thermal_opp_info(nb) container_of(nb, struct thermal_opp_info, \
35 thermal_nb)
36
37 struct sel_table {
38 int min;
39 int max;
40 int sel;
41 };
42
43 struct bin_sel_table {
44 int bin;
45 int sel;
46 };
47
48 struct pvtm_config {
49 unsigned int freq;
50 unsigned int volt;
51 unsigned int ch[2];
52 unsigned int sample_time;
53 unsigned int num;
54 unsigned int err;
55 unsigned int ref_temp;
56 int temp_prop[2];
57 const char *tz_name;
58 struct thermal_zone_device *tz;
59 };
60
61 struct lkg_conversion_table {
62 int temp;
63 int conv;
64 };
65
66 #define PVTM_CH_MAX 8
67 #define PVTM_SUB_CH_MAX 8
68
69 #define FRAC_BITS 10
70 #define int_to_frac(x) ((x) << FRAC_BITS)
71 #define frac_to_int(x) ((x) >> FRAC_BITS)
72
73 static int pvtm_value[PVTM_CH_MAX][PVTM_SUB_CH_MAX];
74 static int lkg_version;
75
76 /*
77 * temp = temp * 10
78 * conv = exp(-ln(1.2) / 5 * (temp - 23)) * 100
79 */
80 static const struct lkg_conversion_table conv_table[] = {
81 { 200, 111 },
82 { 205, 109 },
83 { 210, 107 },
84 { 215, 105 },
85 { 220, 103 },
86 { 225, 101 },
87 { 230, 100 },
88 { 235, 98 },
89 { 240, 96 },
90 { 245, 94 },
91 { 250, 92 },
92 { 255, 91 },
93 { 260, 89 },
94 { 265, 88 },
95 { 270, 86 },
96 { 275, 84 },
97 { 280, 83 },
98 { 285, 81 },
99 { 290, 80 },
100 { 295, 78 },
101 { 300, 77 },
102 { 305, 76 },
103 { 310, 74 },
104 { 315, 73 },
105 { 320, 72 },
106 { 325, 70 },
107 { 330, 69 },
108 { 335, 68 },
109 { 340, 66 },
110 { 345, 65 },
111 { 350, 64 },
112 { 355, 63 },
113 { 360, 62 },
114 { 365, 61 },
115 { 370, 60 },
116 { 375, 58 },
117 { 380, 57 },
118 { 385, 56 },
119 { 390, 55 },
120 { 395, 54 },
121 { 400, 53 },
122 };
123
rockchip_nvmem_cell_read_common(struct device_node * np,const char * cell_id,void * val,size_t count)124 static int rockchip_nvmem_cell_read_common(struct device_node *np,
125 const char *cell_id,
126 void *val, size_t count)
127 {
128 struct nvmem_cell *cell;
129 void *buf;
130 size_t len;
131
132 cell = of_nvmem_cell_get(np, cell_id);
133 if (IS_ERR(cell))
134 return PTR_ERR(cell);
135
136 buf = nvmem_cell_read(cell, &len);
137 if (IS_ERR(buf)) {
138 nvmem_cell_put(cell);
139 return PTR_ERR(buf);
140 }
141 if (len != count) {
142 kfree(buf);
143 nvmem_cell_put(cell);
144 return -EINVAL;
145 }
146 memcpy(val, buf, count);
147 kfree(buf);
148 nvmem_cell_put(cell);
149
150 return 0;
151 }
152
rockchip_nvmem_cell_read_u8(struct device_node * np,const char * cell_id,u8 * val)153 int rockchip_nvmem_cell_read_u8(struct device_node *np, const char *cell_id,
154 u8 *val)
155 {
156 return rockchip_nvmem_cell_read_common(np, cell_id, val, sizeof(*val));
157 }
158 EXPORT_SYMBOL(rockchip_nvmem_cell_read_u8);
159
rockchip_nvmem_cell_read_u16(struct device_node * np,const char * cell_id,u16 * val)160 int rockchip_nvmem_cell_read_u16(struct device_node *np, const char *cell_id,
161 u16 *val)
162 {
163 return rockchip_nvmem_cell_read_common(np, cell_id, val, sizeof(*val));
164 }
165 EXPORT_SYMBOL(rockchip_nvmem_cell_read_u16);
166
rockchip_get_sel_table(struct device_node * np,char * porp_name,struct sel_table ** table)167 static int rockchip_get_sel_table(struct device_node *np, char *porp_name,
168 struct sel_table **table)
169 {
170 struct sel_table *sel_table;
171 const struct property *prop;
172 int count, i;
173
174 prop = of_find_property(np, porp_name, NULL);
175 if (!prop)
176 return -EINVAL;
177
178 if (!prop->value)
179 return -ENODATA;
180
181 count = of_property_count_u32_elems(np, porp_name);
182 if (count < 0)
183 return -EINVAL;
184
185 if (count % 3)
186 return -EINVAL;
187
188 sel_table = kzalloc(sizeof(*sel_table) * (count / 3 + 1), GFP_KERNEL);
189 if (!sel_table)
190 return -ENOMEM;
191
192 for (i = 0; i < count / 3; i++) {
193 of_property_read_u32_index(np, porp_name, 3 * i,
194 &sel_table[i].min);
195 of_property_read_u32_index(np, porp_name, 3 * i + 1,
196 &sel_table[i].max);
197 of_property_read_u32_index(np, porp_name, 3 * i + 2,
198 &sel_table[i].sel);
199 }
200 sel_table[i].min = 0;
201 sel_table[i].max = 0;
202 sel_table[i].sel = SEL_TABLE_END;
203
204 *table = sel_table;
205
206 return 0;
207 }
208
rockchip_get_bin_sel_table(struct device_node * np,char * porp_name,struct bin_sel_table ** table)209 static int rockchip_get_bin_sel_table(struct device_node *np, char *porp_name,
210 struct bin_sel_table **table)
211 {
212 struct bin_sel_table *sel_table;
213 const struct property *prop;
214 int count, i;
215
216 prop = of_find_property(np, porp_name, NULL);
217 if (!prop)
218 return -EINVAL;
219
220 if (!prop->value)
221 return -ENODATA;
222
223 count = of_property_count_u32_elems(np, porp_name);
224 if (count < 0)
225 return -EINVAL;
226
227 if (count % 2)
228 return -EINVAL;
229
230 sel_table = kzalloc(sizeof(*sel_table) * (count / 2 + 1), GFP_KERNEL);
231 if (!sel_table)
232 return -ENOMEM;
233
234 for (i = 0; i < count / 2; i++) {
235 of_property_read_u32_index(np, porp_name, 2 * i,
236 &sel_table[i].bin);
237 of_property_read_u32_index(np, porp_name, 2 * i + 1,
238 &sel_table[i].sel);
239 }
240
241 sel_table[i].bin = 0;
242 sel_table[i].sel = SEL_TABLE_END;
243
244 *table = sel_table;
245
246 return 0;
247 }
248
rockchip_get_sel(struct device_node * np,char * name,int value,int * sel)249 static int rockchip_get_sel(struct device_node *np, char *name,
250 int value, int *sel)
251 {
252 struct sel_table *table = NULL;
253 int i, ret = -EINVAL;
254
255 if (!sel)
256 return -EINVAL;
257
258 if (rockchip_get_sel_table(np, name, &table))
259 return -EINVAL;
260
261 for (i = 0; table[i].sel != SEL_TABLE_END; i++) {
262 if (value >= table[i].min) {
263 *sel = table[i].sel;
264 ret = 0;
265 }
266 }
267 kfree(table);
268
269 return ret;
270 }
271
rockchip_get_bin_sel(struct device_node * np,char * name,int value,int * sel)272 static int rockchip_get_bin_sel(struct device_node *np, char *name,
273 int value, int *sel)
274 {
275 struct bin_sel_table *table = NULL;
276 int i, ret = -EINVAL;
277
278 if (!sel)
279 return -EINVAL;
280
281 if (rockchip_get_bin_sel_table(np, name, &table))
282 return -EINVAL;
283
284 for (i = 0; table[i].sel != SEL_TABLE_END; i++) {
285 if (value == table[i].bin) {
286 *sel = table[i].sel;
287 ret = 0;
288 break;
289 }
290 }
291 kfree(table);
292
293 return ret;
294 }
295
rockchip_parse_pvtm_config(struct device_node * np,struct pvtm_config * pvtm)296 static int rockchip_parse_pvtm_config(struct device_node *np,
297 struct pvtm_config *pvtm)
298 {
299 if (of_property_read_u32(np, "rockchip,pvtm-freq", &pvtm->freq))
300 return -EINVAL;
301 if (of_property_read_u32(np, "rockchip,pvtm-volt", &pvtm->volt))
302 return -EINVAL;
303 if (of_property_read_u32_array(np, "rockchip,pvtm-ch", pvtm->ch, 2))
304 return -EINVAL;
305 if (pvtm->ch[0] >= PVTM_CH_MAX || pvtm->ch[1] >= PVTM_SUB_CH_MAX)
306 return -EINVAL;
307 if (of_property_read_u32(np, "rockchip,pvtm-sample-time",
308 &pvtm->sample_time))
309 return -EINVAL;
310 if (of_property_read_u32(np, "rockchip,pvtm-number", &pvtm->num))
311 return -EINVAL;
312 if (of_property_read_u32(np, "rockchip,pvtm-error", &pvtm->err))
313 return -EINVAL;
314 if (of_property_read_u32(np, "rockchip,pvtm-ref-temp", &pvtm->ref_temp))
315 return -EINVAL;
316 if (of_property_read_u32_array(np, "rockchip,pvtm-temp-prop",
317 pvtm->temp_prop, 2))
318 return -EINVAL;
319 if (of_property_read_string(np, "rockchip,pvtm-thermal-zone",
320 &pvtm->tz_name)) {
321 if (of_property_read_string(np, "rockchip,thermal-zone",
322 &pvtm->tz_name))
323 return -EINVAL;
324 }
325 pvtm->tz = thermal_zone_get_zone_by_name(pvtm->tz_name);
326 if (IS_ERR(pvtm->tz))
327 return -EINVAL;
328 if (!pvtm->tz->ops->get_temp)
329 return -EINVAL;
330
331 return 0;
332 }
333
rockchip_get_pvtm_specific_value(struct device * dev,struct device_node * np,struct clk * clk,struct regulator * reg,int * target_value)334 static int rockchip_get_pvtm_specific_value(struct device *dev,
335 struct device_node *np,
336 struct clk *clk,
337 struct regulator *reg,
338 int *target_value)
339 {
340 struct pvtm_config *pvtm;
341 unsigned long old_freq;
342 unsigned int old_volt;
343 int cur_temp, diff_temp;
344 int cur_value, total_value, avg_value, diff_value;
345 int min_value, max_value;
346 int ret = 0, i = 0, retry = 2;
347
348 pvtm = kzalloc(sizeof(*pvtm), GFP_KERNEL);
349 if (!pvtm)
350 return -ENOMEM;
351
352 ret = rockchip_parse_pvtm_config(np, pvtm);
353 if (ret)
354 goto pvtm_value_out;
355
356 old_freq = clk_get_rate(clk);
357 old_volt = regulator_get_voltage(reg);
358
359 /*
360 * Set pvtm_freq to the lowest frequency in dts,
361 * so change frequency first.
362 */
363 ret = clk_set_rate(clk, pvtm->freq * 1000);
364 if (ret) {
365 dev_err(dev, "Failed to set pvtm freq\n");
366 goto pvtm_value_out;
367 }
368
369 ret = regulator_set_voltage(reg, pvtm->volt, pvtm->volt);
370 if (ret) {
371 dev_err(dev, "Failed to set pvtm_volt\n");
372 goto restore_clk;
373 }
374
375 /* The first few values may be fluctuant, if error is too big, retry*/
376 while (retry--) {
377 total_value = 0;
378 min_value = INT_MAX;
379 max_value = 0;
380
381 for (i = 0; i < pvtm->num; i++) {
382 cur_value = rockchip_get_pvtm_value(pvtm->ch[0],
383 pvtm->ch[1],
384 pvtm->sample_time);
385 if (cur_value <= 0) {
386 ret = -EINVAL;
387 goto resetore_volt;
388 }
389 if (cur_value < min_value)
390 min_value = cur_value;
391 if (cur_value > max_value)
392 max_value = cur_value;
393 total_value += cur_value;
394 }
395 if (max_value - min_value < pvtm->err)
396 break;
397 }
398 if (!total_value || !pvtm->num) {
399 ret = -EINVAL;
400 goto resetore_volt;
401 }
402 avg_value = total_value / pvtm->num;
403
404 /*
405 * As pvtm is influenced by temperature, compute difference between
406 * current temperature and reference temperature
407 */
408 pvtm->tz->ops->get_temp(pvtm->tz, &cur_temp);
409 diff_temp = (cur_temp / 1000 - pvtm->ref_temp);
410 diff_value = diff_temp *
411 (diff_temp < 0 ? pvtm->temp_prop[0] : pvtm->temp_prop[1]);
412 *target_value = avg_value + diff_value;
413
414 pvtm_value[pvtm->ch[0]][pvtm->ch[1]] = *target_value;
415
416 dev_info(dev, "temp=%d, pvtm=%d (%d + %d)\n",
417 cur_temp, *target_value, avg_value, diff_value);
418
419 resetore_volt:
420 regulator_set_voltage(reg, old_volt, old_volt);
421 restore_clk:
422 clk_set_rate(clk, old_freq);
423 pvtm_value_out:
424 kfree(pvtm);
425
426 return ret;
427 }
428
429 /**
430 * mul_frac() - multiply two fixed-point numbers
431 * @x: first multiplicand
432 * @y: second multiplicand
433 *
434 * Return: the result of multiplying two fixed-point numbers. The
435 * result is also a fixed-point number.
436 */
mul_frac(s64 x,s64 y)437 static inline s64 mul_frac(s64 x, s64 y)
438 {
439 return (x * y) >> FRAC_BITS;
440 }
441
temp_to_conversion_rate(int temp)442 static int temp_to_conversion_rate(int temp)
443 {
444 int high, low, mid;
445
446 low = 0;
447 high = ARRAY_SIZE(conv_table) - 1;
448 mid = (high + low) / 2;
449
450 /* No temp available, return max conversion_rate */
451 if (temp <= conv_table[low].temp)
452 return conv_table[low].conv;
453 if (temp >= conv_table[high].temp)
454 return conv_table[high].conv;
455
456 while (low <= high) {
457 if (temp <= conv_table[mid].temp && temp >
458 conv_table[mid - 1].temp) {
459 return conv_table[mid - 1].conv +
460 (conv_table[mid].conv - conv_table[mid - 1].conv) *
461 (temp - conv_table[mid - 1].temp) /
462 (conv_table[mid].temp - conv_table[mid - 1].temp);
463 } else if (temp > conv_table[mid].temp) {
464 low = mid + 1;
465 } else {
466 high = mid - 1;
467 }
468 mid = (low + high) / 2;
469 }
470
471 return 100;
472 }
473
rockchip_adjust_leakage(struct device * dev,struct device_node * np,int * leakage)474 static int rockchip_adjust_leakage(struct device *dev, struct device_node *np,
475 int *leakage)
476 {
477 struct nvmem_cell *cell;
478 u8 value = 0;
479 u32 temp;
480 int conversion;
481 int ret;
482
483 cell = of_nvmem_cell_get(np, "leakage_temp");
484 if (IS_ERR(cell))
485 goto next;
486 nvmem_cell_put(cell);
487 ret = rockchip_nvmem_cell_read_u8(np, "leakage_temp", &value);
488 if (ret) {
489 dev_err(dev, "Failed to get leakage temp\n");
490 return -EINVAL;
491 }
492 /*
493 * The ambient temperature range: 20C to 40C
494 * In order to improve the precision, we do a conversion.
495 * The temp in efuse : temp_efuse = (temp - 20) / (40 - 20) * 63
496 * The ambient temp : temp = (temp_efuse / 63) * (40 - 20) + 20
497 * Reserves a decimal point : temp = temp * 10
498 */
499 temp = value;
500 temp = mul_frac((int_to_frac(temp) / 63 * 20 + int_to_frac(20)),
501 int_to_frac(10));
502 conversion = temp_to_conversion_rate(frac_to_int(temp));
503 *leakage = *leakage * conversion / 100;
504
505 next:
506 cell = of_nvmem_cell_get(np, "leakage_volt");
507 if (IS_ERR(cell))
508 return 0;
509 nvmem_cell_put(cell);
510 ret = rockchip_nvmem_cell_read_u8(np, "leakage_volt", &value);
511 if (ret) {
512 dev_err(dev, "Failed to get leakage volt\n");
513 return -EINVAL;
514 }
515 /*
516 * if ft write leakage use 1.35v, need convert to 1v.
517 * leakage(1v) = leakage(1.35v) / 4
518 */
519 if (value)
520 *leakage = *leakage / 4;
521
522 return 0;
523 }
524
rockchip_get_leakage_version(int * version)525 static int rockchip_get_leakage_version(int *version)
526 {
527 if (*version)
528 return 0;
529
530 if (of_machine_is_compatible("rockchip,rk3368"))
531 *version = LEAKAGE_V2;
532 else if (of_machine_is_compatible("rockchip,rv1126") ||
533 of_machine_is_compatible("rockchip,rv1109"))
534 *version = LEAKAGE_V3;
535 else
536 *version = LEAKAGE_V1;
537
538 return 0;
539 }
540
rockchip_get_leakage_v1(struct device * dev,struct device_node * np,char * lkg_name,int * leakage)541 static int rockchip_get_leakage_v1(struct device *dev, struct device_node *np,
542 char *lkg_name, int *leakage)
543 {
544 struct nvmem_cell *cell;
545 int ret = 0;
546 u8 value = 0;
547
548 cell = of_nvmem_cell_get(np, "leakage");
549 if (IS_ERR(cell)) {
550 ret = rockchip_nvmem_cell_read_u8(np, lkg_name, &value);
551 } else {
552 nvmem_cell_put(cell);
553 ret = rockchip_nvmem_cell_read_u8(np, "leakage", &value);
554 }
555 if (ret)
556 dev_err(dev, "Failed to get %s\n", lkg_name);
557 else
558 *leakage = value;
559
560 return ret;
561 }
562
rockchip_get_leakage_v2(struct device * dev,struct device_node * np,char * lkg_name,int * leakage)563 static int rockchip_get_leakage_v2(struct device *dev, struct device_node *np,
564 char *lkg_name, int *leakage)
565 {
566 int lkg = 0, ret = 0;
567
568 if (rockchip_get_leakage_v1(dev, np, lkg_name, &lkg))
569 return -EINVAL;
570
571 ret = rockchip_adjust_leakage(dev, np, &lkg);
572 if (ret)
573 dev_err(dev, "Failed to adjust leakage, value=%d\n", lkg);
574 else
575 *leakage = lkg;
576
577 return ret;
578 }
579
rockchip_get_leakage_v3(struct device * dev,struct device_node * np,char * lkg_name,int * leakage)580 static int rockchip_get_leakage_v3(struct device *dev, struct device_node *np,
581 char *lkg_name, int *leakage)
582 {
583 int lkg = 0;
584
585 if (rockchip_get_leakage_v1(dev, np, lkg_name, &lkg))
586 return -EINVAL;
587
588 *leakage = (((lkg & 0xf8) >> 3) * 1000) + ((lkg & 0x7) * 125);
589
590 return 0;
591 }
592
rockchip_of_get_leakage(struct device * dev,char * lkg_name,int * leakage)593 int rockchip_of_get_leakage(struct device *dev, char *lkg_name, int *leakage)
594 {
595 struct device_node *np;
596 int ret = -EINVAL;
597
598 np = of_parse_phandle(dev->of_node, "operating-points-v2", 0);
599 if (!np) {
600 dev_warn(dev, "OPP-v2 not supported\n");
601 return -ENOENT;
602 }
603
604 rockchip_get_leakage_version(&lkg_version);
605
606 switch (lkg_version) {
607 case LEAKAGE_V1:
608 ret = rockchip_get_leakage_v1(dev, np, lkg_name, leakage);
609 break;
610 case LEAKAGE_V2:
611 ret = rockchip_get_leakage_v2(dev, np, lkg_name, leakage);
612 break;
613 case LEAKAGE_V3:
614 ret = rockchip_get_leakage_v3(dev, np, lkg_name, leakage);
615 if (!ret) {
616 /*
617 * round up to the nearest whole number for calculating
618 * static power, it does not need to be precise.
619 */
620 if (*leakage % 1000 > 500)
621 *leakage = *leakage / 1000 + 1;
622 else
623 *leakage = *leakage / 1000;
624 }
625 break;
626 default:
627 break;
628 }
629
630 of_node_put(np);
631
632 return ret;
633 }
634 EXPORT_SYMBOL(rockchip_of_get_leakage);
635
rockchip_of_get_lkg_sel(struct device * dev,struct device_node * np,char * lkg_name,int process,int * volt_sel,int * scale_sel)636 void rockchip_of_get_lkg_sel(struct device *dev, struct device_node *np,
637 char *lkg_name, int process,
638 int *volt_sel, int *scale_sel)
639 {
640 struct property *prop = NULL;
641 int leakage = -EINVAL, ret = 0;
642 char name[NAME_MAX];
643
644 rockchip_get_leakage_version(&lkg_version);
645
646 switch (lkg_version) {
647 case LEAKAGE_V1:
648 ret = rockchip_get_leakage_v1(dev, np, lkg_name, &leakage);
649 if (ret)
650 return;
651 dev_info(dev, "leakage=%d\n", leakage);
652 break;
653 case LEAKAGE_V2:
654 ret = rockchip_get_leakage_v2(dev, np, lkg_name, &leakage);
655 if (ret)
656 return;
657 dev_info(dev, "leakage=%d\n", leakage);
658 break;
659 case LEAKAGE_V3:
660 ret = rockchip_get_leakage_v3(dev, np, lkg_name, &leakage);
661 if (ret)
662 return;
663 dev_info(dev, "leakage=%d.%d\n", leakage / 1000,
664 leakage % 1000);
665 break;
666 default:
667 return;
668 }
669
670 if (!volt_sel)
671 goto next;
672 if (process >= 0) {
673 snprintf(name, sizeof(name),
674 "rockchip,p%d-leakage-voltage-sel", process);
675 prop = of_find_property(np, name, NULL);
676 }
677 if (!prop)
678 sprintf(name, "rockchip,leakage-voltage-sel");
679 ret = rockchip_get_sel(np, name, leakage, volt_sel);
680 if (!ret)
681 dev_info(dev, "leakage-volt-sel=%d\n", *volt_sel);
682
683 next:
684 if (!scale_sel)
685 return;
686 if (process >= 0) {
687 snprintf(name, sizeof(name),
688 "rockchip,p%d-leakage-scaling-sel", process);
689 prop = of_find_property(np, name, NULL);
690 }
691 if (!prop)
692 sprintf(name, "rockchip,leakage-scaling-sel");
693 ret = rockchip_get_sel(np, name, leakage, scale_sel);
694 if (!ret)
695 dev_info(dev, "leakage-scale=%d\n", *scale_sel);
696 }
697 EXPORT_SYMBOL(rockchip_of_get_lkg_sel);
698
699
rockchip_get_pvtm(struct device * dev,struct device_node * np,char * reg_name)700 static int rockchip_get_pvtm(struct device *dev, struct device_node *np,
701 char *reg_name)
702 {
703 struct regulator *reg;
704 struct clk *clk;
705 unsigned int ch[2];
706 int pvtm = 0;
707 u16 tmp = 0;
708
709 if (!rockchip_nvmem_cell_read_u16(np, "pvtm", &tmp) && tmp) {
710 pvtm = 10 * tmp;
711 dev_info(dev, "pvtm = %d, from nvmem\n", pvtm);
712 return pvtm;
713 }
714
715 if (of_property_read_u32_array(np, "rockchip,pvtm-ch", ch, 2))
716 return -EINVAL;
717
718 if (ch[0] >= PVTM_CH_MAX || ch[1] >= PVTM_SUB_CH_MAX)
719 return -EINVAL;
720
721 if (pvtm_value[ch[0]][ch[1]]) {
722 dev_info(dev, "pvtm = %d, form pvtm_value\n", pvtm_value[ch[0]][ch[1]]);
723 return pvtm_value[ch[0]][ch[1]];
724 }
725
726 clk = clk_get(dev, NULL);
727 if (IS_ERR_OR_NULL(clk)) {
728 dev_warn(dev, "Failed to get clk\n");
729 return PTR_ERR_OR_ZERO(clk);
730 }
731
732 reg = regulator_get_optional(dev, reg_name);
733 if (IS_ERR_OR_NULL(reg)) {
734 dev_warn(dev, "Failed to get reg\n");
735 clk_put(clk);
736 return PTR_ERR_OR_ZERO(reg);
737 }
738
739 rockchip_get_pvtm_specific_value(dev, np, clk, reg, &pvtm);
740
741 regulator_put(reg);
742 clk_put(clk);
743
744 return pvtm;
745 }
746
rockchip_of_get_pvtm_sel(struct device * dev,struct device_node * np,char * reg_name,int process,int * volt_sel,int * scale_sel)747 void rockchip_of_get_pvtm_sel(struct device *dev, struct device_node *np,
748 char *reg_name, int process,
749 int *volt_sel, int *scale_sel)
750 {
751 struct property *prop = NULL;
752 char name[NAME_MAX];
753 int pvtm, ret;
754
755 pvtm = rockchip_get_pvtm(dev, np, reg_name);
756 if (pvtm <= 0)
757 return;
758
759 if (!volt_sel)
760 goto next;
761 if (process >= 0) {
762 snprintf(name, sizeof(name),
763 "rockchip,p%d-pvtm-voltage-sel", process);
764 prop = of_find_property(np, name, NULL);
765 }
766 if (!prop)
767 sprintf(name, "rockchip,pvtm-voltage-sel");
768 ret = rockchip_get_sel(np, name, pvtm, volt_sel);
769 if (!ret && volt_sel)
770 dev_info(dev, "pvtm-volt-sel=%d\n", *volt_sel);
771
772 next:
773 if (!scale_sel)
774 return;
775 if (process >= 0) {
776 snprintf(name, sizeof(name),
777 "rockchip,p%d-pvtm-scaling-sel", process);
778 prop = of_find_property(np, name, NULL);
779 }
780 if (!prop)
781 sprintf(name, "rockchip,pvtm-scaling-sel");
782 ret = rockchip_get_sel(np, name, pvtm, scale_sel);
783 if (!ret)
784 dev_info(dev, "pvtm-scale=%d\n", *scale_sel);
785 }
786 EXPORT_SYMBOL(rockchip_of_get_pvtm_sel);
787
rockchip_of_get_bin_sel(struct device * dev,struct device_node * np,int bin,int * scale_sel)788 void rockchip_of_get_bin_sel(struct device *dev, struct device_node *np,
789 int bin, int *scale_sel)
790 {
791 int ret = 0;
792
793 if (!scale_sel || bin < 0)
794 return;
795
796 ret = rockchip_get_bin_sel(np, "rockchip,bin-scaling-sel",
797 bin, scale_sel);
798 if (!ret)
799 dev_info(dev, "bin-scale=%d\n", *scale_sel);
800 }
801 EXPORT_SYMBOL(rockchip_of_get_bin_sel);
802
rockchip_of_get_bin_volt_sel(struct device * dev,struct device_node * np,int bin,int * bin_volt_sel)803 void rockchip_of_get_bin_volt_sel(struct device *dev, struct device_node *np,
804 int bin, int *bin_volt_sel)
805 {
806 int ret = 0;
807
808 if (!bin_volt_sel || bin < 0)
809 return;
810
811 ret = rockchip_get_bin_sel(np, "rockchip,bin-voltage-sel",
812 bin, bin_volt_sel);
813 if (!ret)
814 dev_info(dev, "bin-volt-sel=%d\n", *bin_volt_sel);
815 }
816 EXPORT_SYMBOL(rockchip_of_get_bin_volt_sel);
817
rockchip_get_opp_data(const struct of_device_id * matches,struct rockchip_opp_info * info)818 void rockchip_get_opp_data(const struct of_device_id *matches,
819 struct rockchip_opp_info *info)
820 {
821 const struct of_device_id *match;
822 struct device_node *node;
823
824 node = of_find_node_by_path("/");
825 match = of_match_node(matches, node);
826 if (match && match->data)
827 info->data = match->data;
828 of_node_put(node);
829 }
830 EXPORT_SYMBOL(rockchip_get_opp_data);
831
rockchip_get_volt_rm_table(struct device * dev,struct device_node * np,char * porp_name,struct volt_rm_table ** table)832 int rockchip_get_volt_rm_table(struct device *dev, struct device_node *np,
833 char *porp_name, struct volt_rm_table **table)
834 {
835 struct volt_rm_table *rm_table;
836 const struct property *prop;
837 int count, i;
838
839 prop = of_find_property(np, porp_name, NULL);
840 if (!prop)
841 return -EINVAL;
842
843 if (!prop->value)
844 return -ENODATA;
845
846 count = of_property_count_u32_elems(np, porp_name);
847 if (count < 0)
848 return -EINVAL;
849
850 if (count % 2)
851 return -EINVAL;
852
853 rm_table = devm_kzalloc(dev, sizeof(*rm_table) * (count / 2 + 1),
854 GFP_KERNEL);
855 if (!rm_table)
856 return -ENOMEM;
857
858 for (i = 0; i < count / 2; i++) {
859 of_property_read_u32_index(np, porp_name, 2 * i,
860 &rm_table[i].volt);
861 of_property_read_u32_index(np, porp_name, 2 * i + 1,
862 &rm_table[i].rm);
863 }
864
865 rm_table[i].volt = 0;
866 rm_table[i].rm = VOLT_RM_TABLE_END;
867
868 *table = rm_table;
869
870 return 0;
871 }
872 EXPORT_SYMBOL(rockchip_get_volt_rm_table);
873
rockchip_get_scale_volt_sel(struct device * dev,char * lkg_name,char * reg_name,int bin,int process,int * scale,int * volt_sel)874 void rockchip_get_scale_volt_sel(struct device *dev, char *lkg_name,
875 char *reg_name, int bin, int process,
876 int *scale, int *volt_sel)
877 {
878 struct device_node *np;
879 int lkg_scale = 0, pvtm_scale = 0, bin_scale = 0;
880 int lkg_volt_sel = -EINVAL, pvtm_volt_sel = -EINVAL;
881 int bin_volt_sel = -EINVAL;
882
883 np = of_parse_phandle(dev->of_node, "operating-points-v2", 0);
884 if (!np) {
885 dev_warn(dev, "OPP-v2 not supported\n");
886 return;
887 }
888
889 rockchip_of_get_lkg_sel(dev, np, lkg_name, process,
890 &lkg_volt_sel, &lkg_scale);
891 rockchip_of_get_pvtm_sel(dev, np, reg_name, process,
892 &pvtm_volt_sel, &pvtm_scale);
893 rockchip_of_get_bin_sel(dev, np, bin, &bin_scale);
894 rockchip_of_get_bin_volt_sel(dev, np, bin, &bin_volt_sel);
895 if (scale)
896 *scale = max3(lkg_scale, pvtm_scale, bin_scale);
897 if (volt_sel) {
898 if (bin_volt_sel >= 0)
899 *volt_sel = bin_volt_sel;
900 else
901 *volt_sel = max(lkg_volt_sel, pvtm_volt_sel);
902 }
903
904 of_node_put(np);
905 }
906 EXPORT_SYMBOL(rockchip_get_scale_volt_sel);
907
rockchip_set_opp_prop_name(struct device * dev,int process,int volt_sel)908 struct opp_table *rockchip_set_opp_prop_name(struct device *dev, int process,
909 int volt_sel)
910 {
911 char name[MAX_PROP_NAME_LEN];
912
913 if (process >= 0) {
914 if (volt_sel >= 0)
915 snprintf(name, MAX_PROP_NAME_LEN, "P%d-L%d",
916 process, volt_sel);
917 else
918 snprintf(name, MAX_PROP_NAME_LEN, "P%d", process);
919 } else if (volt_sel >= 0) {
920 snprintf(name, MAX_PROP_NAME_LEN, "L%d", volt_sel);
921 } else {
922 return NULL;
923 }
924
925 return dev_pm_opp_set_prop_name(dev, name);
926 }
927 EXPORT_SYMBOL(rockchip_set_opp_prop_name);
928
rockchip_adjust_opp_by_irdrop(struct device * dev,struct device_node * np,unsigned long * safe_rate,unsigned long * max_rate)929 static int rockchip_adjust_opp_by_irdrop(struct device *dev,
930 struct device_node *np,
931 unsigned long *safe_rate,
932 unsigned long *max_rate)
933 {
934 struct sel_table *irdrop_table = NULL;
935 struct opp_table *opp_table;
936 struct dev_pm_opp *opp;
937 int evb_irdrop = 0, board_irdrop, delta_irdrop;
938 int tmp_safe_rate = 0, opp_rate, i, ret = 0;
939 u32 max_volt = UINT_MAX;
940 bool reach_max_volt = false;
941
942 of_property_read_u32_index(np, "rockchip,max-volt", 0, &max_volt);
943 of_property_read_u32_index(np, "rockchip,evb-irdrop", 0, &evb_irdrop);
944 rockchip_get_sel_table(np, "rockchip,board-irdrop", &irdrop_table);
945
946 opp_table = dev_pm_opp_get_opp_table(dev);
947 if (!opp_table) {
948 ret = -ENOMEM;
949 goto out;
950 }
951
952 mutex_lock(&opp_table->lock);
953 list_for_each_entry(opp, &opp_table->opp_list, node) {
954 if (!irdrop_table) {
955 delta_irdrop = 0;
956 } else {
957 opp_rate = opp->rate / 1000000;
958 board_irdrop = -EINVAL;
959 for (i = 0; irdrop_table[i].sel != SEL_TABLE_END; i++) {
960 if (opp_rate >= irdrop_table[i].min)
961 board_irdrop = irdrop_table[i].sel;
962 }
963 if (board_irdrop == -EINVAL)
964 delta_irdrop = 0;
965 else
966 delta_irdrop = board_irdrop - evb_irdrop;
967 }
968 if ((opp->supplies[0].u_volt + delta_irdrop) <= max_volt) {
969 opp->supplies[0].u_volt += delta_irdrop;
970 opp->supplies[0].u_volt_min += delta_irdrop;
971 if (opp->supplies[0].u_volt_max + delta_irdrop <=
972 max_volt)
973 opp->supplies[0].u_volt_max += delta_irdrop;
974 else
975 opp->supplies[0].u_volt_max = max_volt;
976 if (!reach_max_volt)
977 tmp_safe_rate = opp->rate;
978 if (opp->supplies[0].u_volt == max_volt)
979 reach_max_volt = true;
980 } else {
981 opp->supplies[0].u_volt = max_volt;
982 opp->supplies[0].u_volt_min = max_volt;
983 opp->supplies[0].u_volt_max = max_volt;
984 }
985 if (max_rate)
986 *max_rate = opp->rate;
987 if (safe_rate && tmp_safe_rate != opp->rate)
988 *safe_rate = tmp_safe_rate;
989 }
990 mutex_unlock(&opp_table->lock);
991
992 dev_pm_opp_put_opp_table(opp_table);
993 out:
994 kfree(irdrop_table);
995
996 return ret;
997 }
998
rockchip_adjust_opp_by_mbist_vmin(struct device * dev,struct device_node * np)999 static void rockchip_adjust_opp_by_mbist_vmin(struct device *dev,
1000 struct device_node *np)
1001 {
1002 struct opp_table *opp_table;
1003 struct dev_pm_opp *opp;
1004 u32 vmin = 0;
1005 u8 index = 0;
1006
1007 if (rockchip_nvmem_cell_read_u8(np, "mbist-vmin", &index))
1008 return;
1009
1010 if (!index)
1011 return;
1012
1013 if (of_property_read_u32_index(np, "mbist-vmin", index-1, &vmin))
1014 return;
1015
1016 opp_table = dev_pm_opp_get_opp_table(dev);
1017 if (!opp_table)
1018 return;
1019
1020 mutex_lock(&opp_table->lock);
1021 list_for_each_entry(opp, &opp_table->opp_list, node) {
1022 if (opp->supplies->u_volt < vmin) {
1023 opp->supplies->u_volt = vmin;
1024 opp->supplies->u_volt_min = vmin;
1025 }
1026 }
1027 mutex_unlock(&opp_table->lock);
1028 }
1029
rockchip_adjust_opp_table(struct device * dev,unsigned long scale_rate)1030 static int rockchip_adjust_opp_table(struct device *dev,
1031 unsigned long scale_rate)
1032 {
1033 struct dev_pm_opp *opp;
1034 unsigned long rate;
1035 int i, count, ret = 0;
1036
1037 count = dev_pm_opp_get_opp_count(dev);
1038 if (count <= 0) {
1039 ret = count ? count : -ENODATA;
1040 goto out;
1041 }
1042
1043 for (i = 0, rate = 0; i < count; i++, rate++) {
1044 /* find next rate */
1045 opp = dev_pm_opp_find_freq_ceil(dev, &rate);
1046 if (IS_ERR(opp)) {
1047 ret = PTR_ERR(opp);
1048 goto out;
1049 }
1050 if (opp->rate > scale_rate)
1051 dev_pm_opp_remove(dev, opp->rate);
1052 dev_pm_opp_put(opp);
1053 }
1054 out:
1055 return ret;
1056 }
1057
rockchip_adjust_power_scale(struct device * dev,int scale)1058 int rockchip_adjust_power_scale(struct device *dev, int scale)
1059 {
1060 struct device_node *np;
1061 struct clk *clk;
1062 unsigned long safe_rate = 0, max_rate = 0;
1063 int irdrop_scale = 0, opp_scale = 0;
1064 u32 target_scale, avs = 0, avs_scale = 0;
1065 long scale_rate = 0;
1066 int ret = 0;
1067
1068 np = of_parse_phandle(dev->of_node, "operating-points-v2", 0);
1069 if (!np) {
1070 dev_warn(dev, "OPP-v2 not supported\n");
1071 return -ENOENT;
1072 }
1073 of_property_read_u32(np, "rockchip,avs-enable", &avs);
1074 of_property_read_u32(np, "rockchip,avs", &avs);
1075 of_property_read_u32(np, "rockchip,avs-scale", &avs_scale);
1076 rockchip_adjust_opp_by_mbist_vmin(dev, np);
1077 rockchip_adjust_opp_by_irdrop(dev, np, &safe_rate, &max_rate);
1078
1079 dev_info(dev, "avs=%d\n", avs);
1080 clk = of_clk_get_by_name(np, NULL);
1081 if (IS_ERR(clk)) {
1082 if (!safe_rate)
1083 goto out_np;
1084 dev_dbg(dev, "Failed to get clk, safe_rate=%lu\n", safe_rate);
1085 ret = rockchip_adjust_opp_table(dev, safe_rate);
1086 if (ret)
1087 dev_err(dev, "Failed to adjust opp table\n");
1088 goto out_np;
1089 }
1090
1091 if (safe_rate)
1092 irdrop_scale = rockchip_pll_clk_rate_to_scale(clk, safe_rate);
1093 if (max_rate)
1094 opp_scale = rockchip_pll_clk_rate_to_scale(clk, max_rate);
1095 target_scale = max(irdrop_scale, scale);
1096 if (target_scale <= 0)
1097 goto out_clk;
1098 dev_dbg(dev, "target_scale=%d, irdrop_scale=%d, scale=%d\n",
1099 target_scale, irdrop_scale, scale);
1100
1101 if (avs == AVS_SCALING_RATE) {
1102 ret = rockchip_pll_clk_adaptive_scaling(clk, target_scale);
1103 if (ret)
1104 dev_err(dev, "Failed to adaptive scaling\n");
1105 if (opp_scale >= avs_scale)
1106 goto out_clk;
1107 dev_info(dev, "avs-scale=%d, opp-scale=%d\n", avs_scale,
1108 opp_scale);
1109 scale_rate = rockchip_pll_clk_scale_to_rate(clk, avs_scale);
1110 if (scale_rate <= 0) {
1111 dev_err(dev, "Failed to get avs scale rate, %d\n",
1112 avs_scale);
1113 goto out_clk;
1114 }
1115 dev_dbg(dev, "scale_rate=%lu\n", scale_rate);
1116 ret = rockchip_adjust_opp_table(dev, scale_rate);
1117 if (ret)
1118 dev_err(dev, "Failed to adjust opp table\n");
1119 } else if (avs == AVS_DELETE_OPP) {
1120 if (opp_scale >= target_scale)
1121 goto out_clk;
1122 dev_info(dev, "target_scale=%d, opp-scale=%d\n", target_scale,
1123 opp_scale);
1124 scale_rate = rockchip_pll_clk_scale_to_rate(clk, target_scale);
1125 if (scale_rate <= 0) {
1126 dev_err(dev, "Failed to get scale rate, %d\n",
1127 target_scale);
1128 goto out_clk;
1129 }
1130 dev_dbg(dev, "scale_rate=%lu\n", scale_rate);
1131 ret = rockchip_adjust_opp_table(dev, scale_rate);
1132 if (ret)
1133 dev_err(dev, "Failed to adjust opp table\n");
1134 }
1135
1136 out_clk:
1137 clk_put(clk);
1138 out_np:
1139 of_node_put(np);
1140
1141 return ret;
1142 }
1143 EXPORT_SYMBOL(rockchip_adjust_power_scale);
1144
rockchip_init_opp_table(struct device * dev,struct rockchip_opp_info * info,char * lkg_name,char * reg_name)1145 int rockchip_init_opp_table(struct device *dev, struct rockchip_opp_info *info,
1146 char *lkg_name, char *reg_name)
1147 {
1148 struct device_node *np;
1149 int bin = -EINVAL, process = -EINVAL;
1150 int scale = 0, volt_sel = -EINVAL;
1151 int ret = 0, num_clks = 0, i;
1152
1153 /* Get OPP descriptor node */
1154 np = of_parse_phandle(dev->of_node, "operating-points-v2", 0);
1155 if (!np) {
1156 dev_dbg(dev, "Failed to find operating-points-v2\n");
1157 return -ENOENT;
1158 }
1159 if (!info)
1160 goto next;
1161
1162 num_clks = of_clk_get_parent_count(np);
1163 if (num_clks > 0) {
1164 info->clks = devm_kcalloc(dev, num_clks, sizeof(*info->clks),
1165 GFP_KERNEL);
1166 if (!info->clks) {
1167 ret = -ENOMEM;
1168 goto out;
1169 }
1170 for (i = 0; i < num_clks; i++) {
1171 info->clks[i].clk = of_clk_get(np, i);
1172 if (IS_ERR(info->clks[i].clk)) {
1173 ret = PTR_ERR(info->clks[i].clk);
1174 dev_err(dev, "%s: failed to get clk %d\n",
1175 np->name, i);
1176 goto out;
1177 }
1178 }
1179 info->num_clks = num_clks;
1180 }
1181 if (info->data && info->data->set_read_margin) {
1182 info->current_rm = UINT_MAX;
1183 info->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
1184 if (IS_ERR(info->grf))
1185 info->grf = NULL;
1186 rockchip_get_volt_rm_table(dev, np, "volt-mem-read-margin",
1187 &info->volt_rm_tbl);
1188 }
1189 if (info->data && info->data->get_soc_info)
1190 info->data->get_soc_info(dev, np, &bin, &process);
1191
1192 next:
1193 rockchip_get_scale_volt_sel(dev, lkg_name, reg_name, bin, process,
1194 &scale, &volt_sel);
1195 rockchip_set_opp_prop_name(dev, process, volt_sel);
1196 ret = dev_pm_opp_of_add_table(dev);
1197 if (ret) {
1198 dev_err(dev, "Invalid operating-points in device tree.\n");
1199 goto out;
1200 }
1201 rockchip_adjust_power_scale(dev, scale);
1202 out:
1203 of_node_put(np);
1204
1205 return ret;
1206 }
1207 EXPORT_SYMBOL(rockchip_init_opp_table);
1208
1209 MODULE_DESCRIPTION("ROCKCHIP OPP Select");
1210 MODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>, Liang Chen <cl@rock-chips.com>");
1211 MODULE_LICENSE("GPL");
1212