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 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 tgt_rate = __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: %s: target rate=%ld, readback rate=%ld !\n",
227 e->name, tgt_rate, rb_rate);
228 if (tgt_volt != rb_volt)
229 printf("DVFS: %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 tgt_rate = e->opp[0].hz;
249 tgt_rate = __wtemp_clk_set_rate(e, tgt_rate);
250
251 org_volt = __wtemp_regulator_get_value(e);
252 tgt_volt = e->opp[0].uv;
253 __wtemp_regulator_set_value(e, tgt_volt);
254
255 /* Check */
256 rb_rate = __wtemp_clk_get_rate(e);
257 rb_volt = __wtemp_regulator_get_value(e);
258 if (tgt_rate != rb_rate)
259 printf("DVFS: %s: target rate=%ld, readback rate=%ld !\n",
260 e->name, tgt_rate, rb_rate);
261 if (tgt_volt != rb_volt)
262 printf("DVFS: %s: target volt=%d, readback volt=%d !\n",
263 e->name, tgt_volt, rb_volt);
264
265 printf("DVFS: %s(high): %ld->%ld Hz, %d->%d uV\n",
266 e->name, org_rate, tgt_rate, org_volt, tgt_volt);
267 }
268
wtemp_dvfs_is_effect(struct pm_element * e,int temp,enum pm_event evt)269 static bool wtemp_dvfs_is_effect(struct pm_element *e,
270 int temp, enum pm_event evt)
271 {
272 if (evt & PM_EVT_LOW) {
273 if (e->lmt.ltemp_limit && temp <= e->lmt.low_temp)
274 return false;
275 }
276
277 if (evt & PM_EVT_HIGH) {
278 if (e->lmt.tztemp_limit && temp >= e->lmt.tz_temp)
279 return false;
280 else if (e->lmt.htemp_limit && temp >= e->lmt.high_temp)
281 return false;
282 }
283
284 return true;
285 }
286
__wtemp_dvfs_apply(struct udevice * dev,struct pm_element * e,int temp,enum pm_event evt)287 static int __wtemp_dvfs_apply(struct udevice *dev, struct pm_element *e,
288 int temp, enum pm_event evt)
289 {
290 enum pm_event ret = PM_EVT_NONE;
291
292 if (evt & PM_EVT_LOW) {
293 /* Over lowest temperature: increase voltage */
294 if (e->lmt.ltemp_limit && temp <= e->lmt.low_temp) {
295 ret |= PM_EVT_LOW;
296 wtemp_dvfs_low_temp_adjust(dev, e);
297 }
298 }
299
300 if (evt & PM_EVT_HIGH) {
301 /* Over highest/thermal_zone temperature: decrease rate and voltage */
302 if (e->lmt.tztemp_limit && temp >= e->lmt.tz_temp) {
303 ret |= PM_EVT_HIGH;
304 wtemp_dvfs_high_temp_adjust(dev, e);
305 } else if (e->lmt.htemp_limit && temp >= e->lmt.high_temp) {
306 ret |= PM_EVT_HIGH;
307 wtemp_dvfs_high_temp_adjust(dev, e);
308 }
309 }
310
311 return ret;
312 }
313
__wtemp_common_ofdata_to_platdata(ofnode node,struct pm_element * e)314 static int __wtemp_common_ofdata_to_platdata(ofnode node, struct pm_element *e)
315 {
316 ofnode supply, opp_node;
317 u32 phandle, uv, clock[2];
318 uint64_t hz;
319 int ret;
320
321 /* Get regulator and clk */
322 if (!ofnode_read_u32(node, e->supply_name, &phandle)) {
323 supply = ofnode_get_by_phandle(phandle);
324 ret = regulator_get_by_devname(supply.np->name, &e->supply);
325 if (ret) {
326 printf("DVFS: %s: Get supply(%s) failed, ret=%d",
327 e->name, supply.np->full_name, ret);
328 return ret;
329 }
330 debug("DVFS: supply: %s\n", supply.np->full_name);
331 }
332
333 if (!ofnode_read_u32_array(node, "clocks", clock, ARRAY_SIZE(clock))) {
334 e->clk.id = clock[1];
335 ret = rockchip_get_clk(&e->clk.dev);
336 if (ret) {
337 printf("DVFS: %s: Get clk failed, ret=%d\n", e->name, ret);
338 return ret;
339 }
340 }
341
342 /* Get opp-table & limit param */
343 if (!ofnode_read_u32(node, "operating-points-v2", &phandle)) {
344 opp_node = ofnode_get_by_phandle(phandle);
345 e->lmt.low_temp = ofnode_read_s32_default(opp_node,
346 "rockchip,low-temp", -ENODATA);
347 e->lmt.high_temp = ofnode_read_s32_default(opp_node,
348 "rockchip,high-temp", -ENODATA);
349 e->lmt.max_volt = ofnode_read_u32_default(opp_node,
350 "rockchip,max-volt", -ENODATA);
351
352 debug("DVFS: %s: low-temp=%d, high-temp=%d, max-volt=%d\n",
353 e->name, e->lmt.low_temp, e->lmt.high_temp,
354 e->lmt.max_volt);
355
356 ofnode_for_each_subnode(node, opp_node) {
357 if (e->opp_nr >= OPP_TABLE_MAX) {
358 printf("DVFS: over max(%d) opp table items\n",
359 OPP_TABLE_MAX);
360 break;
361 }
362 ofnode_read_u64(node, "opp-hz", &hz);
363 ofnode_read_u32_array(node, "opp-microvolt", &uv, 1);
364 e->opp[e->opp_nr].hz = hz;
365 e->opp[e->opp_nr].uv = uv;
366 e->opp_nr++;
367 debug("DVFS: %s: opp[%d]: hz=%lld, uv=%d, %s\n",
368 e->name, e->opp_nr - 1,
369 hz, uv, ofnode_get_name(node));
370 }
371 }
372 if (!e->opp_nr) {
373 printf("DVFS: %s: Can't find opp table\n", e->name);
374 return -EINVAL;
375 }
376
377 if (e->lmt.max_volt == -ENODATA)
378 e->lmt.max_volt = e->opp[e->opp_nr - 1].uv;
379 if (e->lmt.low_temp != -ENODATA)
380 e->lmt.ltemp_limit = true;
381 if (e->lmt.high_temp != -ENODATA)
382 e->lmt.htemp_limit = true;
383
384 return 0;
385 }
386
wtemp_dvfs_apply(struct udevice * dev)387 static int wtemp_dvfs_apply(struct udevice *dev)
388 {
389 struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
390 struct list_head *node;
391 struct pm_element *e;
392 char s_temp[TEMP_STRING_LEN];
393 int temp, ret;
394
395 ret = thermal_get_temp(priv->thermal, &temp);
396 if (ret) {
397 printf("DVFS: Get temperature failed, ret=%d\n", ret);
398 return ret;
399 }
400
401 temp2string(temp, s_temp, TEMP_STRING_LEN);
402 printf("DVFS: %s'c\n", s_temp);
403
404 /* Apply dvfs policy for all pm element */
405 list_for_each(node, &pm_e_head) {
406 e = list_entry(node, struct pm_element, node);
407 __wtemp_dvfs_apply(dev, e, temp, PM_EVT_BOTH);
408 }
409
410 return 0;
411 }
412
wtemp_dvfs_repeat_apply(struct udevice * dev)413 static int wtemp_dvfs_repeat_apply(struct udevice *dev)
414 {
415 struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
416 struct list_head *node;
417 struct pm_element *e;
418 enum pm_event applied;
419 char s_temp[TEMP_STRING_LEN];
420 int temp, ret;
421
422 repeat:
423 ret = thermal_get_temp(priv->thermal, &temp);
424 if (ret) {
425 printf("DVFS: Get thermal temperature failed, ret=%d\n", ret);
426 return false;
427 }
428
429 /* Apply dvfs policy for all pm element if there is repeat request */
430 applied = PM_EVT_NONE;
431 list_for_each(node, &pm_e_head) {
432 e = list_entry(node, struct pm_element, node);
433 if (e->lmt.ltemp_repeat)
434 applied |= __wtemp_dvfs_apply(dev, e, temp, PM_EVT_LOW);
435 if (e->lmt.htemp_repeat)
436 applied |= __wtemp_dvfs_apply(dev, e, temp, PM_EVT_HIGH);
437 }
438
439 /* Everything is fine, exit */
440 if (applied == PM_EVT_NONE)
441 goto finish;
442
443 /* Check repeat result */
444 udelay(REPEAT_PERIOD_US);
445 list_for_each(node, &pm_e_head) {
446 e = list_entry(node, struct pm_element, node);
447 if (e->lmt.ltemp_repeat &&
448 !wtemp_dvfs_is_effect(e, temp, PM_EVT_LOW))
449 goto repeat;
450 if (e->lmt.htemp_repeat &&
451 !wtemp_dvfs_is_effect(e, temp, PM_EVT_HIGH))
452 goto repeat;
453 }
454
455 finish:
456 list_for_each(node, &pm_e_head) {
457 e = list_entry(node, struct pm_element, node);
458 temp2string(temp, s_temp, TEMP_STRING_LEN);
459 printf("DVFS: %s %s'c, %ld Hz, %d uV\n", e->name,
460 s_temp, __wtemp_clk_get_rate(e),
461 __wtemp_regulator_get_value(e));
462 }
463
464 return 0;
465 }
466
print_e_state(void)467 static void print_e_state(void)
468 {
469 struct pm_element *e;
470 struct list_head *node;
471 char s_low[TEMP_STRING_LEN];
472 char s_high[TEMP_STRING_LEN];
473 char s_tz[TEMP_STRING_LEN];
474
475 list_for_each(node, &pm_e_head) {
476 e = list_entry(node, struct pm_element, node);
477 if (!e->lmt.ltemp_limit &&
478 !e->lmt.htemp_limit && !e->lmt.tztemp_limit)
479 return;
480
481 temp2string(e->lmt.tz_temp, s_tz, TEMP_STRING_LEN);
482 temp2string(e->lmt.low_temp, s_low, TEMP_STRING_LEN);
483 temp2string(e->lmt.high_temp, s_high, TEMP_STRING_LEN);
484 printf("DVFS: %s: low=%s'c, high=%s'c, Vmax=%duV, tz_temp=%s'c, "
485 "h_repeat=%d, l_repeat=%d\n",
486 e->name, e->lmt.ltemp_limit ? s_low : NULL,
487 e->lmt.htemp_limit ? s_high : NULL,
488 e->lmt.max_volt,
489 e->lmt.tztemp_limit ? s_tz : NULL,
490 e->lmt.htemp_repeat, e->lmt.ltemp_repeat);
491 }
492 }
493
wtemp_dvfs_ofdata_to_platdata(struct udevice * dev)494 static int wtemp_dvfs_ofdata_to_platdata(struct udevice *dev)
495 {
496 struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
497 ofnode tz_trip0, cooling_maps, node;
498 ofnode cpus, cpu, dmc;
499 const char *name;
500 int ret, tz_temp;
501 u32 phandle;
502
503 INIT_LIST_HEAD(&pm_e_head);
504
505 /* 1. Parse cpu node */
506 priv->cpu = &pm_cpu;
507 cpus = ofnode_path(FDT_PATH_CPUS);
508 if (!ofnode_valid(cpus)) {
509 debug("DVFS: Can't find %s\n", FDT_PATH_CPUS);
510 goto parse_dmc;
511 }
512
513 ofnode_for_each_subnode(cpu, cpus) {
514 name = ofnode_get_property(cpu, "device_type", NULL);
515 if (!name)
516 continue;
517 if (!strcmp(name, "cpu")) {
518 ret = __wtemp_common_ofdata_to_platdata(cpu, priv->cpu);
519 if (ret)
520 return ret;
521 break;
522 }
523 }
524
525 priv->cpu->lmt.ltemp_repeat =
526 dev_read_bool(dev, "cpu,low-temp-repeat");
527 priv->cpu->lmt.htemp_repeat =
528 dev_read_bool(dev, "cpu,high-temp-repeat");
529
530 list_add_tail(&priv->cpu->node, &pm_e_head);
531
532 /* 2. Parse dmc node */
533 parse_dmc:
534 priv->dmc = &pm_dmc;
535 dmc = ofnode_path(FDT_PATH_DMC);
536 if (!ofnode_valid(dmc)) {
537 debug("DVFS: Can't find %s\n", FDT_PATH_CPUS);
538 goto parse_tz;
539 }
540 if (!IS_ENABLED(CONFIG_ROCKCHIP_DMC)) {
541 debug("DVFS: CONFIG_ROCKCHIP_DMC is disabled\n");
542 goto parse_tz;
543 }
544
545 ret = __wtemp_common_ofdata_to_platdata(dmc, priv->dmc);
546 if (ret)
547 return ret;
548
549 priv->dmc->lmt.ltemp_repeat =
550 dev_read_bool(dev, "dmc,low-temp-repeat");
551 priv->dmc->lmt.htemp_repeat =
552 dev_read_bool(dev, "dmc,high-temp-repeat");
553
554 list_add_tail(&priv->dmc->node, &pm_e_head);
555
556 /* 3. Parse thermal zone node */
557 parse_tz:
558 tz_trip0 = ofnode_path(FDT_PATH_THREMAL_TRIP_POINT0);
559 if (!ofnode_valid(tz_trip0)) {
560 debug("DVFS: Can't find %s\n", FDT_PATH_THREMAL_TRIP_POINT0);
561 goto finish;
562 }
563
564 tz_temp = ofnode_read_s32_default(tz_trip0, "temperature", -ENODATA);
565 if (tz_temp == -ENODATA) {
566 debug("DVFS: Can't get thermal zone trip0 temperature\n");
567 goto finish;
568 }
569
570 cooling_maps = ofnode_path(FDT_PATH_THREMAL_COOLING_MAPS);
571 if (!ofnode_valid(cooling_maps)) {
572 debug("DVFS: Can't find %s\n", FDT_PATH_THREMAL_COOLING_MAPS);
573 goto finish;
574 }
575
576 ofnode_for_each_subnode(node, cooling_maps) {
577 ofnode_read_u32_array(node, "cooling-device", &phandle, 1);
578 name = ofnode_get_name(ofnode_get_by_phandle(phandle));
579 if (!name)
580 continue;
581 if (strstr(name, "cpu")) {
582 priv->cpu->lmt.tztemp_limit = true;
583 priv->cpu->lmt.tz_temp = tz_temp;
584 } else if (strstr(name, "dmc")) {
585 priv->dmc->lmt.tztemp_limit = true;
586 priv->dmc->lmt.tz_temp = tz_temp;
587 }
588 }
589
590 finish:
591 print_e_state();
592
593 return 0;
594 }
595
596 static const struct dm_dvfs_ops wtemp_dvfs_ops = {
597 .apply = wtemp_dvfs_apply,
598 .repeat_apply = wtemp_dvfs_repeat_apply,
599 };
600
wtemp_dvfs_probe(struct udevice * dev)601 static int wtemp_dvfs_probe(struct udevice *dev)
602 {
603 struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
604 int ret;
605
606 #ifdef CONFIG_ROCKCHIP_DMC
607 struct udevice *ram_dev;
608
609 /* Init dmc */
610 ret = uclass_get_device(UCLASS_RAM, 0, &ram_dev);
611 if (ret) {
612 printf("DVFS: Get dmc device failed, ret=%d\n", ret);
613 return ret;
614 }
615 #endif
616 /* Init thermal */
617 ret = uclass_get_device(UCLASS_THERMAL, 0, &priv->thermal);
618 if (ret) {
619 printf("DVFS: Get thermal device failed, ret=%d\n", ret);
620 return ret;
621 }
622
623 return 0;
624 }
625
626 static const struct udevice_id wtemp_dvfs_match[] = {
627 { .compatible = "rockchip,uboot-wide-temperature", },
628 {},
629 };
630
631 U_BOOT_DRIVER(rockchip_wide_temp_dvfs) = {
632 .name = "rockchip_wide_temp_dvfs",
633 .id = UCLASS_DVFS,
634 .ops = &wtemp_dvfs_ops,
635 .of_match = wtemp_dvfs_match,
636 .probe = wtemp_dvfs_probe,
637 .ofdata_to_platdata = wtemp_dvfs_ofdata_to_platdata,
638 .priv_auto_alloc_size = sizeof(struct wtemp_dvfs_priv),
639 };
640