1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
4 */
5 #include <common.h>
6 #include <dm.h>
7 #include <clk.h>
8 #include <dvfs.h>
9 #include <thermal.h>
10 #include <linux/list.h>
11
12 #include <asm/arch/clock.h>
13 #include <power/regulator.h>
14 #ifdef CONFIG_ROCKCHIP_DMC
15 #include <asm/arch/rockchip_dmc.h>
16 #endif
17
18 /*
19 * # This is a simple wide temperature(ie. wtemp) dvfs driver, the policy is:
20 *
21 * 1. U-Boot parse cpu/dmc opp table from kernel dtb, anyone of
22 * "rockchip,low-temp = <...>" and "rockchip,high-temp = <...>" present in
23 * cpu/dmc nodes means wtemp is enabled.
24 *
25 * 1.1. When temperature trigger "rockchip,low-temp", increase 50mv voltage
26 * as target voltage. If target voltage is over "rockchip,max-volt",
27 * just set "rockchip,max-volt" as target voltage and lower 2 level freq,
28 *
29 * 1.2. When temperature trigger "rockchip,high-temp", just apply opp table[0]
30 * voltage and freq.
31 *
32 * 2. U-Boot parse cpu/dmc thermal zone "trip-point-0" temperature from kernel
33 * dtb, and apply the same rules as above [1.2] policy.
34 *
35 *
36 * # The dvfs policy apply moment is:
37 *
38 * 1. Appy it after clk and regulator drivers setup;
39 * 2. Repeat apply it by CONFIG_PREBOOT command until achieve the target
40 * temperature. user should add: #define CONFIG_PREBOOT "dvfs repeat" and
41 * assign repeat property in dts:
42 *
43 * uboot-wide-temperature {
44 * status = "okay";
45 * compatible = "rockchip,uboot-wide-temperature";
46 *
47 * cpu,low-temp-repeat;
48 * cpu,high-temp-repeat;
49 * dmc,low-temp-repeat;
50 * dmc,high-temp-repeat;
51 * };
52 */
53
54 #define FDT_PATH_CPUS "/cpus"
55 #define FDT_PATH_DMC "/dmc"
56 #define FDT_PATH_THREMAL_TRIP_POINT0 \
57 "/thermal-zones/soc-thermal/trips/trip-point-0"
58 #define FDT_PATH_THREMAL_COOLING_MAPS \
59 "/thermal-zones/soc-thermal/cooling-maps"
60
61 #define OPP_TABLE_MAX 20
62 #define RATE_LOWER_LEVEL_N 2
63 #define DIFF_VOLTAGE_UV 50000
64 #define TEMP_STRING_LEN 12
65 #define REPEAT_PERIOD_US 1000000
66
67 static LIST_HEAD(pm_e_head);
68
69 enum pm_id {
70 PM_CPU,
71 PM_DMC,
72 };
73
74 enum pm_event {
75 PM_EVT_NONE = 0x0,
76 PM_EVT_LOW = 0x1,
77 PM_EVT_HIGH = 0x2,
78 PM_EVT_BOTH = PM_EVT_LOW | PM_EVT_HIGH,
79 };
80
81 struct opp_table {
82 u64 hz;
83 u32 uv;
84 };
85
86 struct lmt_param {
87 int low_temp; /* milli degree */
88 int high_temp; /* milli degree */
89 int tz_temp; /* milli degree */
90 int max_volt; /* uV */
91
92 bool htemp_repeat;
93 bool ltemp_repeat;
94
95 bool ltemp_limit;
96 bool htemp_limit;
97 bool tztemp_limit;
98 };
99
100 struct pm_element {
101 int id;
102 const char *name;
103 const char *supply_name;
104 int volt_diff;
105 u32 opp_nr;
106 struct opp_table opp[OPP_TABLE_MAX];
107 struct lmt_param lmt;
108 struct udevice *supply;
109 struct clk clk;
110 struct list_head node;
111 };
112
113 struct wtemp_dvfs_priv {
114 struct udevice *thermal;
115 struct pm_element *cpu;
116 struct pm_element *dmc;
117 };
118
119 static struct pm_element pm_cpu = {
120 .id = PM_CPU,
121 .name = "cpu",
122 .supply_name = "cpu-supply",
123 .volt_diff = DIFF_VOLTAGE_UV,
124 };
125
126 static struct pm_element pm_dmc = {
127 .id = PM_DMC,
128 .name = "dmc",
129 .supply_name = "center-supply",
130 .volt_diff = DIFF_VOLTAGE_UV,
131 };
132
temp2string(int temp,char * data,int len)133 static void temp2string(int temp, char *data, int len)
134 {
135 int decimal_point;
136 int integer;
137
138 integer = abs(temp) / 1000;
139 decimal_point = abs(temp) % 1000;
140 snprintf(data, len, "%s%d.%d",
141 temp < 0 ? "-" : "", integer, decimal_point);
142 }
143
wtemp_get_lowlevel_rate(ulong rate,u32 level,struct pm_element * e)144 static ulong wtemp_get_lowlevel_rate(ulong rate, u32 level,
145 struct pm_element *e)
146 {
147 struct opp_table *opp;
148 int i, count, idx = 0;
149
150 opp = e->opp;
151 count = e->opp_nr;
152
153 for (i = 0; i < count; i++) {
154 if (opp[i].hz >= rate) {
155 idx = (i <= level) ? 0 : i - level;
156 break;
157 }
158 }
159
160 return opp[idx].hz;
161 }
162
__wtemp_clk_get_rate(struct pm_element * e)163 static ulong __wtemp_clk_get_rate(struct pm_element *e)
164 {
165 #ifdef CONFIG_ROCKCHIP_DMC
166 if (e->id == PM_DMC)
167 return rockchip_ddrclk_sip_recalc_rate_v2();
168 #endif
169 return clk_get_rate(&e->clk);
170 }
171
__wtemp_clk_set_rate(struct pm_element * e,ulong rate)172 static ulong __wtemp_clk_set_rate(struct pm_element *e, ulong rate)
173 {
174 #ifdef CONFIG_ROCKCHIP_DMC
175 if (e->id == PM_DMC) {
176 rate = rockchip_ddrclk_sip_round_rate_v2(rate);
177 rockchip_ddrclk_sip_set_rate_v2(rate);
178 } else
179 #endif
180 rate = clk_set_rate(&e->clk, rate);
181
182 return rate;
183 }
184
__wtemp_regulator_get_value(struct pm_element * e)185 static int __wtemp_regulator_get_value(struct pm_element *e)
186 {
187 return regulator_get_value(e->supply);
188 }
189
__wtemp_regulator_set_value(struct pm_element * e,int value)190 static int __wtemp_regulator_set_value(struct pm_element *e, int value)
191 {
192 return regulator_set_value(e->supply, value);
193 }
194
195 /*
196 * Policy: Increase voltage
197 *
198 * 1. target volt = original volt + diff volt;
199 * 2. If target volt is not over max_volt, just set it;
200 * 3. Otherwise set max_volt as target volt and lower the rate(front N level).
201 */
wtemp_dvfs_low_temp_adjust(struct udevice * dev,struct pm_element * e)202 static void wtemp_dvfs_low_temp_adjust(struct udevice *dev, struct pm_element *e)
203 {
204 struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
205 ulong org_rate, tgt_rate, rb_rate;
206 int org_volt, tgt_volt, rb_volt;
207
208 org_rate = __wtemp_clk_get_rate(e);
209 org_volt = __wtemp_regulator_get_value(e);
210 tgt_volt = org_volt + e->volt_diff;
211 if ((e->lmt.max_volt != -ENODATA) && (tgt_volt > e->lmt.max_volt)) {
212 tgt_volt = e->lmt.max_volt;
213 __wtemp_regulator_set_value(e, tgt_volt);
214 tgt_rate = wtemp_get_lowlevel_rate(org_rate,
215 RATE_LOWER_LEVEL_N, priv->cpu);
216 __wtemp_clk_set_rate(e, tgt_rate);
217 } else {
218 __wtemp_regulator_set_value(e, tgt_volt);
219 tgt_rate = org_rate;
220 }
221
222 /* Check */
223 rb_rate = __wtemp_clk_get_rate(e);
224 rb_volt = __wtemp_regulator_get_value(e);
225 if (tgt_rate != rb_rate)
226 printf("DVFS WARN: %s: target rate=%ld, readback rate=%ld !\n",
227 e->name, tgt_rate, rb_rate);
228 if (tgt_volt != rb_volt)
229 printf("DVFS WARN: %s: target volt=%d, readback volt=%d !\n",
230 e->name, tgt_volt, rb_volt);
231
232 printf("DVFS: %s(low): %ld->%ld Hz, %d->%d uV\n",
233 e->name, org_rate, rb_rate, org_volt, rb_volt);
234 }
235
236 /*
237 * Policy:
238 *
239 * Just set opp table[0] volt and rate, i.e. the lowest performance.
240 */
wtemp_dvfs_high_temp_adjust(struct udevice * dev,struct pm_element * e)241 static void wtemp_dvfs_high_temp_adjust(struct udevice *dev, struct pm_element *e)
242 {
243 ulong org_rate, tgt_rate, rb_rate;
244 int org_volt, tgt_volt, rb_volt;
245
246 /* Apply opp[0] */
247 org_rate = __wtemp_clk_get_rate(e);
248
249 tgt_rate = e->opp[0].hz;
250 __wtemp_clk_set_rate(e, tgt_rate);
251 rb_rate = __wtemp_clk_get_rate(e);
252 if (tgt_rate != rb_rate) {
253 printf("DVFS WARN: %s: target rate=%ld, readback rate=%ld !\n",
254 e->name, tgt_rate, rb_rate);
255 return;
256 }
257
258 org_volt = __wtemp_regulator_get_value(e);
259 tgt_volt = e->opp[0].uv;
260 __wtemp_regulator_set_value(e, tgt_volt);
261
262 rb_volt = __wtemp_regulator_get_value(e);
263 if (tgt_volt != rb_volt)
264 printf("DVFS WARN: %s: target volt=%d, readback volt=%d !\n",
265 e->name, tgt_volt, rb_volt);
266
267 printf("DVFS: %s(high): %ld->%ld Hz, %d->%d uV\n",
268 e->name, org_rate, tgt_rate, org_volt, tgt_volt);
269 }
270
wtemp_dvfs_is_effect(struct pm_element * e,int temp,enum pm_event evt)271 static bool wtemp_dvfs_is_effect(struct pm_element *e,
272 int temp, enum pm_event evt)
273 {
274 if (evt & PM_EVT_LOW) {
275 if (e->lmt.ltemp_limit && temp <= e->lmt.low_temp)
276 return false;
277 }
278
279 if (evt & PM_EVT_HIGH) {
280 if (e->lmt.tztemp_limit && temp >= e->lmt.tz_temp)
281 return false;
282 else if (e->lmt.htemp_limit && temp >= e->lmt.high_temp)
283 return false;
284 }
285
286 return true;
287 }
288
__wtemp_dvfs_apply(struct udevice * dev,struct pm_element * e,int temp,enum pm_event evt)289 static int __wtemp_dvfs_apply(struct udevice *dev, struct pm_element *e,
290 int temp, enum pm_event evt)
291 {
292 enum pm_event ret = PM_EVT_NONE;
293
294 if (evt & PM_EVT_LOW) {
295 /* Over lowest temperature: increase voltage */
296 if (e->lmt.ltemp_limit && temp <= e->lmt.low_temp) {
297 ret |= PM_EVT_LOW;
298 wtemp_dvfs_low_temp_adjust(dev, e);
299 }
300 }
301
302 if (evt & PM_EVT_HIGH) {
303 /* Over highest/thermal_zone temperature: decrease rate and voltage */
304 if (e->lmt.tztemp_limit && temp >= e->lmt.tz_temp) {
305 ret |= PM_EVT_HIGH;
306 wtemp_dvfs_high_temp_adjust(dev, e);
307 } else if (e->lmt.htemp_limit && temp >= e->lmt.high_temp) {
308 ret |= PM_EVT_HIGH;
309 wtemp_dvfs_high_temp_adjust(dev, e);
310 }
311 }
312
313 return ret;
314 }
315
__wtemp_common_ofdata_to_platdata(ofnode node,struct pm_element * e)316 static int __wtemp_common_ofdata_to_platdata(ofnode node, struct pm_element *e)
317 {
318 ofnode supply, opp_node;
319 u32 phandle, uv, clock[2];
320 uint64_t hz;
321 int ret;
322
323 /* Get regulator and clk */
324 if (!ofnode_read_u32(node, e->supply_name, &phandle)) {
325 supply = ofnode_get_by_phandle(phandle);
326 ret = regulator_get_by_devname(supply.np->name, &e->supply);
327 if (ret) {
328 printf("DVFS: %s: Get supply(%s) failed, ret=%d",
329 e->name, supply.np->full_name, ret);
330 return ret;
331 }
332 debug("DVFS: supply: %s\n", supply.np->full_name);
333 }
334
335 if (!ofnode_read_u32_array(node, "clocks", clock, ARRAY_SIZE(clock))) {
336 e->clk.id = clock[1];
337 ret = rockchip_get_clk(&e->clk.dev);
338 if (ret) {
339 printf("DVFS: %s: Get clk failed, ret=%d\n", e->name, ret);
340 return ret;
341 }
342 }
343
344 /* Get opp-table & limit param */
345 if (!ofnode_read_u32(node, "operating-points-v2", &phandle)) {
346 opp_node = ofnode_get_by_phandle(phandle);
347 e->lmt.low_temp = ofnode_read_s32_default(opp_node,
348 "rockchip,low-temp", -ENODATA);
349 e->lmt.high_temp = ofnode_read_s32_default(opp_node,
350 "rockchip,high-temp", -ENODATA);
351 e->lmt.max_volt = ofnode_read_u32_default(opp_node,
352 "rockchip,max-volt", -ENODATA);
353
354 debug("DVFS: %s: low-temp=%d, high-temp=%d, max-volt=%d\n",
355 e->name, e->lmt.low_temp, e->lmt.high_temp,
356 e->lmt.max_volt);
357
358 ofnode_for_each_subnode(node, opp_node) {
359 if (e->opp_nr >= OPP_TABLE_MAX) {
360 printf("DVFS: over max(%d) opp table items\n",
361 OPP_TABLE_MAX);
362 break;
363 }
364 ofnode_read_u64(node, "opp-hz", &hz);
365 ofnode_read_u32_array(node, "opp-microvolt", &uv, 1);
366 e->opp[e->opp_nr].hz = hz;
367 e->opp[e->opp_nr].uv = uv;
368 e->opp_nr++;
369 debug("DVFS: %s: opp[%d]: hz=%lld, uv=%d, %s\n",
370 e->name, e->opp_nr - 1,
371 hz, uv, ofnode_get_name(node));
372 }
373 }
374 if (!e->opp_nr) {
375 printf("DVFS: %s: Can't find opp table\n", e->name);
376 return -EINVAL;
377 }
378
379 if (e->lmt.max_volt == -ENODATA)
380 e->lmt.max_volt = e->opp[e->opp_nr - 1].uv;
381 if (e->lmt.low_temp != -ENODATA)
382 e->lmt.ltemp_limit = true;
383 if (e->lmt.high_temp != -ENODATA)
384 e->lmt.htemp_limit = true;
385
386 return 0;
387 }
388
wtemp_dvfs_apply(struct udevice * dev)389 static int wtemp_dvfs_apply(struct udevice *dev)
390 {
391 struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
392 struct list_head *node;
393 struct pm_element *e;
394 char s_temp[TEMP_STRING_LEN];
395 int temp, ret;
396
397 ret = thermal_get_temp(priv->thermal, &temp);
398 if (ret) {
399 printf("DVFS: Get temperature failed, ret=%d\n", ret);
400 return ret;
401 }
402
403 temp2string(temp, s_temp, TEMP_STRING_LEN);
404 printf("DVFS: %s'c\n", s_temp);
405
406 /* Apply dvfs policy for all pm element */
407 list_for_each(node, &pm_e_head) {
408 e = list_entry(node, struct pm_element, node);
409 __wtemp_dvfs_apply(dev, e, temp, PM_EVT_BOTH);
410 }
411
412 return 0;
413 }
414
wtemp_dvfs_repeat_apply(struct udevice * dev)415 static int wtemp_dvfs_repeat_apply(struct udevice *dev)
416 {
417 struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
418 struct list_head *node;
419 struct pm_element *e;
420 enum pm_event applied;
421 char s_temp[TEMP_STRING_LEN];
422 int temp, ret;
423
424 repeat:
425 ret = thermal_get_temp(priv->thermal, &temp);
426 if (ret) {
427 printf("DVFS: Get thermal temperature failed, ret=%d\n", ret);
428 return false;
429 }
430
431 /* Apply dvfs policy for all pm element if there is repeat request */
432 applied = PM_EVT_NONE;
433 list_for_each(node, &pm_e_head) {
434 e = list_entry(node, struct pm_element, node);
435 if (e->lmt.ltemp_repeat)
436 applied |= __wtemp_dvfs_apply(dev, e, temp, PM_EVT_LOW);
437 if (e->lmt.htemp_repeat)
438 applied |= __wtemp_dvfs_apply(dev, e, temp, PM_EVT_HIGH);
439 }
440
441 /* Everything is fine, exit */
442 if (applied == PM_EVT_NONE)
443 goto finish;
444
445 /* Check repeat result */
446 udelay(REPEAT_PERIOD_US);
447 list_for_each(node, &pm_e_head) {
448 e = list_entry(node, struct pm_element, node);
449 if (e->lmt.ltemp_repeat &&
450 !wtemp_dvfs_is_effect(e, temp, PM_EVT_LOW))
451 goto repeat;
452 if (e->lmt.htemp_repeat &&
453 !wtemp_dvfs_is_effect(e, temp, PM_EVT_HIGH))
454 goto repeat;
455 }
456
457 finish:
458 list_for_each(node, &pm_e_head) {
459 e = list_entry(node, struct pm_element, node);
460 temp2string(temp, s_temp, TEMP_STRING_LEN);
461 printf("DVFS: %s %s'c, %ld Hz, %d uV\n", e->name,
462 s_temp, __wtemp_clk_get_rate(e),
463 __wtemp_regulator_get_value(e));
464 }
465
466 return 0;
467 }
468
print_e_state(void)469 static void print_e_state(void)
470 {
471 struct pm_element *e;
472 struct list_head *node;
473 char s_low[TEMP_STRING_LEN];
474 char s_high[TEMP_STRING_LEN];
475 char s_tz[TEMP_STRING_LEN];
476
477 list_for_each(node, &pm_e_head) {
478 e = list_entry(node, struct pm_element, node);
479 if (!e->lmt.ltemp_limit &&
480 !e->lmt.htemp_limit && !e->lmt.tztemp_limit)
481 return;
482
483 temp2string(e->lmt.tz_temp, s_tz, TEMP_STRING_LEN);
484 temp2string(e->lmt.low_temp, s_low, TEMP_STRING_LEN);
485 temp2string(e->lmt.high_temp, s_high, TEMP_STRING_LEN);
486 printf("DVFS: %s: low=%s'c, high=%s'c, Vmax=%duV, tz_temp=%s'c, "
487 "h_repeat=%d, l_repeat=%d\n",
488 e->name, e->lmt.ltemp_limit ? s_low : NULL,
489 e->lmt.htemp_limit ? s_high : NULL,
490 e->lmt.max_volt,
491 e->lmt.tztemp_limit ? s_tz : NULL,
492 e->lmt.htemp_repeat, e->lmt.ltemp_repeat);
493 }
494 }
495
wtemp_dvfs_ofdata_to_platdata(struct udevice * dev)496 static int wtemp_dvfs_ofdata_to_platdata(struct udevice *dev)
497 {
498 struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
499 ofnode tz_trip0, cooling_maps, node;
500 ofnode cpus, cpu, dmc;
501 const char *name;
502 int ret, tz_temp;
503 u32 phandle;
504
505 INIT_LIST_HEAD(&pm_e_head);
506
507 /* 1. Parse cpu node */
508 priv->cpu = &pm_cpu;
509 cpus = ofnode_path(FDT_PATH_CPUS);
510 if (!ofnode_valid(cpus)) {
511 debug("DVFS: Can't find %s\n", FDT_PATH_CPUS);
512 goto parse_dmc;
513 }
514
515 ofnode_for_each_subnode(cpu, cpus) {
516 name = ofnode_get_property(cpu, "device_type", NULL);
517 if (!name)
518 continue;
519 if (!strcmp(name, "cpu")) {
520 ret = __wtemp_common_ofdata_to_platdata(cpu, priv->cpu);
521 if (ret)
522 return ret;
523 break;
524 }
525 }
526
527 priv->cpu->lmt.ltemp_repeat =
528 dev_read_bool(dev, "cpu,low-temp-repeat");
529 priv->cpu->lmt.htemp_repeat =
530 dev_read_bool(dev, "cpu,high-temp-repeat");
531
532 list_add_tail(&priv->cpu->node, &pm_e_head);
533
534 /* 2. Parse dmc node */
535 parse_dmc:
536 priv->dmc = &pm_dmc;
537 dmc = ofnode_path(FDT_PATH_DMC);
538 if (!ofnode_valid(dmc)) {
539 debug("DVFS: Can't find %s\n", FDT_PATH_CPUS);
540 goto parse_tz;
541 }
542 if (!IS_ENABLED(CONFIG_ROCKCHIP_DMC)) {
543 debug("DVFS: CONFIG_ROCKCHIP_DMC is disabled\n");
544 goto parse_tz;
545 }
546
547 ret = __wtemp_common_ofdata_to_platdata(dmc, priv->dmc);
548 if (ret)
549 return ret;
550
551 priv->dmc->lmt.ltemp_repeat =
552 dev_read_bool(dev, "dmc,low-temp-repeat");
553 priv->dmc->lmt.htemp_repeat =
554 dev_read_bool(dev, "dmc,high-temp-repeat");
555
556 list_add_tail(&priv->dmc->node, &pm_e_head);
557
558 /* 3. Parse thermal zone node */
559 parse_tz:
560 tz_trip0 = ofnode_path(FDT_PATH_THREMAL_TRIP_POINT0);
561 if (!ofnode_valid(tz_trip0)) {
562 debug("DVFS: Can't find %s\n", FDT_PATH_THREMAL_TRIP_POINT0);
563 goto finish;
564 }
565
566 tz_temp = ofnode_read_s32_default(tz_trip0, "temperature", -ENODATA);
567 if (tz_temp == -ENODATA) {
568 debug("DVFS: Can't get thermal zone trip0 temperature\n");
569 goto finish;
570 }
571
572 cooling_maps = ofnode_path(FDT_PATH_THREMAL_COOLING_MAPS);
573 if (!ofnode_valid(cooling_maps)) {
574 debug("DVFS: Can't find %s\n", FDT_PATH_THREMAL_COOLING_MAPS);
575 goto finish;
576 }
577
578 ofnode_for_each_subnode(node, cooling_maps) {
579 ofnode_read_u32_array(node, "cooling-device", &phandle, 1);
580 name = ofnode_get_name(ofnode_get_by_phandle(phandle));
581 if (!name)
582 continue;
583 if (strstr(name, "cpu")) {
584 priv->cpu->lmt.tztemp_limit = true;
585 priv->cpu->lmt.tz_temp = tz_temp;
586 } else if (strstr(name, "dmc")) {
587 priv->dmc->lmt.tztemp_limit = true;
588 priv->dmc->lmt.tz_temp = tz_temp;
589 }
590 }
591
592 finish:
593 print_e_state();
594
595 return 0;
596 }
597
598 static const struct dm_dvfs_ops wtemp_dvfs_ops = {
599 .apply = wtemp_dvfs_apply,
600 .repeat_apply = wtemp_dvfs_repeat_apply,
601 };
602
wtemp_dvfs_probe(struct udevice * dev)603 static int wtemp_dvfs_probe(struct udevice *dev)
604 {
605 struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
606 int ret;
607
608 #ifdef CONFIG_ROCKCHIP_DMC
609 struct udevice *ram_dev;
610
611 /* Init dmc */
612 ret = uclass_get_device(UCLASS_RAM, 0, &ram_dev);
613 if (ret) {
614 printf("DVFS: Get dmc device failed, ret=%d\n", ret);
615 return ret;
616 }
617 #endif
618 /* Init thermal */
619 ret = uclass_get_device(UCLASS_THERMAL, 0, &priv->thermal);
620 if (ret) {
621 printf("DVFS: Get thermal device failed, ret=%d\n", ret);
622 return ret;
623 }
624
625 return 0;
626 }
627
628 static const struct udevice_id wtemp_dvfs_match[] = {
629 { .compatible = "rockchip,uboot-wide-temperature", },
630 {},
631 };
632
633 U_BOOT_DRIVER(rockchip_wide_temp_dvfs) = {
634 .name = "rockchip_wide_temp_dvfs",
635 .id = UCLASS_DVFS,
636 .ops = &wtemp_dvfs_ops,
637 .of_match = wtemp_dvfs_match,
638 .probe = wtemp_dvfs_probe,
639 .ofdata_to_platdata = wtemp_dvfs_ofdata_to_platdata,
640 .priv_auto_alloc_size = sizeof(struct wtemp_dvfs_priv),
641 };
642