mirror of
https://github.com/armbian/build
synced 2025-09-24 19:47:06 +07:00
1042 lines
29 KiB
Diff
1042 lines
29 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: SuperKali <hello@superkali.me>
|
|
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 <hello@superkali.me>
|
|
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 <dt-bindings/interrupt-controller/arm-gic.h>
|
|
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 <dt-bindings/phy/phy.h>
|
|
#include <dt-bindings/pinctrl/rockchip.h>
|
|
#include <dt-bindings/power/rockchip,rk3576-power.h>
|
|
#include <dt-bindings/reset/rockchip,rk3576-cru.h>
|
|
#include <dt-bindings/soc/rockchip,boot-mode.h>
|
|
+#include <dt-bindings/thermal/thermal.h>
|
|
|
|
/ {
|
|
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 = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
|
|
<GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
|
|
<GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
|
|
@@ -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 = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
|
|
+ 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 <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/io.h>
|
|
#include <linux/module.h>
|
|
+#include <linux/nvmem-consumer.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regmap.h>
|
|
@@ -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
|
|
|