mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/2] thermal: airoha: Expose EN7581 CPU temperature
@ 2026-10-31 14:19 Vitaliy Sochnev
  2026-10-31 14:19 ` [PATCH 1/2] dt-bindings: thermal: airoha: Allow sensor indices for EN7581 Vitaliy Sochnev
  2026-10-31 14:19 ` [PATCH 2/2] thermal/drivers/airoha: Read the EN7581 CPU ADC Vitaliy Sochnev
  0 siblings, 2 replies; 3+ messages in thread
From: Vitaliy Sochnev @ 2026-10-31 14:19 UTC (permalink / raw)
  To: daniel.lezcano
  Cc: rafael, rui.zhang, lukasz.luba, robh, krzk+dt, conor+dt,
	ansuelsmth, linux-pm, devicetree, linux-kernel, Vitaliy Sochnev

EN7581 has a second thermal ADC for the CPU die at SCU offsets 0x2fc/0x308.
Expose it as sensor index 1 while keeping the main ADC at index 0 and
retaining zero-cell device tree compatibility. The hardware temperature
monitor and its trips continue to use only the main ADC.

Tested on Nokia XG-040G-MD (EN7581, OpenWrt 6.18.55): both zones read
successfully before and after reboot; temperatures tracked CPU load and
cooling, and matched direct ADC readings within 0.3 C. Twenty driver
unbind/bind cycles with an IRQ pending preserved both zones and handled
the interrupt each time. The driver builds for arm64 with W=1 and the
binding passes dt_binding_check.

Vitaliy Sochnev (2):
  dt-bindings: thermal: airoha: Allow sensor indices for EN7581
  thermal/drivers/airoha: Read the EN7581 CPU ADC

 .../thermal/airoha,en7581-thermal.yaml        |   6 +-
 drivers/thermal/airoha_thermal.c              | 157 ++++++++++++++++--
 2 files changed, 152 insertions(+), 11 deletions(-)


base-commit: 2dc1f296b5754dd2441fc8b6613a362a47f6b98f
-- 
2.55.0

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 1/2] dt-bindings: thermal: airoha: Allow sensor indices for EN7581
  2026-10-31 14:19 [PATCH 0/2] thermal: airoha: Expose EN7581 CPU temperature Vitaliy Sochnev
@ 2026-10-31 14:19 ` Vitaliy Sochnev
  2026-10-31 14:19 ` [PATCH 2/2] thermal/drivers/airoha: Read the EN7581 CPU ADC Vitaliy Sochnev
  1 sibling, 0 replies; 3+ messages in thread
From: Vitaliy Sochnev @ 2026-10-31 14:19 UTC (permalink / raw)
  To: daniel.lezcano
  Cc: rafael, rui.zhang, lukasz.luba, robh, krzk+dt, conor+dt,
	ansuelsmth, linux-pm, devicetree, linux-kernel, Vitaliy Sochnev

Allow either the existing zero-cell form or a sensor index that
selects the main or CPU ADC.

Signed-off-by: Vitaliy Sochnev <sochnev.v.74@gmail.com>
---
 .../devicetree/bindings/thermal/airoha,en7581-thermal.yaml  | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/thermal/airoha,en7581-thermal.yaml b/Documentation/devicetree/bindings/thermal/airoha,en7581-thermal.yaml
index ca0242ef03788..e7be20d1bd1ce 100644
--- a/Documentation/devicetree/bindings/thermal/airoha,en7581-thermal.yaml
+++ b/Documentation/devicetree/bindings/thermal/airoha,en7581-thermal.yaml
@@ -24,7 +24,11 @@ properties:
     $ref: /schemas/types.yaml#/definitions/phandle
 
   '#thermal-sensor-cells':
-    const: 0
+    description: >
+      With one cell, the phandle argument selects ADC 0 (main) or 1 (CPU).
+      Zero cells remain valid for existing device trees. Only the main ADC
+      has a hardware trip interrupt; CPU ADC zones with trips need polling.
+    enum: [0, 1]
 
 required:
   - compatible
-- 
2.55.0


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 2/2] thermal/drivers/airoha: Read the EN7581 CPU ADC
  2026-10-31 14:19 [PATCH 0/2] thermal: airoha: Expose EN7581 CPU temperature Vitaliy Sochnev
  2026-10-31 14:19 ` [PATCH 1/2] dt-bindings: thermal: airoha: Allow sensor indices for EN7581 Vitaliy Sochnev
@ 2026-10-31 14:19 ` Vitaliy Sochnev
  1 sibling, 0 replies; 3+ messages in thread
From: Vitaliy Sochnev @ 2026-10-31 14:19 UTC (permalink / raw)
  To: daniel.lezcano
  Cc: rafael, rui.zhang, lukasz.luba, robh, krzk+dt, conor+dt,
	ansuelsmth, linux-pm, devicetree, linux-kernel, Vitaliy Sochnev

Register the second EN7581 ADC as a read-only thermal zone. The
hardware monitor and interrupt continue to use the main ADC. Use the
CPU calibration offset from efuse, falling back to the main ADC when
it is absent.

Signed-off-by: Vitaliy Sochnev <sochnev.v.74@gmail.com>
---
 drivers/thermal/airoha_thermal.c | 157 +++++++++++++++++++++++++++++--
 1 file changed, 147 insertions(+), 10 deletions(-)

diff --git a/drivers/thermal/airoha_thermal.c b/drivers/thermal/airoha_thermal.c
index 7c63a1b44a6b9..982b0736df9a3 100644
--- a/drivers/thermal/airoha_thermal.c
+++ b/drivers/thermal/airoha_thermal.c
@@ -18,6 +18,8 @@
 #define   EN7581_MUX_TADC			GENMASK(3, 1)
 #define EN7581_DOUT_TADC			0x2f8
 #define   EN7581_DOUT_TADC_MASK			GENMASK(15, 0)
+#define EN7581_PWD_TADC_CPU			0x2fc
+#define EN7581_DOUT_TADC_CPU			0x308
 
 #define AN7583_MUX_TADC				0x2a0
 #define   AN7583_LOAD_ADJ			GENMASK(3, 2)
@@ -236,6 +238,13 @@ enum an7583_thermal_diode_mux {
 	AN7583_D1_TADC,
 };
 
+enum en7581_thermal_sensor {
+	EN7581_TADC,
+	EN7581_TADC_CPU,
+
+	EN7581_NUM_SENSOR,
+};
+
 enum airoha_thermal_chip_scu_field {
 	AIROHA_THERMAL_DOUT_TADC,
 	AIROHA_THERMAL_MUX_SENSOR,
@@ -264,6 +273,8 @@ struct airoha_thermal_priv {
 	int init_temp;
 	int default_slope;
 	int default_offset;
+	int cpu_offset;
+	bool cpu_adc_failed;
 };
 
 struct airoha_thermal_soc_data {
@@ -354,19 +365,53 @@ static void airoha_set_thermal_mux(struct airoha_thermal_priv *priv,
 	usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC);
 }
 
+static int en7581_thermal_read_adc(struct airoha_thermal_priv *priv, int sensor,
+				   int *value)
+{
+	u32 val;
+	int ret;
+
+	if (sensor != EN7581_TADC_CPU) {
+		*value = airoha_get_thermal_ADC(priv);
+		return 0;
+	}
+
+	ret = regmap_read(priv->chip_scu, EN7581_DOUT_TADC_CPU, &val);
+	if (ret)
+		return ret;
+
+	*value = FIELD_GET(EN7581_DOUT_TADC_MASK, val);
+	return 0;
+}
+
 static int en7581_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
 {
 	struct airoha_thermal_zone *zone = thermal_zone_device_priv(tz);
 	struct airoha_thermal_priv *priv = zone->priv;
 	int min_value, max_value, avg_value, value;
-	int i;
+	int offset = priv->default_offset;
+	int i, ret;
+	u32 sel;
+
+	if (zone->sensor == EN7581_TADC_CPU) {
+		/* Other mux positions convert to temperatures above the trips */
+		ret = regmap_read(priv->chip_scu, EN7581_PWD_TADC_CPU, &sel);
+		if (ret)
+			return ret;
+		if (FIELD_GET(EN7581_MUX_TADC, sel) != EN7581_SCU_THERMAL_MUX_DIODE1)
+			return -EAGAIN;
+
+		offset = priv->cpu_offset;
+	}
 
 	avg_value = 0;
 	min_value = INT_MAX;
 	max_value = INT_MIN;
 
 	for (i = 0; i < AIROHA_MAX_SAMPLES; i++) {
-		value = airoha_get_thermal_ADC(priv);
+		ret = en7581_thermal_read_adc(priv, zone->sensor, &value);
+		if (ret)
+			return ret;
 		min_value = min(value, min_value);
 		max_value = max(value, max_value);
 		avg_value += value;
@@ -376,7 +421,8 @@ static int en7581_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
 	avg_value -= (min_value + max_value);
 	avg_value /= AIROHA_MAX_SAMPLES - 2;
 
-	*temp = RAW_TO_TEMP(priv, avg_value);
+	*temp = (((avg_value - offset) * 1000) / priv->default_slope +
+		 priv->init_temp) * 100;
 	return 0;
 }
 
@@ -387,6 +433,10 @@ static int en7581_thermal_set_trips(struct thermal_zone_device *tz, int low,
 	struct airoha_thermal_priv *priv = zone->priv;
 	bool enable_monitor = false;
 
+	/* The temperature monitor samples only the main ADC */
+	if (zone->sensor == EN7581_TADC_CPU)
+		return 0;
+
 	if (high != INT_MAX) {
 		/* Validate high and clamp it a supported value */
 		high = clamp_t(int, high, RAW_TO_TEMP(priv, 0),
@@ -484,6 +534,70 @@ static void en7581_thermal_setup_adc_val(struct device *dev,
 	}
 }
 
+static int en7581_thermal_setup_cpu_adc(struct device *dev,
+					struct airoha_thermal_priv *priv)
+{
+	int tries = AIROHA_THERMAL_MUX_TRIES;
+	u32 pllrg, sel, efuse, main;
+	int cpu;
+	int ret, restore_ret;
+
+	ret = regmap_read(priv->chip_scu, EN7581_PLLRG_PROTECT, &pllrg);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT,
+			   priv->pllrg_protect);
+	if (ret)
+		goto restore_protect;
+
+	/* Unprotected writes are dropped silently, so read the value back */
+	do {
+		ret = regmap_update_bits(priv->chip_scu, EN7581_PWD_TADC_CPU,
+					 EN7581_MUX_TADC,
+					 FIELD_PREP(EN7581_MUX_TADC,
+						    EN7581_SCU_THERMAL_MUX_DIODE1));
+		if (ret)
+			break;
+		ret = regmap_read(priv->chip_scu, EN7581_PWD_TADC_CPU, &sel);
+		if (ret)
+			break;
+	} while (FIELD_GET(EN7581_MUX_TADC, sel) != EN7581_SCU_THERMAL_MUX_DIODE1 &&
+		 --tries);
+
+	if (!ret && FIELD_GET(EN7581_MUX_TADC, sel) != EN7581_SCU_THERMAL_MUX_DIODE1)
+		ret = -EIO;
+
+restore_protect:
+	restore_ret = regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, pllrg);
+	if (ret)
+		return ret;
+	if (restore_ret)
+		return restore_ret;
+
+	usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC);
+
+	ret = regmap_read(priv->map, EN7581_PTPSPARE1, &efuse);
+	if (ret)
+		return ret;
+	if (FIELD_GET(EN7581_EFUSE_TEMP_OFFSET, efuse)) {
+		priv->cpu_offset = FIELD_GET(EN7581_EFUSE_TEMP_OFFSET, efuse);
+		return 0;
+	}
+
+	/* Without calibration assume both ADCs are at the same temperature */
+	ret = en7581_thermal_read_adc(priv, EN7581_TADC_CPU, &cpu);
+	if (ret)
+		return ret;
+	ret = regmap_field_read(priv->chip_scu_fields[AIROHA_THERMAL_DOUT_TADC],
+				&main);
+	if (ret)
+		return ret;
+	priv->cpu_offset = cpu - (int)main + priv->default_offset;
+	dev_info(dev, "missing CPU thermal calibration EFUSE, using main sensor offset\n");
+	return 0;
+}
+
 static void en7581_thermal_setup_monitor(struct airoha_thermal_priv *priv)
 {
 	/* Set measure mode */
@@ -495,8 +609,7 @@ static void en7581_thermal_setup_monitor(struct airoha_thermal_priv *priv)
 	 * The AHB temp monitor system doesn't have direct access to the
 	 * thermal sensor. It does instead work by providing various
 	 * addresses to configure how to access and setup an ADC for the
-	 * sensor. EN7581 supports only one sensor hence the
-	 * implementation is greatly simplified but the AHB supports
+	 * sensor. EN7581 monitors only the main ADC here; the AHB supports
 	 * up to 4 different sensors from the same ADC that can be
 	 * switched by tuning the ADC mux or writing address.
 	 *
@@ -556,7 +669,8 @@ static int en7581_thermal_probe(struct platform_device *pdev,
 	struct device_node *chip_scu_np;
 	struct device *dev = &pdev->dev;
 	void __iomem *base;
-	int i;
+	u32 sensor_cells = 0;
+	int i, ret;
 
 	base = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(base))
@@ -572,8 +686,11 @@ static int en7581_thermal_probe(struct platform_device *pdev,
 		return -EINVAL;
 
 	priv->chip_scu = syscon_node_to_regmap(chip_scu_np);
-	if (IS_ERR(priv->chip_scu))
-		return PTR_ERR(priv->chip_scu);
+	if (IS_ERR(priv->chip_scu)) {
+		ret = PTR_ERR(priv->chip_scu);
+		of_node_put(chip_scu_np);
+		return ret;
+	}
 
 	for (i = 0; i < AIROHA_THERMAL_FIELD_MAX; i++) {
 		struct regmap_field *field;
@@ -592,11 +709,21 @@ static int en7581_thermal_probe(struct platform_device *pdev,
 		priv->chip_scu_fields[i] = field;
 	}
 
-	of_address_to_resource(chip_scu_np, 0, &priv->scu_adc_res);
+	ret = of_address_to_resource(chip_scu_np, 0, &priv->scu_adc_res);
 	of_node_put(chip_scu_np);
+	if (ret)
+		return ret;
 
 	en7581_thermal_setup_monitor(priv);
 	en7581_thermal_setup_adc_val(dev, priv);
+	of_property_read_u32(dev->of_node, "#thermal-sensor-cells", &sensor_cells);
+	if (sensor_cells) {
+		ret = en7581_thermal_setup_cpu_adc(dev, priv);
+		if (ret) {
+			dev_warn(dev, "CPU thermal ADC unavailable: %d\n", ret);
+			priv->cpu_adc_failed = true;
+		}
+	}
 
 	return 0;
 }
@@ -751,6 +878,9 @@ static int airoha_thermal_probe(struct platform_device *pdev)
 		struct airoha_thermal_zone *zone;
 		struct thermal_zone_device *tz;
 
+		if (priv->cpu_adc_failed && soc_data->sensors[i] == EN7581_TADC_CPU)
+			continue;
+
 		zone = devm_kzalloc(dev, sizeof(*zone), GFP_KERNEL);
 		if (!zone)
 			return -ENOMEM;
@@ -786,8 +916,15 @@ static const int an7583_zone_sensors[AN7583_NUM_SENSOR] = {
 	AN7583_CPU_TEMP_SENSOR,
 };
 
+/* The main ADC stays zone 0 */
+static const int en7581_zone_sensors[EN7581_NUM_SENSOR] = {
+	EN7581_TADC,
+	EN7581_TADC_CPU,
+};
+
 static const struct airoha_thermal_soc_data en7581_data = {
-	.num_sensors = 1,
+	.sensors = en7581_zone_sensors,
+	.num_sensors = EN7581_NUM_SENSOR,
 	.pllrg_protect = EN7581_SCU_THERMAL_PROTECT_KEY,
 	.thdev_ops = &en7581_thdev_ops,
 	.probe = &en7581_thermal_probe,
-- 
2.55.0


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-10-31 14:19 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-31 14:19 [PATCH 0/2] thermal: airoha: Expose EN7581 CPU temperature Vitaliy Sochnev
2026-10-31 14:19 ` [PATCH 1/2] dt-bindings: thermal: airoha: Allow sensor indices for EN7581 Vitaliy Sochnev
2026-10-31 14:19 ` [PATCH 2/2] thermal/drivers/airoha: Read the EN7581 CPU ADC Vitaliy Sochnev

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®