diff --git a/patch/kernel/archive/rockchip64-6.16/rk3576-thermal-support.patch b/patch/kernel/archive/rockchip64-6.16/rk3576-thermal-support.patch new file mode 100644 index 000000000..bb4d0245f --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.16/rk3576-thermal-support.patch @@ -0,0 +1,1041 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: SuperKali +Date: Thu, 11 Sep 2025 15:32:33 +0200 +Subject: rockchip: Add RK3576 thermal sensor support with OTP trim + +Add support for the RK3576's thermal sensor with six channels +providing measurements for package temperature, big cores, little +cores, GPU, NPU and DDR controller. + +This includes: +- Device tree binding updates for RK3576 TSADC +- Support for reading thermal trim values from OTP cells +- Thermal zones configuration with passive cooling +- Driver support for RK3576 hardware variant + +The trim functionality improves sensor accuracy by applying +per-sensor calibration offsets stored in factory-programmed +OTP memory. + +Based on patches from Nicolas Frattaroli and Ye Zhang. + +Signed-off-by: SuperKali +Link: https://lore.kernel.org/all/aHgGiojM3nnNg8Bk@mai.linaro.org/ +--- + Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml | 63 ++- + arch/arm64/boot/dts/rockchip/rk3576.dtsi | 214 ++++++++++ + drivers/thermal/rockchip_thermal.c | 273 ++++++++-- + 3 files changed, 504 insertions(+), 46 deletions(-) + +diff --git a/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml b/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml +index 89baf5e6d..573f447cc 100644 +--- a/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml ++++ b/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml +@@ -15,15 +15,15 @@ properties: + compatible: + enum: + - rockchip,px30-tsadc + - rockchip,rk3228-tsadc + - rockchip,rk3288-tsadc +- - rockchip,rk3308-tsadc + - rockchip,rk3328-tsadc + - rockchip,rk3368-tsadc + - rockchip,rk3399-tsadc + - rockchip,rk3568-tsadc ++ - rockchip,rk3576-tsadc + - rockchip,rk3588-tsadc + - rockchip,rv1108-tsadc + + reg: + maxItems: 1 +@@ -38,10 +38,21 @@ properties: + clock-names: + items: + - const: tsadc + - const: apb_pclk + ++ nvmem-cells: ++ items: ++ - description: cell handle to where the trim's base temperature is stored ++ - description: ++ cell handle to where the trim's tenths of Celsius base value is stored ++ ++ nvmem-cell-names: ++ items: ++ - const: trim_base ++ - const: trim_base_frac ++ + resets: + minItems: 1 + maxItems: 3 + + reset-names: +@@ -49,10 +60,16 @@ properties: + items: + - const: tsadc-apb + - const: tsadc + - const: tsadc-phy + ++ "#address-cells": ++ const: 1 ++ ++ "#size-cells": ++ const: 0 ++ + "#thermal-sensor-cells": + const: 1 + + rockchip,grf: + description: The phandle of the syscon node for the general register file. +@@ -70,18 +87,62 @@ properties: + rockchip,hw-tshut-polarity: + description: The hardware-controlled active polarity 0:LOW 1:HIGH. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + ++patternProperties: ++ "@[0-9a-f]+$": ++ type: object ++ properties: ++ reg: ++ maxItems: 1 ++ description: sensor ID, a.k.a. channel number ++ ++ nvmem-cells: ++ items: ++ - description: handle of cell containing calibration data ++ ++ nvmem-cell-names: ++ items: ++ - const: trim ++ ++ required: ++ - reg ++ ++ unevaluatedProperties: false ++ + required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - resets + ++allOf: ++ - if: ++ not: ++ properties: ++ compatible: ++ contains: ++ const: rockchip,rk3568-tsadc ++ then: ++ properties: ++ nvmem-cells: false ++ nvmem-cell-names: false ++ - if: ++ not: ++ properties: ++ compatible: ++ contains: ++ enum: ++ - rockchip,rk3568-tsadc ++ - rockchip,rk3576-tsadc ++ then: ++ patternProperties: ++ "@[0-9a-f]+$": false ++ + unevaluatedProperties: false + + examples: + - | + #include +diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi +index b99f4bd4c..8264feec6 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi +@@ -9,10 +9,11 @@ + #include + #include + #include + #include + #include ++#include + + / { + compatible = "rockchip,rk3576"; + + interrupt-parent = <&gic>; +@@ -124,10 +125,11 @@ cpu_l1: cpu@1 { + reg = <0x1>; + enable-method = "psci"; + capacity-dmips-mhz = <485>; + clocks = <&scmi_clk SCMI_ARMCLK_L>; + operating-points-v2 = <&cluster0_opp_table>; ++ #cooling-cells = <2>; + cpu-idle-states = <&CPU_SLEEP>; + }; + + cpu_l2: cpu@2 { + device_type = "cpu"; +@@ -135,10 +137,11 @@ cpu_l2: cpu@2 { + reg = <0x2>; + enable-method = "psci"; + capacity-dmips-mhz = <485>; + clocks = <&scmi_clk SCMI_ARMCLK_L>; + operating-points-v2 = <&cluster0_opp_table>; ++ #cooling-cells = <2>; + cpu-idle-states = <&CPU_SLEEP>; + }; + + cpu_l3: cpu@3 { + device_type = "cpu"; +@@ -146,10 +149,11 @@ cpu_l3: cpu@3 { + reg = <0x3>; + enable-method = "psci"; + capacity-dmips-mhz = <485>; + clocks = <&scmi_clk SCMI_ARMCLK_L>; + operating-points-v2 = <&cluster0_opp_table>; ++ #cooling-cells = <2>; + cpu-idle-states = <&CPU_SLEEP>; + }; + + cpu_b0: cpu@100 { + device_type = "cpu"; +@@ -170,10 +174,11 @@ cpu_b1: cpu@101 { + reg = <0x101>; + enable-method = "psci"; + capacity-dmips-mhz = <1024>; + clocks = <&scmi_clk SCMI_ARMCLK_B>; + operating-points-v2 = <&cluster1_opp_table>; ++ #cooling-cells = <2>; + cpu-idle-states = <&CPU_SLEEP>; + }; + + cpu_b2: cpu@102 { + device_type = "cpu"; +@@ -181,10 +186,11 @@ cpu_b2: cpu@102 { + reg = <0x102>; + enable-method = "psci"; + capacity-dmips-mhz = <1024>; + clocks = <&scmi_clk SCMI_ARMCLK_B>; + operating-points-v2 = <&cluster1_opp_table>; ++ #cooling-cells = <2>; + cpu-idle-states = <&CPU_SLEEP>; + }; + + cpu_b3: cpu@103 { + device_type = "cpu"; +@@ -192,10 +198,11 @@ cpu_b3: cpu@103 { + reg = <0x103>; + enable-method = "psci"; + capacity-dmips-mhz = <1024>; + clocks = <&scmi_clk SCMI_ARMCLK_B>; + operating-points-v2 = <&cluster1_opp_table>; ++ #cooling-cells = <2>; + cpu-idle-states = <&CPU_SLEEP>; + }; + + idle-states { + entry-method = "psci"; +@@ -518,10 +525,144 @@ pmu_a72: pmu-a72 { + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + ++ thermal_zones: thermal-zones { ++ package_thermal: package-thermal { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsadc 0>; ++ ++ trips { ++ package_crit: package-crit { ++ temperature = <115000>; ++ hysteresis = <0>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ ++ bigcore_thermal: bigcore-thermal { ++ polling-delay-passive = <100>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsadc 1>; ++ ++ trips { ++ bigcore_alert: bigcore-alert { ++ temperature = <85000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ ++ bigcore_crit: bigcore-crit { ++ temperature = <115000>; ++ hysteresis = <0>; ++ type = "critical"; ++ }; ++ }; ++ ++ cooling-maps { ++ map0 { ++ trip = <&bigcore_alert>; ++ cooling-device = ++ <&cpu_b0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_b1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_b2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_b3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; ++ }; ++ }; ++ }; ++ ++ littlecore_thermal: littlecore-thermal { ++ polling-delay-passive = <100>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsadc 2>; ++ ++ trips { ++ littlecore_alert: littlecore-alert { ++ temperature = <85000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ ++ littlecore_crit: littlecore-crit { ++ temperature = <115000>; ++ hysteresis = <0>; ++ type = "critical"; ++ }; ++ }; ++ ++ cooling-maps { ++ map0 { ++ trip = <&littlecore_alert>; ++ cooling-device = ++ <&cpu_l0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_l1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_l2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_l3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; ++ }; ++ }; ++ }; ++ ++ gpu_thermal: gpu-thermal { ++ polling-delay-passive = <100>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsadc 3>; ++ ++ trips { ++ gpu_alert: gpu-alert { ++ temperature = <85000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ ++ gpu_crit: gpu-crit { ++ temperature = <115000>; ++ hysteresis = <0>; ++ type = "critical"; ++ }; ++ }; ++ ++ cooling-maps { ++ map0 { ++ trip = <&gpu_alert>; ++ cooling-device = ++ <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; ++ }; ++ }; ++ }; ++ ++ npu_thermal: npu-thermal { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsadc 4>; ++ ++ trips { ++ npu_crit: npu-crit { ++ temperature = <115000>; ++ hysteresis = <0>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ ++ ddr_thermal: ddr-thermal { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsadc 5>; ++ ++ trips { ++ ddr_crit: ddr-crit { ++ temperature = <115000>; ++ hysteresis = <0>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ }; ++ + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , +@@ -1823,10 +1964,34 @@ npu_leakage: npu-leakage@20 { + reg = <0x20 0x1>; + }; + gpu_leakage: gpu-leakage@21 { + reg = <0x21 0x1>; + }; ++ bigcore_tsadc_trim: bigcore-tsadc-trim@24 { ++ reg = <0x24 0x2>; ++ bits = <0 10>; ++ }; ++ litcore_tsadc_trim: litcore-tsadc-trim@26 { ++ reg = <0x26 0x2>; ++ bits = <0 10>; ++ }; ++ ddr_tsadc_trim: ddr-tsadc-trim@28 { ++ reg = <0x28 0x2>; ++ bits = <0 10>; ++ }; ++ npu_tsadc_trim: npu-tsadc-trim@2a { ++ reg = <0x2a 0x2>; ++ bits = <0 10>; ++ }; ++ gpu_tsadc_trim: gpu-tsadc-trim@2c { ++ reg = <0x2c 0x2>; ++ bits = <0 10>; ++ }; ++ soc_tsadc_trim: soc-tsadc-trim@64 { ++ reg = <0x64 0x2>; ++ bits = <0 10>; ++ }; + log_leakage: log-leakage@22 { + reg = <0x22 0x1>; + }; + }; + +@@ -2336,10 +2501,59 @@ saradc: adc@2ae00000 { + reset-names = "saradc-apb"; + #io-channel-cells = <1>; + status = "disabled"; + }; + ++ tsadc: tsadc@2ae70000 { ++ compatible = "rockchip,rk3576-tsadc"; ++ reg = <0x0 0x2ae70000 0x0 0x400>; ++ interrupts = ; ++ clocks = <&cru CLK_TSADC>, <&cru PCLK_TSADC>; ++ clock-names = "tsadc", "apb_pclk"; ++ assigned-clocks = <&cru CLK_TSADC>; ++ assigned-clock-rates = <2000000>; ++ resets = <&cru SRST_P_TSADC>, <&cru SRST_TSADC>; ++ reset-names = "tsadc-apb", "tsadc"; ++ #thermal-sensor-cells = <1>; ++ rockchip,hw-tshut-temp = <120000>; ++ rockchip,hw-tshut-mode = <0>; ++ rockchip,hw-tshut-polarity = <0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ sensor@0 { ++ reg = <0>; ++ nvmem-cells = <&soc_tsadc_trim>; ++ nvmem-cell-names = "trim"; ++ }; ++ sensor@1 { ++ reg = <1>; ++ nvmem-cells = <&bigcore_tsadc_trim>; ++ nvmem-cell-names = "trim"; ++ }; ++ sensor@2 { ++ reg = <2>; ++ nvmem-cells = <&litcore_tsadc_trim>; ++ nvmem-cell-names = "trim"; ++ }; ++ sensor@3 { ++ reg = <3>; ++ nvmem-cells = <&ddr_tsadc_trim>; ++ nvmem-cell-names = "trim"; ++ }; ++ sensor@4 { ++ reg = <4>; ++ nvmem-cells = <&npu_tsadc_trim>; ++ nvmem-cell-names = "trim"; ++ }; ++ sensor@5 { ++ reg = <5>; ++ nvmem-cells = <&gpu_tsadc_trim>; ++ nvmem-cell-names = "trim"; ++ }; ++ }; ++ + i2c9: i2c@2ae80000 { + compatible = "rockchip,rk3576-i2c", "rockchip,rk3399-i2c"; + reg = <0x0 0x2ae80000 0x0 0x1000>; + clocks = <&cru CLK_I2C9>, <&cru PCLK_I2C9>; + clock-names = "i2c", "pclk"; +diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c +index 8a8c76bbe..3beff9b6f 100644 +--- a/drivers/thermal/rockchip_thermal.c ++++ b/drivers/thermal/rockchip_thermal.c +@@ -7,10 +7,11 @@ + #include + #include + #include + #include + #include ++#include + #include + #include + #include + #include + #include +@@ -67,27 +68,32 @@ struct chip_tsadc_table { + + /** + * struct rockchip_tsadc_chip - hold the private data of tsadc chip + * @chn_offset: the channel offset of the first channel + * @chn_num: the channel number of tsadc chip +- * @tshut_temp: the hardware-controlled shutdown temperature value ++ * @trim_slope: used to convert the trim code to a temperature in millicelsius ++ * @tshut_temp: the hardware-controlled shutdown temperature value, with no trim + * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO) + * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH) + * @initialize: SoC special initialize tsadc controller method + * @irq_ack: clear the interrupt + * @control: enable/disable method for the tsadc controller +- * @get_temp: get the temperature ++ * @get_temp: get the raw temperature, unadjusted by trim + * @set_alarm_temp: set the high temperature interrupt + * @set_tshut_temp: set the hardware-controlled shutdown temperature + * @set_tshut_mode: set the hardware-controlled shutdown mode ++ * @get_trim_code: convert a hardware temperature code to one adjusted for by trim + * @table: the chip-specific conversion table + */ + struct rockchip_tsadc_chip { + /* The sensor id of chip correspond to the ADC channel */ + int chn_offset; + int chn_num; + ++ /* Used to convert trim code to trim temp */ ++ int trim_slope; ++ + /* The hardware-controlled tshut property */ + int tshut_temp; + enum tshut_mode tshut_mode; + enum tshut_polarity tshut_polarity; + +@@ -103,25 +109,31 @@ struct rockchip_tsadc_chip { + int (*set_alarm_temp)(const struct chip_tsadc_table *table, + int chn, void __iomem *reg, int temp); + int (*set_tshut_temp)(const struct chip_tsadc_table *table, + int chn, void __iomem *reg, int temp); + void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m); ++ int (*get_trim_code)(const struct chip_tsadc_table *table, ++ int code, int trim_base, int trim_base_frac); + + /* Per-table methods */ + struct chip_tsadc_table table; + }; + + /** + * struct rockchip_thermal_sensor - hold the information of thermal sensor + * @thermal: pointer to the platform/configuration data + * @tzd: pointer to a thermal zone ++ * @of_node: pointer to the device_node representing this sensor, if any + * @id: identifier of the thermal sensor ++ * @trim_temp: per-sensor trim temperature value + */ + struct rockchip_thermal_sensor { + struct rockchip_thermal_data *thermal; + struct thermal_zone_device *tzd; ++ struct device_node *of_node; + int id; ++ int trim_temp; + }; + + /** + * struct rockchip_thermal_data - hold the private data of thermal driver + * @chip: pointer to the platform/configuration data +@@ -130,11 +142,15 @@ struct rockchip_thermal_sensor { + * @sensors: array of thermal sensors + * @clk: the controller clock is divided by the exteral 24MHz + * @pclk: the advanced peripherals bus clock + * @grf: the general register file will be used to do static set by software + * @regs: the base address of tsadc controller +- * @tshut_temp: the hardware-controlled shutdown temperature value ++ * @trim_base: major component of sensor trim value, in Celsius ++ * @trim_base_frac: minor component of sensor trim value, in Decicelsius ++ * @trim: fallback thermal trim value for each channel ++ * @tshut_temp: the hardware-controlled shutdown temperature value, with no trim ++ * @trim_temp: the fallback trim temperature for the whole sensor + * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO) + * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH) + */ + struct rockchip_thermal_data { + const struct rockchip_tsadc_chip *chip; +@@ -147,11 +163,16 @@ struct rockchip_thermal_data { + struct clk *pclk; + + struct regmap *grf; + void __iomem *regs; + ++ int trim_base; ++ int trim_base_frac; ++ int trim; ++ + int tshut_temp; ++ int trim_temp; + enum tshut_mode tshut_mode; + enum tshut_polarity tshut_polarity; + }; + + /* +@@ -247,10 +268,13 @@ struct rockchip_thermal_data { + #define GRF_TSADC_VCM_EN_L (0x10001 << 7) + #define GRF_TSADC_VCM_EN_H (0x10001 << 7) + + #define GRF_CON_TSADC_CH_INV (0x10001 << 1) + ++ ++#define RK_MAX_TEMP (180000) ++ + /** + * struct tsadc_table - code to temperature conversion table + * @code: the value of adc channel + * @temp: the temperature + * Note: +@@ -1043,11 +1067,11 @@ static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs, + } + + writel_relaxed(val, regs + TSADCV2_INT_EN); + } + +-static void rk_tsadcv3_tshut_mode(int chn, void __iomem *regs, ++static void rk_tsadcv4_tshut_mode(int chn, void __iomem *regs, + enum tshut_mode mode) + { + u32 val_gpio, val_cru; + + if (mode == TSHUT_MODE_GPIO) { +@@ -1059,31 +1083,18 @@ static void rk_tsadcv3_tshut_mode(int chn, void __iomem *regs, + } + writel_relaxed(val_gpio, regs + TSADCV3_HSHUT_GPIO_INT_EN); + writel_relaxed(val_cru, regs + TSADCV3_HSHUT_CRU_INT_EN); + } + +-static const struct rockchip_tsadc_chip rk3308_tsadc_data = { +- .chn_num = 2, /* 2 channels for tsadc */ +- +- .tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */ +- .tshut_temp = 95000, +- +- .initialize = rk_tsadcv4_initialize, +- .irq_ack = rk_tsadcv3_irq_ack, +- .control = rk_tsadcv3_control, +- .get_temp = rk_tsadcv2_get_temp, +- .set_alarm_temp = rk_tsadcv2_alarm_temp, +- .set_tshut_temp = rk_tsadcv2_tshut_temp, +- .set_tshut_mode = rk_tsadcv2_tshut_mode, ++static int rk_tsadcv2_get_trim_code(const struct chip_tsadc_table *table, ++ int code, int trim_base, int trim_base_frac) ++{ ++ int temp = trim_base * 1000 + trim_base_frac * 100; ++ u32 base_code = rk_tsadcv2_temp_to_code(table, temp); + +- .table = { +- .id = rk3328_code_table, +- .length = ARRAY_SIZE(rk3328_code_table), +- .data_mask = TSADCV2_DATA_MASK, +- .mode = ADC_INCREMENT, +- }, +-}; ++ return code - base_code; ++} + + static const struct rockchip_tsadc_chip px30_tsadc_data = { + /* cpu, gpu */ + .chn_offset = 0, + .chn_num = 2, /* 2 channels for tsadc */ +@@ -1304,10 +1315,34 @@ static const struct rockchip_tsadc_chip rk3568_tsadc_data = { + .data_mask = TSADCV2_DATA_MASK, + .mode = ADC_INCREMENT, + }, + }; + ++static const struct rockchip_tsadc_chip rk3576_tsadc_data = { ++ /* top, big_core, little_core, ddr, npu, gpu */ ++ .chn_offset = 0, ++ .chn_num = 6, /* six channels for tsadc */ ++ .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */ ++ .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ ++ .tshut_temp = 95000, ++ .initialize = rk_tsadcv8_initialize, ++ .irq_ack = rk_tsadcv4_irq_ack, ++ .control = rk_tsadcv4_control, ++ .get_temp = rk_tsadcv4_get_temp, ++ .set_alarm_temp = rk_tsadcv3_alarm_temp, ++ .set_tshut_temp = rk_tsadcv3_tshut_temp, ++ .set_tshut_mode = rk_tsadcv4_tshut_mode, ++ .get_trim_code = rk_tsadcv2_get_trim_code, ++ .trim_slope = 923, ++ .table = { ++ .id = rk3588_code_table, ++ .length = ARRAY_SIZE(rk3588_code_table), ++ .data_mask = TSADCV4_DATA_MASK, ++ .mode = ADC_INCREMENT, ++ }, ++}; ++ + static const struct rockchip_tsadc_chip rk3588_tsadc_data = { + /* top, big_core0, big_core1, little_core, center, gpu, npu */ + .chn_offset = 0, + .chn_num = 7, /* seven channels for tsadc */ + .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */ +@@ -1317,11 +1352,11 @@ static const struct rockchip_tsadc_chip rk3588_tsadc_data = { + .irq_ack = rk_tsadcv4_irq_ack, + .control = rk_tsadcv4_control, + .get_temp = rk_tsadcv4_get_temp, + .set_alarm_temp = rk_tsadcv3_alarm_temp, + .set_tshut_temp = rk_tsadcv3_tshut_temp, +- .set_tshut_mode = rk_tsadcv3_tshut_mode, ++ .set_tshut_mode = rk_tsadcv4_tshut_mode, + .table = { + .id = rk3588_code_table, + .length = ARRAY_SIZE(rk3588_code_table), + .data_mask = TSADCV4_DATA_MASK, + .mode = ADC_INCREMENT, +@@ -1342,14 +1377,10 @@ static const struct of_device_id of_rockchip_thermal_match[] = { + }, + { + .compatible = "rockchip,rk3288-tsadc", + .data = (void *)&rk3288_tsadc_data, + }, +- { +- .compatible = "rockchip,rk3308-tsadc", +- .data = (void *)&rk3308_tsadc_data, +- }, + { + .compatible = "rockchip,rk3328-tsadc", + .data = (void *)&rk3328_tsadc_data, + }, + { +@@ -1366,10 +1397,14 @@ static const struct of_device_id of_rockchip_thermal_match[] = { + }, + { + .compatible = "rockchip,rk3568-tsadc", + .data = (void *)&rk3568_tsadc_data, + }, ++ { ++ .compatible = "rockchip,rk3576-tsadc", ++ .data = (void *)&rk3576_tsadc_data, ++ }, + { + .compatible = "rockchip,rk3588-tsadc", + .data = (void *)&rk3588_tsadc_data, + }, + { /* end */ }, +@@ -1411,11 +1446,11 @@ static int rockchip_thermal_set_trips(struct thermal_zone_device *tz, int low, i + + dev_dbg(&thermal->pdev->dev, "%s: sensor %d: low: %d, high %d\n", + __func__, sensor->id, low, high); + + return tsadc->set_alarm_temp(&tsadc->table, +- sensor->id, thermal->regs, high); ++ sensor->id, thermal->regs, high + sensor->trim_temp); + } + + static int rockchip_thermal_get_temp(struct thermal_zone_device *tz, int *out_temp) + { + struct rockchip_thermal_sensor *sensor = thermal_zone_device_priv(tz); +@@ -1423,18 +1458,118 @@ static int rockchip_thermal_get_temp(struct thermal_zone_device *tz, int *out_te + const struct rockchip_tsadc_chip *tsadc = sensor->thermal->chip; + int retval; + + retval = tsadc->get_temp(&tsadc->table, + sensor->id, thermal->regs, out_temp); ++ *out_temp -= sensor->trim_temp; ++ + return retval; + } + + static const struct thermal_zone_device_ops rockchip_of_thermal_ops = { + .get_temp = rockchip_thermal_get_temp, + .set_trips = rockchip_thermal_set_trips, + }; + ++/** ++ * rockchip_get_efuse_value - read an OTP cell from a device node ++ * @np: pointer to the device node with the nvmem-cells property ++ * @cell_name: name of cell that should be read ++ * @value: pointer to where the read value will be placed ++ * ++ * Return: Negative errno on failure, during which *value will not be touched, ++ * or 0 on success. ++ */ ++static int rockchip_get_efuse_value(struct device_node *np, const char *cell_name, ++ int *value) ++{ ++ struct nvmem_cell *cell; ++ int ret = 0; ++ size_t len; ++ u8 *buf; ++ int i; ++ ++ cell = of_nvmem_cell_get(np, cell_name); ++ if (IS_ERR(cell)) ++ return PTR_ERR(cell); ++ ++ buf = nvmem_cell_read(cell, &len); ++ ++ nvmem_cell_put(cell); ++ ++ if (IS_ERR(buf)) ++ return PTR_ERR(buf); ++ ++ if (len > sizeof(*value)) { ++ ret = -ERANGE; ++ goto exit; ++ } ++ ++ /* Copy with implicit endian conversion */ ++ *value = 0; ++ for (i = 0; i < len; i++) ++ *value |= (int) buf[i] << (8 * i); ++ ++exit: ++ kfree(buf); ++ return ret; ++} ++ ++static int rockchip_get_trim_configuration(struct device *dev, struct device_node *np, ++ struct rockchip_thermal_data *thermal) ++{ ++ const struct rockchip_tsadc_chip *tsadc = thermal->chip; ++ int trim_base = 0, trim_base_frac = 0, trim = 0; ++ int trim_code; ++ int ret; ++ ++ thermal->trim_base = 0; ++ thermal->trim_base_frac = 0; ++ thermal->trim = 0; ++ ++ if (!tsadc->get_trim_code) ++ return 0; ++ ++ ret = rockchip_get_efuse_value(np, "trim_base", &trim_base); ++ if (ret < 0) { ++ if (ret == -ENOENT) { ++ trim_base = 30; ++ dev_dbg(dev, "trim_base is absent, defaulting to 30\n"); ++ } else { ++ dev_err(dev, "failed reading nvmem value of trim_base: %pe\n", ++ ERR_PTR(ret)); ++ return ret; ++ } ++ } ++ ret = rockchip_get_efuse_value(np, "trim_base_frac", &trim_base_frac); ++ if (ret < 0) { ++ if (ret == -ENOENT) { ++ dev_dbg(dev, "trim_base_frac is absent, defaulting to 0\n"); ++ } else { ++ dev_err(dev, "failed reading nvmem value of trim_base_frac: %pe\n", ++ ERR_PTR(ret)); ++ return ret; ++ } ++ } ++ thermal->trim_base = trim_base; ++ thermal->trim_base_frac = trim_base_frac; ++ ++ /* ++ * If the tsadc node contains the trim property, then it is used in the ++ * absence of per-channel trim values ++ */ ++ if (!rockchip_get_efuse_value(np, "trim", &trim)) ++ thermal->trim = trim; ++ if (trim) { ++ trim_code = tsadc->get_trim_code(&tsadc->table, trim, ++ trim_base, trim_base_frac); ++ thermal->trim_temp = thermal->chip->trim_slope * trim_code; ++ } ++ ++ return 0; ++} ++ + static int rockchip_configure_from_dt(struct device *dev, + struct device_node *np, + struct rockchip_thermal_data *thermal) + { + u32 shut_temp, tshut_mode, tshut_polarity; +@@ -1491,37 +1626,66 @@ static int rockchip_configure_from_dt(struct device *dev, + */ + thermal->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(thermal->grf)) + dev_warn(dev, "Missing rockchip,grf property\n"); + ++ rockchip_get_trim_configuration(dev, np, thermal); ++ + return 0; + } + + static int + rockchip_thermal_register_sensor(struct platform_device *pdev, + struct rockchip_thermal_data *thermal, + struct rockchip_thermal_sensor *sensor, + int id) + { + const struct rockchip_tsadc_chip *tsadc = thermal->chip; ++ struct device *dev = &pdev->dev; ++ int trim = thermal->trim; ++ int trim_code, tshut_temp; ++ int trim_temp = 0; + int error; + ++ if (thermal->trim_temp) ++ trim_temp = thermal->trim_temp; ++ ++ if (tsadc->get_trim_code && sensor->of_node) { ++ error = rockchip_get_efuse_value(sensor->of_node, "trim", &trim); ++ if (error < 0 && error != -ENOENT) { ++ dev_err(dev, "failed reading trim of sensor %d: %pe\n", ++ id, ERR_PTR(error)); ++ return error; ++ } ++ if (trim) { ++ trim_code = tsadc->get_trim_code(&tsadc->table, trim, ++ thermal->trim_base, ++ thermal->trim_base_frac); ++ trim_temp = thermal->chip->trim_slope * trim_code; ++ } ++ } ++ ++ sensor->trim_temp = trim_temp; ++ ++ dev_dbg(dev, "trim of sensor %d is %d\n", id, sensor->trim_temp); ++ ++ tshut_temp = min(thermal->tshut_temp + sensor->trim_temp, RK_MAX_TEMP); ++ + tsadc->set_tshut_mode(id, thermal->regs, thermal->tshut_mode); + +- error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs, +- thermal->tshut_temp); ++ error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs, tshut_temp); + if (error) +- dev_err(&pdev->dev, "%s: invalid tshut=%d, error=%d\n", +- __func__, thermal->tshut_temp, error); ++ dev_err(dev, "%s: invalid tshut=%d, error=%d\n", ++ __func__, tshut_temp, error); + + sensor->thermal = thermal; + sensor->id = id; +- sensor->tzd = devm_thermal_of_zone_register(&pdev->dev, id, sensor, ++ sensor->tzd = devm_thermal_of_zone_register(dev, id, sensor, + &rockchip_of_thermal_ops); + if (IS_ERR(sensor->tzd)) { + error = PTR_ERR(sensor->tzd); +- dev_err(&pdev->dev, "failed to register sensor %d: %d\n", ++ dev_err(dev, "failed to register sensor %d: %d\n", + id, error); + return error; + } + + return 0; +@@ -1540,13 +1704,15 @@ static void rockchip_thermal_reset_controller(struct reset_control *reset) + + static int rockchip_thermal_probe(struct platform_device *pdev) + { + struct device_node *np = pdev->dev.of_node; + struct rockchip_thermal_data *thermal; ++ struct device_node *child; + int irq; + int i; + int error; ++ u32 chn; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -EINVAL; + +@@ -1593,10 +1759,22 @@ static int rockchip_thermal_probe(struct platform_device *pdev) + "failed to parse device tree data\n"); + + thermal->chip->initialize(thermal->grf, thermal->regs, + thermal->tshut_polarity); + ++ for_each_available_child_of_node(np, child) { ++ if (!of_property_read_u32(child, "reg", &chn)) { ++ if (chn < thermal->chip->chn_num) ++ thermal->sensors[chn].of_node = child; ++ else ++ dev_warn(&pdev->dev, ++ "sensor address (%d) too large, ignoring its trim\n", ++ chn); ++ } ++ ++ } ++ + for (i = 0; i < thermal->chip->chn_num; i++) { + error = rockchip_thermal_register_sensor(pdev, thermal, + &thermal->sensors[i], + thermal->chip->chn_offset + i); + if (error) +@@ -1662,12 +1840,15 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev) + } + + static int __maybe_unused rockchip_thermal_resume(struct device *dev) + { + struct rockchip_thermal_data *thermal = dev_get_drvdata(dev); +- int i; ++ const struct rockchip_tsadc_chip *tsadc = thermal->chip; ++ struct rockchip_thermal_sensor *sensor; ++ int tshut_temp; + int error; ++ int i; + + error = clk_enable(thermal->clk); + if (error) + return error; + +@@ -1677,25 +1858,27 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev) + return error; + } + + rockchip_thermal_reset_controller(thermal->reset); + +- thermal->chip->initialize(thermal->grf, thermal->regs, +- thermal->tshut_polarity); ++ tsadc->initialize(thermal->grf, thermal->regs, thermal->tshut_polarity); + + for (i = 0; i < thermal->chip->chn_num; i++) { +- int id = thermal->sensors[i].id; ++ sensor = &thermal->sensors[i]; ++ ++ tshut_temp = min(thermal->tshut_temp + sensor->trim_temp, ++ RK_MAX_TEMP); + +- thermal->chip->set_tshut_mode(id, thermal->regs, ++ tsadc->set_tshut_mode(sensor->id, thermal->regs, + thermal->tshut_mode); + +- error = thermal->chip->set_tshut_temp(&thermal->chip->table, +- id, thermal->regs, +- thermal->tshut_temp); ++ error = tsadc->set_tshut_temp(&thermal->chip->table, ++ sensor->id, thermal->regs, ++ tshut_temp); + if (error) + dev_err(dev, "%s: invalid tshut=%d, error=%d\n", +- __func__, thermal->tshut_temp, error); ++ __func__, tshut_temp, error); + } + + thermal->chip->control(thermal->regs, true); + + for (i = 0; i < thermal->chip->chn_num; i++) +-- +Armbian +