10eea0d25SJoseph Chen // SPDX-License-Identifier: GPL-2.0
20eea0d25SJoseph Chen /*
30eea0d25SJoseph Chen * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
40eea0d25SJoseph Chen */
50eea0d25SJoseph Chen #include <common.h>
60eea0d25SJoseph Chen #include <dm.h>
70eea0d25SJoseph Chen #include <clk.h>
80eea0d25SJoseph Chen #include <dvfs.h>
90eea0d25SJoseph Chen #include <thermal.h>
100eea0d25SJoseph Chen #include <linux/list.h>
110eea0d25SJoseph Chen
120eea0d25SJoseph Chen #include <asm/arch/clock.h>
130eea0d25SJoseph Chen #include <power/regulator.h>
140eea0d25SJoseph Chen #ifdef CONFIG_ROCKCHIP_DMC
150eea0d25SJoseph Chen #include <asm/arch/rockchip_dmc.h>
160eea0d25SJoseph Chen #endif
170eea0d25SJoseph Chen
180eea0d25SJoseph Chen /*
190eea0d25SJoseph Chen * # This is a simple wide temperature(ie. wtemp) dvfs driver, the policy is:
200eea0d25SJoseph Chen *
210eea0d25SJoseph Chen * 1. U-Boot parse cpu/dmc opp table from kernel dtb, anyone of
220eea0d25SJoseph Chen * "rockchip,low-temp = <...>" and "rockchip,high-temp = <...>" present in
230eea0d25SJoseph Chen * cpu/dmc nodes means wtemp is enabled.
240eea0d25SJoseph Chen *
250eea0d25SJoseph Chen * 1.1. When temperature trigger "rockchip,low-temp", increase 50mv voltage
260eea0d25SJoseph Chen * as target voltage. If target voltage is over "rockchip,max-volt",
270eea0d25SJoseph Chen * just set "rockchip,max-volt" as target voltage and lower 2 level freq,
280eea0d25SJoseph Chen *
290eea0d25SJoseph Chen * 1.2. When temperature trigger "rockchip,high-temp", just apply opp table[0]
300eea0d25SJoseph Chen * voltage and freq.
310eea0d25SJoseph Chen *
320eea0d25SJoseph Chen * 2. U-Boot parse cpu/dmc thermal zone "trip-point-0" temperature from kernel
330eea0d25SJoseph Chen * dtb, and apply the same rules as above [1.2] policy.
340eea0d25SJoseph Chen *
350eea0d25SJoseph Chen *
360eea0d25SJoseph Chen * # The dvfs policy apply moment is:
370eea0d25SJoseph Chen *
380eea0d25SJoseph Chen * 1. Appy it after clk and regulator drivers setup;
390eea0d25SJoseph Chen * 2. Repeat apply it by CONFIG_PREBOOT command until achieve the target
400eea0d25SJoseph Chen * temperature. user should add: #define CONFIG_PREBOOT "dvfs repeat" and
410eea0d25SJoseph Chen * assign repeat property in dts:
420eea0d25SJoseph Chen *
430eea0d25SJoseph Chen * uboot-wide-temperature {
440eea0d25SJoseph Chen * status = "okay";
450eea0d25SJoseph Chen * compatible = "rockchip,uboot-wide-temperature";
460eea0d25SJoseph Chen *
470eea0d25SJoseph Chen * cpu,low-temp-repeat;
480eea0d25SJoseph Chen * cpu,high-temp-repeat;
490eea0d25SJoseph Chen * dmc,low-temp-repeat;
500eea0d25SJoseph Chen * dmc,high-temp-repeat;
510eea0d25SJoseph Chen * };
520eea0d25SJoseph Chen */
530eea0d25SJoseph Chen
540eea0d25SJoseph Chen #define FDT_PATH_CPUS "/cpus"
550eea0d25SJoseph Chen #define FDT_PATH_DMC "/dmc"
560eea0d25SJoseph Chen #define FDT_PATH_THREMAL_TRIP_POINT0 \
570eea0d25SJoseph Chen "/thermal-zones/soc-thermal/trips/trip-point-0"
580eea0d25SJoseph Chen #define FDT_PATH_THREMAL_COOLING_MAPS \
590eea0d25SJoseph Chen "/thermal-zones/soc-thermal/cooling-maps"
600eea0d25SJoseph Chen
610eea0d25SJoseph Chen #define OPP_TABLE_MAX 20
620eea0d25SJoseph Chen #define RATE_LOWER_LEVEL_N 2
630eea0d25SJoseph Chen #define DIFF_VOLTAGE_UV 50000
640eea0d25SJoseph Chen #define TEMP_STRING_LEN 12
650eea0d25SJoseph Chen #define REPEAT_PERIOD_US 1000000
660eea0d25SJoseph Chen
670eea0d25SJoseph Chen static LIST_HEAD(pm_e_head);
680eea0d25SJoseph Chen
690eea0d25SJoseph Chen enum pm_id {
700eea0d25SJoseph Chen PM_CPU,
710eea0d25SJoseph Chen PM_DMC,
720eea0d25SJoseph Chen };
730eea0d25SJoseph Chen
740eea0d25SJoseph Chen enum pm_event {
750eea0d25SJoseph Chen PM_EVT_NONE = 0x0,
760eea0d25SJoseph Chen PM_EVT_LOW = 0x1,
770eea0d25SJoseph Chen PM_EVT_HIGH = 0x2,
780eea0d25SJoseph Chen PM_EVT_BOTH = PM_EVT_LOW | PM_EVT_HIGH,
790eea0d25SJoseph Chen };
800eea0d25SJoseph Chen
810eea0d25SJoseph Chen struct opp_table {
820eea0d25SJoseph Chen u64 hz;
830eea0d25SJoseph Chen u32 uv;
840eea0d25SJoseph Chen };
850eea0d25SJoseph Chen
860eea0d25SJoseph Chen struct lmt_param {
870eea0d25SJoseph Chen int low_temp; /* milli degree */
880eea0d25SJoseph Chen int high_temp; /* milli degree */
890eea0d25SJoseph Chen int tz_temp; /* milli degree */
900eea0d25SJoseph Chen int max_volt; /* uV */
910eea0d25SJoseph Chen
920eea0d25SJoseph Chen bool htemp_repeat;
930eea0d25SJoseph Chen bool ltemp_repeat;
940eea0d25SJoseph Chen
950eea0d25SJoseph Chen bool ltemp_limit;
960eea0d25SJoseph Chen bool htemp_limit;
970eea0d25SJoseph Chen bool tztemp_limit;
980eea0d25SJoseph Chen };
990eea0d25SJoseph Chen
1000eea0d25SJoseph Chen struct pm_element {
1010eea0d25SJoseph Chen int id;
1020eea0d25SJoseph Chen const char *name;
1030eea0d25SJoseph Chen const char *supply_name;
1040eea0d25SJoseph Chen int volt_diff;
1050eea0d25SJoseph Chen u32 opp_nr;
1060eea0d25SJoseph Chen struct opp_table opp[OPP_TABLE_MAX];
1070eea0d25SJoseph Chen struct lmt_param lmt;
1080eea0d25SJoseph Chen struct udevice *supply;
1090eea0d25SJoseph Chen struct clk clk;
1100eea0d25SJoseph Chen struct list_head node;
1110eea0d25SJoseph Chen };
1120eea0d25SJoseph Chen
1130eea0d25SJoseph Chen struct wtemp_dvfs_priv {
1140eea0d25SJoseph Chen struct udevice *thermal;
1150eea0d25SJoseph Chen struct pm_element *cpu;
1160eea0d25SJoseph Chen struct pm_element *dmc;
1170eea0d25SJoseph Chen };
1180eea0d25SJoseph Chen
1190eea0d25SJoseph Chen static struct pm_element pm_cpu = {
1200eea0d25SJoseph Chen .id = PM_CPU,
1210eea0d25SJoseph Chen .name = "cpu",
1220eea0d25SJoseph Chen .supply_name = "cpu-supply",
1230eea0d25SJoseph Chen .volt_diff = DIFF_VOLTAGE_UV,
1240eea0d25SJoseph Chen };
1250eea0d25SJoseph Chen
1260eea0d25SJoseph Chen static struct pm_element pm_dmc = {
1270eea0d25SJoseph Chen .id = PM_DMC,
1280eea0d25SJoseph Chen .name = "dmc",
1290eea0d25SJoseph Chen .supply_name = "center-supply",
1300eea0d25SJoseph Chen .volt_diff = DIFF_VOLTAGE_UV,
1310eea0d25SJoseph Chen };
1320eea0d25SJoseph Chen
temp2string(int temp,char * data,int len)1330eea0d25SJoseph Chen static void temp2string(int temp, char *data, int len)
1340eea0d25SJoseph Chen {
1350eea0d25SJoseph Chen int decimal_point;
1360eea0d25SJoseph Chen int integer;
1370eea0d25SJoseph Chen
1380eea0d25SJoseph Chen integer = abs(temp) / 1000;
1390eea0d25SJoseph Chen decimal_point = abs(temp) % 1000;
1400eea0d25SJoseph Chen snprintf(data, len, "%s%d.%d",
1410eea0d25SJoseph Chen temp < 0 ? "-" : "", integer, decimal_point);
1420eea0d25SJoseph Chen }
1430eea0d25SJoseph Chen
wtemp_get_lowlevel_rate(ulong rate,u32 level,struct pm_element * e)1440eea0d25SJoseph Chen static ulong wtemp_get_lowlevel_rate(ulong rate, u32 level,
1450eea0d25SJoseph Chen struct pm_element *e)
1460eea0d25SJoseph Chen {
1470eea0d25SJoseph Chen struct opp_table *opp;
1480eea0d25SJoseph Chen int i, count, idx = 0;
1490eea0d25SJoseph Chen
1500eea0d25SJoseph Chen opp = e->opp;
1510eea0d25SJoseph Chen count = e->opp_nr;
1520eea0d25SJoseph Chen
1530eea0d25SJoseph Chen for (i = 0; i < count; i++) {
1540eea0d25SJoseph Chen if (opp[i].hz >= rate) {
1550eea0d25SJoseph Chen idx = (i <= level) ? 0 : i - level;
1560eea0d25SJoseph Chen break;
1570eea0d25SJoseph Chen }
1580eea0d25SJoseph Chen }
1590eea0d25SJoseph Chen
1600eea0d25SJoseph Chen return opp[idx].hz;
1610eea0d25SJoseph Chen }
1620eea0d25SJoseph Chen
__wtemp_clk_get_rate(struct pm_element * e)1630eea0d25SJoseph Chen static ulong __wtemp_clk_get_rate(struct pm_element *e)
1640eea0d25SJoseph Chen {
1650eea0d25SJoseph Chen #ifdef CONFIG_ROCKCHIP_DMC
1660eea0d25SJoseph Chen if (e->id == PM_DMC)
1670eea0d25SJoseph Chen return rockchip_ddrclk_sip_recalc_rate_v2();
1680eea0d25SJoseph Chen #endif
1690eea0d25SJoseph Chen return clk_get_rate(&e->clk);
1700eea0d25SJoseph Chen }
1710eea0d25SJoseph Chen
__wtemp_clk_set_rate(struct pm_element * e,ulong rate)1720eea0d25SJoseph Chen static ulong __wtemp_clk_set_rate(struct pm_element *e, ulong rate)
1730eea0d25SJoseph Chen {
1740eea0d25SJoseph Chen #ifdef CONFIG_ROCKCHIP_DMC
1750eea0d25SJoseph Chen if (e->id == PM_DMC) {
1760eea0d25SJoseph Chen rate = rockchip_ddrclk_sip_round_rate_v2(rate);
1770eea0d25SJoseph Chen rockchip_ddrclk_sip_set_rate_v2(rate);
1780eea0d25SJoseph Chen } else
1790eea0d25SJoseph Chen #endif
180*3e169ed6SJoseph Chen rate = clk_set_rate(&e->clk, rate);
1810eea0d25SJoseph Chen
1820eea0d25SJoseph Chen return rate;
1830eea0d25SJoseph Chen }
1840eea0d25SJoseph Chen
__wtemp_regulator_get_value(struct pm_element * e)1850eea0d25SJoseph Chen static int __wtemp_regulator_get_value(struct pm_element *e)
1860eea0d25SJoseph Chen {
1870eea0d25SJoseph Chen return regulator_get_value(e->supply);
1880eea0d25SJoseph Chen }
1890eea0d25SJoseph Chen
__wtemp_regulator_set_value(struct pm_element * e,int value)1900eea0d25SJoseph Chen static int __wtemp_regulator_set_value(struct pm_element *e, int value)
1910eea0d25SJoseph Chen {
1920eea0d25SJoseph Chen return regulator_set_value(e->supply, value);
1930eea0d25SJoseph Chen }
1940eea0d25SJoseph Chen
1950eea0d25SJoseph Chen /*
1960eea0d25SJoseph Chen * Policy: Increase voltage
1970eea0d25SJoseph Chen *
1980eea0d25SJoseph Chen * 1. target volt = original volt + diff volt;
1990eea0d25SJoseph Chen * 2. If target volt is not over max_volt, just set it;
2000eea0d25SJoseph Chen * 3. Otherwise set max_volt as target volt and lower the rate(front N level).
2010eea0d25SJoseph Chen */
wtemp_dvfs_low_temp_adjust(struct udevice * dev,struct pm_element * e)2020eea0d25SJoseph Chen static void wtemp_dvfs_low_temp_adjust(struct udevice *dev, struct pm_element *e)
2030eea0d25SJoseph Chen {
2040eea0d25SJoseph Chen struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
2050eea0d25SJoseph Chen ulong org_rate, tgt_rate, rb_rate;
2060eea0d25SJoseph Chen int org_volt, tgt_volt, rb_volt;
2070eea0d25SJoseph Chen
2080eea0d25SJoseph Chen org_rate = __wtemp_clk_get_rate(e);
2090eea0d25SJoseph Chen org_volt = __wtemp_regulator_get_value(e);
2100eea0d25SJoseph Chen tgt_volt = org_volt + e->volt_diff;
2110eea0d25SJoseph Chen if ((e->lmt.max_volt != -ENODATA) && (tgt_volt > e->lmt.max_volt)) {
2120eea0d25SJoseph Chen tgt_volt = e->lmt.max_volt;
2130eea0d25SJoseph Chen __wtemp_regulator_set_value(e, tgt_volt);
2140eea0d25SJoseph Chen tgt_rate = wtemp_get_lowlevel_rate(org_rate,
2150eea0d25SJoseph Chen RATE_LOWER_LEVEL_N, priv->cpu);
216*3e169ed6SJoseph Chen __wtemp_clk_set_rate(e, tgt_rate);
2170eea0d25SJoseph Chen } else {
2180eea0d25SJoseph Chen __wtemp_regulator_set_value(e, tgt_volt);
2190eea0d25SJoseph Chen tgt_rate = org_rate;
2200eea0d25SJoseph Chen }
2210eea0d25SJoseph Chen
2220eea0d25SJoseph Chen /* Check */
2230eea0d25SJoseph Chen rb_rate = __wtemp_clk_get_rate(e);
2240eea0d25SJoseph Chen rb_volt = __wtemp_regulator_get_value(e);
2250eea0d25SJoseph Chen if (tgt_rate != rb_rate)
226*3e169ed6SJoseph Chen printf("DVFS WARN: %s: target rate=%ld, readback rate=%ld !\n",
2270eea0d25SJoseph Chen e->name, tgt_rate, rb_rate);
2280eea0d25SJoseph Chen if (tgt_volt != rb_volt)
229*3e169ed6SJoseph Chen printf("DVFS WARN: %s: target volt=%d, readback volt=%d !\n",
2300eea0d25SJoseph Chen e->name, tgt_volt, rb_volt);
2310eea0d25SJoseph Chen
2320eea0d25SJoseph Chen printf("DVFS: %s(low): %ld->%ld Hz, %d->%d uV\n",
2330eea0d25SJoseph Chen e->name, org_rate, rb_rate, org_volt, rb_volt);
2340eea0d25SJoseph Chen }
2350eea0d25SJoseph Chen
2360eea0d25SJoseph Chen /*
2370eea0d25SJoseph Chen * Policy:
2380eea0d25SJoseph Chen *
2390eea0d25SJoseph Chen * Just set opp table[0] volt and rate, i.e. the lowest performance.
2400eea0d25SJoseph Chen */
wtemp_dvfs_high_temp_adjust(struct udevice * dev,struct pm_element * e)2410eea0d25SJoseph Chen static void wtemp_dvfs_high_temp_adjust(struct udevice *dev, struct pm_element *e)
2420eea0d25SJoseph Chen {
2430eea0d25SJoseph Chen ulong org_rate, tgt_rate, rb_rate;
2440eea0d25SJoseph Chen int org_volt, tgt_volt, rb_volt;
2450eea0d25SJoseph Chen
2460eea0d25SJoseph Chen /* Apply opp[0] */
2470eea0d25SJoseph Chen org_rate = __wtemp_clk_get_rate(e);
248*3e169ed6SJoseph Chen
2490eea0d25SJoseph Chen tgt_rate = e->opp[0].hz;
250*3e169ed6SJoseph Chen __wtemp_clk_set_rate(e, tgt_rate);
251*3e169ed6SJoseph Chen rb_rate = __wtemp_clk_get_rate(e);
252*3e169ed6SJoseph Chen if (tgt_rate != rb_rate) {
253*3e169ed6SJoseph Chen printf("DVFS WARN: %s: target rate=%ld, readback rate=%ld !\n",
254*3e169ed6SJoseph Chen e->name, tgt_rate, rb_rate);
255*3e169ed6SJoseph Chen return;
256*3e169ed6SJoseph Chen }
2570eea0d25SJoseph Chen
2580eea0d25SJoseph Chen org_volt = __wtemp_regulator_get_value(e);
2590eea0d25SJoseph Chen tgt_volt = e->opp[0].uv;
2600eea0d25SJoseph Chen __wtemp_regulator_set_value(e, tgt_volt);
2610eea0d25SJoseph Chen
2620eea0d25SJoseph Chen rb_volt = __wtemp_regulator_get_value(e);
2630eea0d25SJoseph Chen if (tgt_volt != rb_volt)
264*3e169ed6SJoseph Chen printf("DVFS WARN: %s: target volt=%d, readback volt=%d !\n",
2650eea0d25SJoseph Chen e->name, tgt_volt, rb_volt);
2660eea0d25SJoseph Chen
2670eea0d25SJoseph Chen printf("DVFS: %s(high): %ld->%ld Hz, %d->%d uV\n",
2680eea0d25SJoseph Chen e->name, org_rate, tgt_rate, org_volt, tgt_volt);
2690eea0d25SJoseph Chen }
2700eea0d25SJoseph Chen
wtemp_dvfs_is_effect(struct pm_element * e,int temp,enum pm_event evt)2710eea0d25SJoseph Chen static bool wtemp_dvfs_is_effect(struct pm_element *e,
2720eea0d25SJoseph Chen int temp, enum pm_event evt)
2730eea0d25SJoseph Chen {
2740eea0d25SJoseph Chen if (evt & PM_EVT_LOW) {
2750eea0d25SJoseph Chen if (e->lmt.ltemp_limit && temp <= e->lmt.low_temp)
2760eea0d25SJoseph Chen return false;
2770eea0d25SJoseph Chen }
2780eea0d25SJoseph Chen
2790eea0d25SJoseph Chen if (evt & PM_EVT_HIGH) {
2800eea0d25SJoseph Chen if (e->lmt.tztemp_limit && temp >= e->lmt.tz_temp)
2810eea0d25SJoseph Chen return false;
2820eea0d25SJoseph Chen else if (e->lmt.htemp_limit && temp >= e->lmt.high_temp)
2830eea0d25SJoseph Chen return false;
2840eea0d25SJoseph Chen }
2850eea0d25SJoseph Chen
2860eea0d25SJoseph Chen return true;
2870eea0d25SJoseph Chen }
2880eea0d25SJoseph Chen
__wtemp_dvfs_apply(struct udevice * dev,struct pm_element * e,int temp,enum pm_event evt)2890eea0d25SJoseph Chen static int __wtemp_dvfs_apply(struct udevice *dev, struct pm_element *e,
2900eea0d25SJoseph Chen int temp, enum pm_event evt)
2910eea0d25SJoseph Chen {
2920eea0d25SJoseph Chen enum pm_event ret = PM_EVT_NONE;
2930eea0d25SJoseph Chen
2940eea0d25SJoseph Chen if (evt & PM_EVT_LOW) {
2950eea0d25SJoseph Chen /* Over lowest temperature: increase voltage */
2960eea0d25SJoseph Chen if (e->lmt.ltemp_limit && temp <= e->lmt.low_temp) {
2970eea0d25SJoseph Chen ret |= PM_EVT_LOW;
2980eea0d25SJoseph Chen wtemp_dvfs_low_temp_adjust(dev, e);
2990eea0d25SJoseph Chen }
3000eea0d25SJoseph Chen }
3010eea0d25SJoseph Chen
3020eea0d25SJoseph Chen if (evt & PM_EVT_HIGH) {
3030eea0d25SJoseph Chen /* Over highest/thermal_zone temperature: decrease rate and voltage */
3040eea0d25SJoseph Chen if (e->lmt.tztemp_limit && temp >= e->lmt.tz_temp) {
3050eea0d25SJoseph Chen ret |= PM_EVT_HIGH;
3060eea0d25SJoseph Chen wtemp_dvfs_high_temp_adjust(dev, e);
3070eea0d25SJoseph Chen } else if (e->lmt.htemp_limit && temp >= e->lmt.high_temp) {
3080eea0d25SJoseph Chen ret |= PM_EVT_HIGH;
3090eea0d25SJoseph Chen wtemp_dvfs_high_temp_adjust(dev, e);
3100eea0d25SJoseph Chen }
3110eea0d25SJoseph Chen }
3120eea0d25SJoseph Chen
3130eea0d25SJoseph Chen return ret;
3140eea0d25SJoseph Chen }
3150eea0d25SJoseph Chen
__wtemp_common_ofdata_to_platdata(ofnode node,struct pm_element * e)3160eea0d25SJoseph Chen static int __wtemp_common_ofdata_to_platdata(ofnode node, struct pm_element *e)
3170eea0d25SJoseph Chen {
3180eea0d25SJoseph Chen ofnode supply, opp_node;
3190eea0d25SJoseph Chen u32 phandle, uv, clock[2];
3200eea0d25SJoseph Chen uint64_t hz;
3210eea0d25SJoseph Chen int ret;
3220eea0d25SJoseph Chen
3230eea0d25SJoseph Chen /* Get regulator and clk */
3240eea0d25SJoseph Chen if (!ofnode_read_u32(node, e->supply_name, &phandle)) {
3250eea0d25SJoseph Chen supply = ofnode_get_by_phandle(phandle);
3260eea0d25SJoseph Chen ret = regulator_get_by_devname(supply.np->name, &e->supply);
3270eea0d25SJoseph Chen if (ret) {
3280eea0d25SJoseph Chen printf("DVFS: %s: Get supply(%s) failed, ret=%d",
3290eea0d25SJoseph Chen e->name, supply.np->full_name, ret);
3300eea0d25SJoseph Chen return ret;
3310eea0d25SJoseph Chen }
3320eea0d25SJoseph Chen debug("DVFS: supply: %s\n", supply.np->full_name);
3330eea0d25SJoseph Chen }
3340eea0d25SJoseph Chen
3350eea0d25SJoseph Chen if (!ofnode_read_u32_array(node, "clocks", clock, ARRAY_SIZE(clock))) {
3360eea0d25SJoseph Chen e->clk.id = clock[1];
3370eea0d25SJoseph Chen ret = rockchip_get_clk(&e->clk.dev);
3380eea0d25SJoseph Chen if (ret) {
3390eea0d25SJoseph Chen printf("DVFS: %s: Get clk failed, ret=%d\n", e->name, ret);
3400eea0d25SJoseph Chen return ret;
3410eea0d25SJoseph Chen }
3420eea0d25SJoseph Chen }
3430eea0d25SJoseph Chen
3440eea0d25SJoseph Chen /* Get opp-table & limit param */
3450eea0d25SJoseph Chen if (!ofnode_read_u32(node, "operating-points-v2", &phandle)) {
3460eea0d25SJoseph Chen opp_node = ofnode_get_by_phandle(phandle);
3470eea0d25SJoseph Chen e->lmt.low_temp = ofnode_read_s32_default(opp_node,
3480eea0d25SJoseph Chen "rockchip,low-temp", -ENODATA);
3490eea0d25SJoseph Chen e->lmt.high_temp = ofnode_read_s32_default(opp_node,
3500eea0d25SJoseph Chen "rockchip,high-temp", -ENODATA);
3510eea0d25SJoseph Chen e->lmt.max_volt = ofnode_read_u32_default(opp_node,
3520eea0d25SJoseph Chen "rockchip,max-volt", -ENODATA);
3530eea0d25SJoseph Chen
3540eea0d25SJoseph Chen debug("DVFS: %s: low-temp=%d, high-temp=%d, max-volt=%d\n",
3550eea0d25SJoseph Chen e->name, e->lmt.low_temp, e->lmt.high_temp,
3560eea0d25SJoseph Chen e->lmt.max_volt);
3570eea0d25SJoseph Chen
3580eea0d25SJoseph Chen ofnode_for_each_subnode(node, opp_node) {
3590eea0d25SJoseph Chen if (e->opp_nr >= OPP_TABLE_MAX) {
3600eea0d25SJoseph Chen printf("DVFS: over max(%d) opp table items\n",
3610eea0d25SJoseph Chen OPP_TABLE_MAX);
3620eea0d25SJoseph Chen break;
3630eea0d25SJoseph Chen }
3640eea0d25SJoseph Chen ofnode_read_u64(node, "opp-hz", &hz);
3650eea0d25SJoseph Chen ofnode_read_u32_array(node, "opp-microvolt", &uv, 1);
3660eea0d25SJoseph Chen e->opp[e->opp_nr].hz = hz;
3670eea0d25SJoseph Chen e->opp[e->opp_nr].uv = uv;
3680eea0d25SJoseph Chen e->opp_nr++;
3690eea0d25SJoseph Chen debug("DVFS: %s: opp[%d]: hz=%lld, uv=%d, %s\n",
3700eea0d25SJoseph Chen e->name, e->opp_nr - 1,
3710eea0d25SJoseph Chen hz, uv, ofnode_get_name(node));
3720eea0d25SJoseph Chen }
3730eea0d25SJoseph Chen }
3740eea0d25SJoseph Chen if (!e->opp_nr) {
3750eea0d25SJoseph Chen printf("DVFS: %s: Can't find opp table\n", e->name);
3760eea0d25SJoseph Chen return -EINVAL;
3770eea0d25SJoseph Chen }
3780eea0d25SJoseph Chen
3790eea0d25SJoseph Chen if (e->lmt.max_volt == -ENODATA)
3800eea0d25SJoseph Chen e->lmt.max_volt = e->opp[e->opp_nr - 1].uv;
3810eea0d25SJoseph Chen if (e->lmt.low_temp != -ENODATA)
3820eea0d25SJoseph Chen e->lmt.ltemp_limit = true;
3830eea0d25SJoseph Chen if (e->lmt.high_temp != -ENODATA)
3840eea0d25SJoseph Chen e->lmt.htemp_limit = true;
3850eea0d25SJoseph Chen
3860eea0d25SJoseph Chen return 0;
3870eea0d25SJoseph Chen }
3880eea0d25SJoseph Chen
wtemp_dvfs_apply(struct udevice * dev)3890eea0d25SJoseph Chen static int wtemp_dvfs_apply(struct udevice *dev)
3900eea0d25SJoseph Chen {
3910eea0d25SJoseph Chen struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
3920eea0d25SJoseph Chen struct list_head *node;
3930eea0d25SJoseph Chen struct pm_element *e;
3940eea0d25SJoseph Chen char s_temp[TEMP_STRING_LEN];
3950eea0d25SJoseph Chen int temp, ret;
3960eea0d25SJoseph Chen
3970eea0d25SJoseph Chen ret = thermal_get_temp(priv->thermal, &temp);
3980eea0d25SJoseph Chen if (ret) {
3990eea0d25SJoseph Chen printf("DVFS: Get temperature failed, ret=%d\n", ret);
4000eea0d25SJoseph Chen return ret;
4010eea0d25SJoseph Chen }
4020eea0d25SJoseph Chen
4030eea0d25SJoseph Chen temp2string(temp, s_temp, TEMP_STRING_LEN);
4040eea0d25SJoseph Chen printf("DVFS: %s'c\n", s_temp);
4050eea0d25SJoseph Chen
4060eea0d25SJoseph Chen /* Apply dvfs policy for all pm element */
4070eea0d25SJoseph Chen list_for_each(node, &pm_e_head) {
4080eea0d25SJoseph Chen e = list_entry(node, struct pm_element, node);
4090eea0d25SJoseph Chen __wtemp_dvfs_apply(dev, e, temp, PM_EVT_BOTH);
4100eea0d25SJoseph Chen }
4110eea0d25SJoseph Chen
4120eea0d25SJoseph Chen return 0;
4130eea0d25SJoseph Chen }
4140eea0d25SJoseph Chen
wtemp_dvfs_repeat_apply(struct udevice * dev)4150eea0d25SJoseph Chen static int wtemp_dvfs_repeat_apply(struct udevice *dev)
4160eea0d25SJoseph Chen {
4170eea0d25SJoseph Chen struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
4180eea0d25SJoseph Chen struct list_head *node;
4190eea0d25SJoseph Chen struct pm_element *e;
4200eea0d25SJoseph Chen enum pm_event applied;
4210eea0d25SJoseph Chen char s_temp[TEMP_STRING_LEN];
4220eea0d25SJoseph Chen int temp, ret;
4230eea0d25SJoseph Chen
4240eea0d25SJoseph Chen repeat:
4250eea0d25SJoseph Chen ret = thermal_get_temp(priv->thermal, &temp);
4260eea0d25SJoseph Chen if (ret) {
4270eea0d25SJoseph Chen printf("DVFS: Get thermal temperature failed, ret=%d\n", ret);
4280eea0d25SJoseph Chen return false;
4290eea0d25SJoseph Chen }
4300eea0d25SJoseph Chen
4310eea0d25SJoseph Chen /* Apply dvfs policy for all pm element if there is repeat request */
4320eea0d25SJoseph Chen applied = PM_EVT_NONE;
4330eea0d25SJoseph Chen list_for_each(node, &pm_e_head) {
4340eea0d25SJoseph Chen e = list_entry(node, struct pm_element, node);
4350eea0d25SJoseph Chen if (e->lmt.ltemp_repeat)
4360eea0d25SJoseph Chen applied |= __wtemp_dvfs_apply(dev, e, temp, PM_EVT_LOW);
4370eea0d25SJoseph Chen if (e->lmt.htemp_repeat)
4380eea0d25SJoseph Chen applied |= __wtemp_dvfs_apply(dev, e, temp, PM_EVT_HIGH);
4390eea0d25SJoseph Chen }
4400eea0d25SJoseph Chen
4410eea0d25SJoseph Chen /* Everything is fine, exit */
4420eea0d25SJoseph Chen if (applied == PM_EVT_NONE)
4430eea0d25SJoseph Chen goto finish;
4440eea0d25SJoseph Chen
4450eea0d25SJoseph Chen /* Check repeat result */
4460eea0d25SJoseph Chen udelay(REPEAT_PERIOD_US);
4470eea0d25SJoseph Chen list_for_each(node, &pm_e_head) {
4480eea0d25SJoseph Chen e = list_entry(node, struct pm_element, node);
4490eea0d25SJoseph Chen if (e->lmt.ltemp_repeat &&
4500eea0d25SJoseph Chen !wtemp_dvfs_is_effect(e, temp, PM_EVT_LOW))
4510eea0d25SJoseph Chen goto repeat;
4520eea0d25SJoseph Chen if (e->lmt.htemp_repeat &&
4530eea0d25SJoseph Chen !wtemp_dvfs_is_effect(e, temp, PM_EVT_HIGH))
4540eea0d25SJoseph Chen goto repeat;
4550eea0d25SJoseph Chen }
4560eea0d25SJoseph Chen
4570eea0d25SJoseph Chen finish:
4580eea0d25SJoseph Chen list_for_each(node, &pm_e_head) {
4590eea0d25SJoseph Chen e = list_entry(node, struct pm_element, node);
4600eea0d25SJoseph Chen temp2string(temp, s_temp, TEMP_STRING_LEN);
4610eea0d25SJoseph Chen printf("DVFS: %s %s'c, %ld Hz, %d uV\n", e->name,
4620eea0d25SJoseph Chen s_temp, __wtemp_clk_get_rate(e),
4630eea0d25SJoseph Chen __wtemp_regulator_get_value(e));
4640eea0d25SJoseph Chen }
4650eea0d25SJoseph Chen
4660eea0d25SJoseph Chen return 0;
4670eea0d25SJoseph Chen }
4680eea0d25SJoseph Chen
print_e_state(void)4690eea0d25SJoseph Chen static void print_e_state(void)
4700eea0d25SJoseph Chen {
4710eea0d25SJoseph Chen struct pm_element *e;
4720eea0d25SJoseph Chen struct list_head *node;
4730eea0d25SJoseph Chen char s_low[TEMP_STRING_LEN];
4740eea0d25SJoseph Chen char s_high[TEMP_STRING_LEN];
4750eea0d25SJoseph Chen char s_tz[TEMP_STRING_LEN];
4760eea0d25SJoseph Chen
4770eea0d25SJoseph Chen list_for_each(node, &pm_e_head) {
4780eea0d25SJoseph Chen e = list_entry(node, struct pm_element, node);
4790eea0d25SJoseph Chen if (!e->lmt.ltemp_limit &&
4800eea0d25SJoseph Chen !e->lmt.htemp_limit && !e->lmt.tztemp_limit)
4810eea0d25SJoseph Chen return;
4820eea0d25SJoseph Chen
4830eea0d25SJoseph Chen temp2string(e->lmt.tz_temp, s_tz, TEMP_STRING_LEN);
4840eea0d25SJoseph Chen temp2string(e->lmt.low_temp, s_low, TEMP_STRING_LEN);
4850eea0d25SJoseph Chen temp2string(e->lmt.high_temp, s_high, TEMP_STRING_LEN);
4860eea0d25SJoseph Chen printf("DVFS: %s: low=%s'c, high=%s'c, Vmax=%duV, tz_temp=%s'c, "
4870eea0d25SJoseph Chen "h_repeat=%d, l_repeat=%d\n",
4880eea0d25SJoseph Chen e->name, e->lmt.ltemp_limit ? s_low : NULL,
4890eea0d25SJoseph Chen e->lmt.htemp_limit ? s_high : NULL,
4900eea0d25SJoseph Chen e->lmt.max_volt,
4910eea0d25SJoseph Chen e->lmt.tztemp_limit ? s_tz : NULL,
4920eea0d25SJoseph Chen e->lmt.htemp_repeat, e->lmt.ltemp_repeat);
4930eea0d25SJoseph Chen }
4940eea0d25SJoseph Chen }
4950eea0d25SJoseph Chen
wtemp_dvfs_ofdata_to_platdata(struct udevice * dev)4960eea0d25SJoseph Chen static int wtemp_dvfs_ofdata_to_platdata(struct udevice *dev)
4970eea0d25SJoseph Chen {
4980eea0d25SJoseph Chen struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
4990eea0d25SJoseph Chen ofnode tz_trip0, cooling_maps, node;
5000eea0d25SJoseph Chen ofnode cpus, cpu, dmc;
5010eea0d25SJoseph Chen const char *name;
5020eea0d25SJoseph Chen int ret, tz_temp;
5030eea0d25SJoseph Chen u32 phandle;
5040eea0d25SJoseph Chen
5050eea0d25SJoseph Chen INIT_LIST_HEAD(&pm_e_head);
5060eea0d25SJoseph Chen
5070eea0d25SJoseph Chen /* 1. Parse cpu node */
5080eea0d25SJoseph Chen priv->cpu = &pm_cpu;
5090eea0d25SJoseph Chen cpus = ofnode_path(FDT_PATH_CPUS);
5100eea0d25SJoseph Chen if (!ofnode_valid(cpus)) {
5110eea0d25SJoseph Chen debug("DVFS: Can't find %s\n", FDT_PATH_CPUS);
5120eea0d25SJoseph Chen goto parse_dmc;
5130eea0d25SJoseph Chen }
5140eea0d25SJoseph Chen
5150eea0d25SJoseph Chen ofnode_for_each_subnode(cpu, cpus) {
5160eea0d25SJoseph Chen name = ofnode_get_property(cpu, "device_type", NULL);
5170eea0d25SJoseph Chen if (!name)
5180eea0d25SJoseph Chen continue;
5190eea0d25SJoseph Chen if (!strcmp(name, "cpu")) {
5200eea0d25SJoseph Chen ret = __wtemp_common_ofdata_to_platdata(cpu, priv->cpu);
5210eea0d25SJoseph Chen if (ret)
5220eea0d25SJoseph Chen return ret;
5230eea0d25SJoseph Chen break;
5240eea0d25SJoseph Chen }
5250eea0d25SJoseph Chen }
5260eea0d25SJoseph Chen
5270eea0d25SJoseph Chen priv->cpu->lmt.ltemp_repeat =
5280eea0d25SJoseph Chen dev_read_bool(dev, "cpu,low-temp-repeat");
5290eea0d25SJoseph Chen priv->cpu->lmt.htemp_repeat =
5300eea0d25SJoseph Chen dev_read_bool(dev, "cpu,high-temp-repeat");
5310eea0d25SJoseph Chen
5320eea0d25SJoseph Chen list_add_tail(&priv->cpu->node, &pm_e_head);
5330eea0d25SJoseph Chen
5340eea0d25SJoseph Chen /* 2. Parse dmc node */
5350eea0d25SJoseph Chen parse_dmc:
5360eea0d25SJoseph Chen priv->dmc = &pm_dmc;
5370eea0d25SJoseph Chen dmc = ofnode_path(FDT_PATH_DMC);
5380eea0d25SJoseph Chen if (!ofnode_valid(dmc)) {
5390eea0d25SJoseph Chen debug("DVFS: Can't find %s\n", FDT_PATH_CPUS);
5400eea0d25SJoseph Chen goto parse_tz;
5410eea0d25SJoseph Chen }
5420eea0d25SJoseph Chen if (!IS_ENABLED(CONFIG_ROCKCHIP_DMC)) {
5430eea0d25SJoseph Chen debug("DVFS: CONFIG_ROCKCHIP_DMC is disabled\n");
5440eea0d25SJoseph Chen goto parse_tz;
5450eea0d25SJoseph Chen }
5460eea0d25SJoseph Chen
5470eea0d25SJoseph Chen ret = __wtemp_common_ofdata_to_platdata(dmc, priv->dmc);
5480eea0d25SJoseph Chen if (ret)
5490eea0d25SJoseph Chen return ret;
5500eea0d25SJoseph Chen
5510eea0d25SJoseph Chen priv->dmc->lmt.ltemp_repeat =
5520eea0d25SJoseph Chen dev_read_bool(dev, "dmc,low-temp-repeat");
5530eea0d25SJoseph Chen priv->dmc->lmt.htemp_repeat =
5540eea0d25SJoseph Chen dev_read_bool(dev, "dmc,high-temp-repeat");
5550eea0d25SJoseph Chen
5560eea0d25SJoseph Chen list_add_tail(&priv->dmc->node, &pm_e_head);
5570eea0d25SJoseph Chen
5580eea0d25SJoseph Chen /* 3. Parse thermal zone node */
5590eea0d25SJoseph Chen parse_tz:
5600eea0d25SJoseph Chen tz_trip0 = ofnode_path(FDT_PATH_THREMAL_TRIP_POINT0);
5610eea0d25SJoseph Chen if (!ofnode_valid(tz_trip0)) {
5620eea0d25SJoseph Chen debug("DVFS: Can't find %s\n", FDT_PATH_THREMAL_TRIP_POINT0);
5630eea0d25SJoseph Chen goto finish;
5640eea0d25SJoseph Chen }
5650eea0d25SJoseph Chen
5660eea0d25SJoseph Chen tz_temp = ofnode_read_s32_default(tz_trip0, "temperature", -ENODATA);
5670eea0d25SJoseph Chen if (tz_temp == -ENODATA) {
5680eea0d25SJoseph Chen debug("DVFS: Can't get thermal zone trip0 temperature\n");
5690eea0d25SJoseph Chen goto finish;
5700eea0d25SJoseph Chen }
5710eea0d25SJoseph Chen
5720eea0d25SJoseph Chen cooling_maps = ofnode_path(FDT_PATH_THREMAL_COOLING_MAPS);
5730eea0d25SJoseph Chen if (!ofnode_valid(cooling_maps)) {
5740eea0d25SJoseph Chen debug("DVFS: Can't find %s\n", FDT_PATH_THREMAL_COOLING_MAPS);
5750eea0d25SJoseph Chen goto finish;
5760eea0d25SJoseph Chen }
5770eea0d25SJoseph Chen
5780eea0d25SJoseph Chen ofnode_for_each_subnode(node, cooling_maps) {
5790eea0d25SJoseph Chen ofnode_read_u32_array(node, "cooling-device", &phandle, 1);
5800eea0d25SJoseph Chen name = ofnode_get_name(ofnode_get_by_phandle(phandle));
5810eea0d25SJoseph Chen if (!name)
5820eea0d25SJoseph Chen continue;
5830eea0d25SJoseph Chen if (strstr(name, "cpu")) {
5840eea0d25SJoseph Chen priv->cpu->lmt.tztemp_limit = true;
5850eea0d25SJoseph Chen priv->cpu->lmt.tz_temp = tz_temp;
5860eea0d25SJoseph Chen } else if (strstr(name, "dmc")) {
5870eea0d25SJoseph Chen priv->dmc->lmt.tztemp_limit = true;
5880eea0d25SJoseph Chen priv->dmc->lmt.tz_temp = tz_temp;
5890eea0d25SJoseph Chen }
5900eea0d25SJoseph Chen }
5910eea0d25SJoseph Chen
5920eea0d25SJoseph Chen finish:
5930eea0d25SJoseph Chen print_e_state();
5940eea0d25SJoseph Chen
5950eea0d25SJoseph Chen return 0;
5960eea0d25SJoseph Chen }
5970eea0d25SJoseph Chen
5980eea0d25SJoseph Chen static const struct dm_dvfs_ops wtemp_dvfs_ops = {
5990eea0d25SJoseph Chen .apply = wtemp_dvfs_apply,
6000eea0d25SJoseph Chen .repeat_apply = wtemp_dvfs_repeat_apply,
6010eea0d25SJoseph Chen };
6020eea0d25SJoseph Chen
wtemp_dvfs_probe(struct udevice * dev)6030eea0d25SJoseph Chen static int wtemp_dvfs_probe(struct udevice *dev)
6040eea0d25SJoseph Chen {
6050eea0d25SJoseph Chen struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
6060eea0d25SJoseph Chen int ret;
6070eea0d25SJoseph Chen
6080eea0d25SJoseph Chen #ifdef CONFIG_ROCKCHIP_DMC
6090eea0d25SJoseph Chen struct udevice *ram_dev;
6100eea0d25SJoseph Chen
6110eea0d25SJoseph Chen /* Init dmc */
6120eea0d25SJoseph Chen ret = uclass_get_device(UCLASS_RAM, 0, &ram_dev);
6130eea0d25SJoseph Chen if (ret) {
6140eea0d25SJoseph Chen printf("DVFS: Get dmc device failed, ret=%d\n", ret);
6150eea0d25SJoseph Chen return ret;
6160eea0d25SJoseph Chen }
6170eea0d25SJoseph Chen #endif
6180eea0d25SJoseph Chen /* Init thermal */
6190eea0d25SJoseph Chen ret = uclass_get_device(UCLASS_THERMAL, 0, &priv->thermal);
6200eea0d25SJoseph Chen if (ret) {
6210eea0d25SJoseph Chen printf("DVFS: Get thermal device failed, ret=%d\n", ret);
6220eea0d25SJoseph Chen return ret;
6230eea0d25SJoseph Chen }
6240eea0d25SJoseph Chen
6250eea0d25SJoseph Chen return 0;
6260eea0d25SJoseph Chen }
6270eea0d25SJoseph Chen
6280eea0d25SJoseph Chen static const struct udevice_id wtemp_dvfs_match[] = {
6290eea0d25SJoseph Chen { .compatible = "rockchip,uboot-wide-temperature", },
6300eea0d25SJoseph Chen {},
6310eea0d25SJoseph Chen };
6320eea0d25SJoseph Chen
6330eea0d25SJoseph Chen U_BOOT_DRIVER(rockchip_wide_temp_dvfs) = {
6340eea0d25SJoseph Chen .name = "rockchip_wide_temp_dvfs",
6350eea0d25SJoseph Chen .id = UCLASS_DVFS,
6360eea0d25SJoseph Chen .ops = &wtemp_dvfs_ops,
6370eea0d25SJoseph Chen .of_match = wtemp_dvfs_match,
6380eea0d25SJoseph Chen .probe = wtemp_dvfs_probe,
6390eea0d25SJoseph Chen .ofdata_to_platdata = wtemp_dvfs_ofdata_to_platdata,
6400eea0d25SJoseph Chen .priv_auto_alloc_size = sizeof(struct wtemp_dvfs_priv),
6410eea0d25SJoseph Chen };
642