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