Devicetree
 help / color / mirror / Atom feed
* [PATCH v6 0/7] thermal: loongson2: Add support for Loongson-2K0300 SoC
@ 2026-09-28  4:44 Binbin Zhou
  2026-09-28  4:44 ` [PATCH v6 1/7] thermal/drivers/loongson2: Correct thermal sensor registration loop Binbin Zhou
                   ` (6 more replies)
  0 siblings, 7 replies; 18+ messages in thread
From: Binbin Zhou @ 2026-09-28  4:44 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yinbo Zhu, Rafael J . Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-pm,
	Binbin Zhou

Hi all:

This patch series adds thermal zone support for the Loongson-2K0300 SoC.

The Loongson-2K0300's thermal sensor is generally compatible with the
existing Loongson-2K series, but it features several key differences:
1. It has an independent CPU ID register for reading chip version
information.
2. The raw temperature output field has been extended from 8 bits to 11
bits.
3. The temperature calculation formula has been updated to incorporate a
correction factor based on the version information.

To ensure robustness on older hardware revisions, a fallback mechanism
is included: if the calculated temperature falls outside the valid range
(-55 TO 125),  the driver falls back to a simpler calculation and prints
a warning.

The series also includes fixes and cleanups for the existing Loongson-2
thermal driver, which are necessary to support the new SoC cleanly.

The patchset have been validated on the Loongson-2K series development boards.

Thanks.
Binbin

=========
V6:
 - Rebase on linux-pm/thermal;
Patch (2/7):
 - New patch;
 - Fix incorrect clamp() argument order for thermal trip points;
Patch (3/7):
 - New patch;
 - Fix Loongson-2K2000 trip point programming by adding a per-SoC
   set_trips_regs callback and handling the split HI_GATE registers;
Patch (4/7):
 - New patch;
 - cleanup: use GENMASK/FIELD_* helpers, add named constants, and factor
   common trip point setup;
Patch (5/7):
 - New patch;
 - refactor: move thermal_zone_device_ops into chip_data and replace the
   Loongson-2K2000 flag special case with an optional init_extend_resource
   callback;
Patch (7/7):
 - Updated the 10-bit output to 11-bit in commit message;
 - Temporarily remove the implementation of `set_trips`;
 - Remove unnecessary header files;
 - Rewrite `get_temp`, separating `read_calib` and `detect_old_fuse` into
   new functions to be called during the probe;
 - Use macros to define the relevant numbers.

Link to V5:
https://lore.kernel.org/all/cover.1789546118.git.zhoubinbin@loongson.cn/#t

V5:
 - Rebase on linux-pm/thermal;
 - Drop patch-3 for it was applied.

Link to V4:
https://lore.kernel.org/all/cover.1785829933.git.zhoubinbin@loongson.cn/

V4:
 - Rebase on linux-mfd/for-mfd-next;
Patch (3/5):
 - Remove the operation that adds `compatible` to the `select` node.

Link to V3:
https://lore.kernel.org/all/cover.1785480242.git.zhoubinbin@loongson.cn/

V3:
Patch (2/5):
 - New patch for moving thermal_ops to chip_data;
Patch (3/5):
 - Correct spelling errors;
Patch (4/5):
 - Add Acked-by from Conor, thanks;
Patch (5/5):
 - Keep loongson2_thermal_data->chip_data as const, and use
   data->flags as the private data.
 - Renmae LOONGSON2_THSENS_OUT_10B_MASK ->
   LOONGSON2_THSENS_OUT_11B_MASK;

Link to V2:
https://lore.kernel.org/all/cover.1784619512.git.zhoubinbin@loongson.cn/

V2:
Patch (1/4):
 - New patch form Sashiko;
Patch (2/4):
 - New patch for adding chipid syscon compatible;
Patch (3/4):
 - Use `loongson,chipid` phandle replace reg property;
Patch (3/4):
 - Refact `calib_offset` calculate routing;
 - Add LS2K0300_OLD_FUSE_FLAG flag.

Link to V1:
https://lore.kernel.org/all/cover.1783670011.git.zhoubinbin@loongson.cn/

Binbin Zhou (7):
  thermal/drivers/loongson2: Correct thermal sensor registration loop
  thermal/drivers/loongson2: Fix clamp() usage for thermal trip points
  thermal/drivers/loongson2: Fix Loongson-2K2000 trip point programming
  thermal/drivers/loongson2: Use bitfields and per-SoC thermal ops
  thermal/drivers/loongson2: Move per-SoC ops into chip data
  dt-bindings: thermal: loongson,ls2k-thermal: Add compatible for
    Loongson-2K0300
  thermal/drivers/loongson2: Add Loongson-2K0300 support

 .../thermal/loongson,ls2k-thermal.yaml        |  73 +++-
 drivers/thermal/loongson2_thermal.c           | 336 ++++++++++++++----
 2 files changed, 321 insertions(+), 88 deletions(-)


base-commit: ec0d89150a9381d591344a9f6f5428655c227a7f
-- 
2.52.0


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

* [PATCH v6 1/7] thermal/drivers/loongson2: Correct thermal sensor registration loop
  2026-09-28  4:44 [PATCH v6 0/7] thermal: loongson2: Add support for Loongson-2K0300 SoC Binbin Zhou
@ 2026-09-28  4:44 ` Binbin Zhou
  2026-09-28  7:15   ` Daniel Lezcano
  2026-09-28  4:44 ` [PATCH v6 2/7] thermal/drivers/loongson2: Fix clamp() usage for thermal trip points Binbin Zhou
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Binbin Zhou @ 2026-09-28  4:44 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yinbo Zhu, Rafael J . Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-pm,
	Binbin Zhou, stable, Sashiko

The registration loop in loongson2_thermal_probe() incorrectly uses
dev_err_probe() when the sensor is not present (-ENODEV). In that case,
the driver should continue to the next sensor index rather than treating
it as a fatal error.

Fix this by correctly handling -ENODEV and only returning on other
errors. Also add a final check to ensure at least one thermal zone was
registered.

Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/cover.1783670011.git.zhoubinbin@loongson.cn?part=2
Fixes: e7e3a7c35791 ("thermal/drivers/loongson-2: Add thermal management support")
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/thermal/loongson2_thermal.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
index 4d40fc706a53..99b7392fc68f 100644
--- a/drivers/thermal/loongson2_thermal.c
+++ b/drivers/thermal/loongson2_thermal.c
@@ -160,16 +160,18 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
 
 	for (i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
 		tzd = devm_thermal_of_zone_register(dev, i, data, thermal_ops);
-
 		if (!IS_ERR(tzd))
 			break;
 
-		if (PTR_ERR(tzd) != -ENODEV)
+		if (PTR_ERR(tzd) == -ENODEV)
 			continue;
 
-		return dev_err_probe(dev, PTR_ERR(tzd), "failed to register");
+		return dev_err_probe(dev, PTR_ERR(tzd), "failed to register sensor %d\n", i);
 	}
 
+	if (IS_ERR(tzd))
+		return dev_err_probe(dev, -ENODEV, "No thermal sensor registered\n");
+
 	ret = devm_request_threaded_irq(dev, irq, NULL, loongson2_thermal_irq_thread,
 					IRQF_ONESHOT, "loongson2_thermal", tzd);
 	if (ret < 0)
-- 
2.52.0


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

* [PATCH v6 2/7] thermal/drivers/loongson2: Fix clamp() usage for thermal trip points
  2026-09-28  4:44 [PATCH v6 0/7] thermal: loongson2: Add support for Loongson-2K0300 SoC Binbin Zhou
  2026-09-28  4:44 ` [PATCH v6 1/7] thermal/drivers/loongson2: Correct thermal sensor registration loop Binbin Zhou
@ 2026-09-28  4:44 ` Binbin Zhou
  2026-09-28  4:44 ` [PATCH v6 3/7] thermal/drivers/loongson2: Fix Loongson-2K2000 trip point programming Binbin Zhou
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 18+ messages in thread
From: Binbin Zhou @ 2026-09-28  4:44 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yinbo Zhu, Rafael J . Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-pm,
	Binbin Zhou, stable

clamp() takes the value to clamp as its first argument, followed by the
minimum and maximum bounds. The current code passes the -40 and 125
limits as the value instead, so the trip thresholds are not clamped to the
supported sensor range.

Correct the arguments so that low and high are clamped to [-40, 125].

Cc: stable@vger.kernel.org
Fixes: 734b5def91b5 ("thermal/drivers/loongson2: Add Loongson-2K2000 support")
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/thermal/loongson2_thermal.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
index 99b7392fc68f..bcabca360157 100644
--- a/drivers/thermal/loongson2_thermal.c
+++ b/drivers/thermal/loongson2_thermal.c
@@ -62,10 +62,10 @@ static int loongson2_thermal_set(struct loongson2_thermal_data *data,
 				 int low, int high, bool enable)
 {
 	/* Set low temperature threshold */
-	loongson2_set_ctrl_regs(data, clamp(-40, low, high), true, enable);
+	loongson2_set_ctrl_regs(data, clamp(low, -40, 125), true, enable);
 
 	/* Set high temperature threshold */
-	loongson2_set_ctrl_regs(data, clamp(125, low, high), false, enable);
+	loongson2_set_ctrl_regs(data, clamp(high, -40, 125), false, enable);
 
 	return 0;
 }
-- 
2.52.0


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

* [PATCH v6 3/7] thermal/drivers/loongson2: Fix Loongson-2K2000 trip point programming
  2026-09-28  4:44 [PATCH v6 0/7] thermal: loongson2: Add support for Loongson-2K0300 SoC Binbin Zhou
  2026-09-28  4:44 ` [PATCH v6 1/7] thermal/drivers/loongson2: Correct thermal sensor registration loop Binbin Zhou
  2026-09-28  4:44 ` [PATCH v6 2/7] thermal/drivers/loongson2: Fix clamp() usage for thermal trip points Binbin Zhou
@ 2026-09-28  4:44 ` Binbin Zhou
  2026-09-28  4:54   ` sashiko-bot
  2026-09-28  4:44 ` [PATCH v6 4/7] thermal/drivers/loongson2: Use bitfields and per-SoC thermal ops Binbin Zhou
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Binbin Zhou @ 2026-09-28  4:44 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yinbo Zhu, Rafael J . Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-pm,
	Binbin Zhou, stable

Loongson-2K2000 uses a different trip point layout than Loongson-2K1000:
the low 8 bits of the gate value are written to the control register,
while the high 8 bits are written to separate HI_GATE_HI/LOW registers.
The gate value is calculated as (temp + 311) * 0x4000 / 820.

Introduce a per-SoC set_trips_regs() callback. Keep the existing
Loongson-2K1000 logic and add the Loongson-2K2000 implementation.
Move the common clamping into loongson2_thermal_set_trips() and pass
Celsius values to the callback.

Cc: stable@vger.kernel.org
Fixes: 734b5def91b5 ("thermal/drivers/loongson2: Add Loongson-2K2000 support")
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/thermal/loongson2_thermal.c | 77 ++++++++++++++++++++---------
 1 file changed, 55 insertions(+), 22 deletions(-)

diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
index bcabca360157..45fe6100134e 100644
--- a/drivers/thermal/loongson2_thermal.c
+++ b/drivers/thermal/loongson2_thermal.c
@@ -29,15 +29,23 @@
 					 LOONGSON2_THSENS_INT_HIGH)
 #define LOONGSON2_THSENS_OUT_MASK	0xFF
 
+/* Loongson-2K2000-Specific Registers */
+#define LS2K2000_THSENS_HI_GATE_HI_REG	0x18
+#define LS2K2000_THSENS_HI_GATE_LOW_REG	0x1c
+
 /*
  * This flag is used to indicate the temperature reading
  * method of the Loongson-2K2000
  */
 #define LS2K2000_THSENS_OUT_FLAG	BIT(0)
 
+struct loongson2_thermal_data;
+
 struct loongson2_thermal_chip_data {
 	unsigned int thermal_sensor_sel;
 	unsigned int flags;
+	void (*set_trips_regs)(struct loongson2_thermal_data *data,
+			       int temp, bool low, bool enable);
 };
 
 struct loongson2_thermal_data {
@@ -46,30 +54,40 @@ struct loongson2_thermal_data {
 	const struct loongson2_thermal_chip_data *chip_data;
 };
 
-static void loongson2_set_ctrl_regs(struct loongson2_thermal_data *data,
-				    int ctrl_data, bool low, bool enable)
+static void loongson2_thermal_set_trip_temp(struct loongson2_thermal_data *data,
+					    int low, int high, bool enable)
 {
-	int reg_ctrl = 0;
-	int reg_off  = data->chip_data->thermal_sensor_sel * 2;
-	int ctrl_reg = low ? LOONGSON2_THSENS_CTRL_LOW_REG : LOONGSON2_THSENS_CTRL_HI_REG;
+	/* Set low temperature threshold */
+	data->chip_data->set_trips_regs(data, low, true, enable);
 
-	reg_ctrl = ctrl_data + HECTO;
-	reg_ctrl |= enable ? 0x100 : 0;
-	writew(reg_ctrl, data->ctrl_reg + ctrl_reg + reg_off);
+	/* Set high temperature threshold */
+	data->chip_data->set_trips_regs(data, high, false, enable);
 }
 
-static int loongson2_thermal_set(struct loongson2_thermal_data *data,
-				 int low, int high, bool enable)
+static int loongson2_thermal_set_trips(struct thermal_zone_device *tz, int low, int high)
 {
-	/* Set low temperature threshold */
-	loongson2_set_ctrl_regs(data, clamp(low, -40, 125), true, enable);
+	struct loongson2_thermal_data *data = thermal_zone_device_priv(tz);
 
-	/* Set high temperature threshold */
-	loongson2_set_ctrl_regs(data, clamp(high, -40, 125), false, enable);
+	low = clamp(low, -40000, 125000);
+	high = clamp(high, -40000, 125000);
+
+	loongson2_thermal_set_trip_temp(data, low / MILLI, high / MILLI, true);
 
 	return 0;
 }
 
+static void loongson2_2k1000_set_trips_regs(struct loongson2_thermal_data *data,
+					    int temp, bool low, bool enable)
+{
+	int reg_ctrl = 0;
+	int reg_off  = data->chip_data->thermal_sensor_sel * 2;
+	int ctrl_reg = low ? LOONGSON2_THSENS_CTRL_LOW_REG : LOONGSON2_THSENS_CTRL_HI_REG;
+
+	reg_ctrl = temp + HECTO;
+	reg_ctrl |= enable ? 0x100 : 0;
+	writew(reg_ctrl, data->ctrl_reg + ctrl_reg + reg_off);
+}
+
 static int loongson2_2k1000_get_temp(struct thermal_zone_device *tz, int *temp)
 {
 	int val;
@@ -81,6 +99,26 @@ static int loongson2_2k1000_get_temp(struct thermal_zone_device *tz, int *temp)
 	return 0;
 }
 
+static void loongson2_2k2000_set_trips_regs(struct loongson2_thermal_data *data,
+					    int temp, bool low, bool enable)
+{
+	int hi_reg, ctrl_reg;
+	int val = 0, gate;
+
+	ctrl_reg = low ? LOONGSON2_THSENS_CTRL_LOW_REG : LOONGSON2_THSENS_CTRL_HI_REG;
+	gate = (temp + 311) * 0x4000 / 820;
+
+	val = gate & 0xff;
+	val |= enable ? 0x100 : 0;
+	writel(val, data->ctrl_reg + ctrl_reg);
+
+	hi_reg = low ? LS2K2000_THSENS_HI_GATE_LOW_REG : LS2K2000_THSENS_HI_GATE_HI_REG;
+
+	val = readl(data->ctrl_reg + hi_reg);
+	val = (val & ~0xff) | ((gate >> 8) & 0xff);
+	writel(val, data->ctrl_reg + hi_reg);
+}
+
 static int loongson2_2k2000_get_temp(struct thermal_zone_device *tz, int *temp)
 {
 	int val;
@@ -104,13 +142,6 @@ static irqreturn_t loongson2_thermal_irq_thread(int irq, void *dev)
 	return IRQ_HANDLED;
 }
 
-static int loongson2_thermal_set_trips(struct thermal_zone_device *tz, int low, int high)
-{
-	struct loongson2_thermal_data *data = thermal_zone_device_priv(tz);
-
-	return loongson2_thermal_set(data, low/MILLI, high/MILLI, true);
-}
-
 static const struct thermal_zone_device_ops loongson2_2k1000_of_thermal_ops = {
 	.get_temp = loongson2_2k1000_get_temp,
 	.set_trips = loongson2_thermal_set_trips,
@@ -156,7 +187,7 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
 
 	writeb(LOONGSON2_THSENS_INT_EN, data->ctrl_reg + LOONGSON2_THSENS_STATUS_REG);
 
-	loongson2_thermal_set(data, 0, 0, false);
+	loongson2_thermal_set_trip_temp(data, 0, 0, false);
 
 	for (i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
 		tzd = devm_thermal_of_zone_register(dev, i, data, thermal_ops);
@@ -185,11 +216,13 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
 static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k1000_data = {
 	.thermal_sensor_sel = 0,
 	.flags = 0,
+	.set_trips_regs = loongson2_2k1000_set_trips_regs,
 };
 
 static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k2000_data = {
 	.thermal_sensor_sel = 0,
 	.flags = LS2K2000_THSENS_OUT_FLAG,
+	.set_trips_regs = loongson2_2k2000_set_trips_regs,
 };
 
 static const struct of_device_id of_loongson2_thermal_match[] = {
-- 
2.52.0


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

* [PATCH v6 4/7] thermal/drivers/loongson2: Use bitfields and per-SoC thermal ops
  2026-09-28  4:44 [PATCH v6 0/7] thermal: loongson2: Add support for Loongson-2K0300 SoC Binbin Zhou
                   ` (2 preceding siblings ...)
  2026-09-28  4:44 ` [PATCH v6 3/7] thermal/drivers/loongson2: Fix Loongson-2K2000 trip point programming Binbin Zhou
@ 2026-09-28  4:44 ` Binbin Zhou
  2026-09-28  4:52   ` sashiko-bot
  2026-09-28  4:44 ` [PATCH v6 5/7] thermal/drivers/loongson2: Move per-SoC ops into chip data Binbin Zhou
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Binbin Zhou @ 2026-09-28  4:44 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yinbo Zhu, Rafael J . Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-pm,
	Binbin Zhou

Replace open-coded masks and shifts with GENMASK()/FIELD_* helpers, and
define named constants for the Loongson-2K2000 temperature conversion
and trip gate calculations.

Split the temperature read and thermal zone ops into Loongson-2K1000 and
Loongson-2K2000 variants, and keep the chip-specific trip register
callback in the chip data. This prepares the driver for further SoC
support.

No functional change intended.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/thermal/loongson2_thermal.c | 123 ++++++++++++++++------------
 1 file changed, 72 insertions(+), 51 deletions(-)

diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
index 45fe6100134e..0660a74fd922 100644
--- a/drivers/thermal/loongson2_thermal.c
+++ b/drivers/thermal/loongson2_thermal.c
@@ -5,6 +5,7 @@
  * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
  */
 
+#include <linux/bitfield.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/minmax.h>
@@ -27,11 +28,23 @@
 #define LOONGSON2_THSENS_INT_HIGH	BIT(1)
 #define LOONGSON2_THSENS_INT_EN		(LOONGSON2_THSENS_INT_LO | \
 					 LOONGSON2_THSENS_INT_HIGH)
-#define LOONGSON2_THSENS_OUT_MASK	0xFF
+#define LOONGSON2_THSENS_CTRL_INTEN	BIT(8)
+
+#define LOONGSON2_THSENS_OUT_8B_MASK	GENMASK(7, 0)
+#define LOONGSON2_THSENS_OUT_16B_MASK	GENMASK(15, 0)
+
+#define LOONGSON2_LOWEST_VALID_TEMP     (-40000)
+#define LOONGSON2_HIGHEST_VALID_TEMP    (125000)
 
 /* Loongson-2K2000-Specific Registers */
 #define LS2K2000_THSENS_HI_GATE_HI_REG	0x18
 #define LS2K2000_THSENS_HI_GATE_LOW_REG	0x1c
+#define LS2K2000_GATE0_VAL_MASK		GENMASK(7, 0)
+#define LS2K2000_THSENS_SEL_MASK	GENMASK(11, 10)
+
+#define LS2K2000_TEMP_MUL		(820)
+#define LS2K2000_TEMP_DIV		(0x4000)
+#define LS2K2000_TEMP_OFFSET		(311)
 
 /*
  * This flag is used to indicate the temperature reading
@@ -64,71 +77,101 @@ static void loongson2_thermal_set_trip_temp(struct loongson2_thermal_data *data,
 	data->chip_data->set_trips_regs(data, high, false, enable);
 }
 
-static int loongson2_thermal_set_trips(struct thermal_zone_device *tz, int low, int high)
+static int loognson2_thermal_set_trips(struct thermal_zone_device *tz, int low, int high)
 {
 	struct loongson2_thermal_data *data = thermal_zone_device_priv(tz);
 
-	low = clamp(low, -40000, 125000);
-	high = clamp(high, -40000, 125000);
+	low = clamp(low, LOONGSON2_LOWEST_VALID_TEMP, LOONGSON2_HIGHEST_VALID_TEMP);
+	high = clamp(high, LOONGSON2_LOWEST_VALID_TEMP, LOONGSON2_HIGHEST_VALID_TEMP);
 
 	loongson2_thermal_set_trip_temp(data, low / MILLI, high / MILLI, true);
 
 	return 0;
 }
 
-static void loongson2_2k1000_set_trips_regs(struct loongson2_thermal_data *data,
+static int ls2k1000_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+	struct loongson2_thermal_data *data = thermal_zone_device_priv(tz);
+	int val;
+
+	val = FIELD_GET(LOONGSON2_THSENS_OUT_8B_MASK,
+			readl(data->ctrl_reg + LOONGSON2_THSENS_OUT_REG));
+	*temp = (val - HECTO) * KILO;
+
+	return 0;
+}
+
+static void ls2k1000_thermal_set_trips_regs(struct loongson2_thermal_data *data,
 					    int temp, bool low, bool enable)
 {
-	int reg_ctrl = 0;
-	int reg_off  = data->chip_data->thermal_sensor_sel * 2;
 	int ctrl_reg = low ? LOONGSON2_THSENS_CTRL_LOW_REG : LOONGSON2_THSENS_CTRL_HI_REG;
+	int reg_off  = data->chip_data->thermal_sensor_sel * 2;
+	int reg_ctrl = 0;
 
 	reg_ctrl = temp + HECTO;
-	reg_ctrl |= enable ? 0x100 : 0;
+	if (enable)
+		reg_ctrl |= LOONGSON2_THSENS_CTRL_INTEN;
+
 	writew(reg_ctrl, data->ctrl_reg + ctrl_reg + reg_off);
 }
 
-static int loongson2_2k1000_get_temp(struct thermal_zone_device *tz, int *temp)
+static int ls2k2000_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
 {
-	int val;
 	struct loongson2_thermal_data *data = thermal_zone_device_priv(tz);
+	int val;
 
-	val = readl(data->ctrl_reg + LOONGSON2_THSENS_OUT_REG);
-	*temp = ((val & LOONGSON2_THSENS_OUT_MASK) - HECTO) * KILO;
+	val = FIELD_GET(LOONGSON2_THSENS_OUT_16B_MASK, readl(data->temp_reg));
+	*temp = (val * LS2K2000_TEMP_MUL / LS2K2000_TEMP_DIV - LS2K2000_TEMP_OFFSET) * KILO;
 
 	return 0;
 }
 
-static void loongson2_2k2000_set_trips_regs(struct loongson2_thermal_data *data,
+static void ls2k2000_thermal_set_trips_regs(struct loongson2_thermal_data *data,
 					    int temp, bool low, bool enable)
 {
 	int hi_reg, ctrl_reg;
 	int val = 0, gate;
 
 	ctrl_reg = low ? LOONGSON2_THSENS_CTRL_LOW_REG : LOONGSON2_THSENS_CTRL_HI_REG;
-	gate = (temp + 311) * 0x4000 / 820;
+	gate = (temp + LS2K2000_TEMP_OFFSET) * LS2K2000_TEMP_DIV / LS2K2000_TEMP_MUL;
+
+	val = FIELD_PREP(LS2K2000_GATE0_VAL_MASK, FIELD_GET(GENMASK(7, 0), gate)) |
+	      FIELD_PREP(LS2K2000_THSENS_SEL_MASK, 0);
+	if (enable)
+		val |= LOONGSON2_THSENS_CTRL_INTEN;
 
-	val = gate & 0xff;
-	val |= enable ? 0x100 : 0;
 	writel(val, data->ctrl_reg + ctrl_reg);
 
 	hi_reg = low ? LS2K2000_THSENS_HI_GATE_LOW_REG : LS2K2000_THSENS_HI_GATE_HI_REG;
 
 	val = readl(data->ctrl_reg + hi_reg);
-	val = (val & ~0xff) | ((gate >> 8) & 0xff);
+	val &= ~LS2K2000_GATE0_VAL_MASK;
+	val |= FIELD_PREP(LS2K2000_GATE0_VAL_MASK, FIELD_GET(GENMASK(15, 8), gate));
+
 	writel(val, data->ctrl_reg + hi_reg);
 }
 
-static int loongson2_2k2000_get_temp(struct thermal_zone_device *tz, int *temp)
-{
-	int val;
-	struct loongson2_thermal_data *data = thermal_zone_device_priv(tz);
+static const struct thermal_zone_device_ops ls2k1000_thermal_zone_ops = {
+	.get_temp = ls2k1000_thermal_get_temp,
+	.set_trips = loognson2_thermal_set_trips,
+};
 
-	val = readl(data->temp_reg);
-	*temp = ((val & 0xffff) * 820 / 0x4000 - 311) * KILO;
+static const struct thermal_zone_device_ops ls2k2000_thermal_zone_ops = {
+	.get_temp = ls2k2000_thermal_get_temp,
+	.set_trips = loognson2_thermal_set_trips,
+};
 
-	return 0;
-}
+static const struct loongson2_thermal_chip_data ls2k1000_thermal_chip_data = {
+	.thermal_sensor_sel = 0,
+	.flags = 0,
+	.set_trips_regs = ls2k1000_thermal_set_trips_regs,
+};
+
+static const struct loongson2_thermal_chip_data ls2k2000_thermal_chip_data = {
+	.thermal_sensor_sel = 0,
+	.flags = LS2K2000_THSENS_OUT_FLAG,
+	.set_trips_regs = ls2k2000_thermal_set_trips_regs,
+};
 
 static irqreturn_t loongson2_thermal_irq_thread(int irq, void *dev)
 {
@@ -142,16 +185,6 @@ static irqreturn_t loongson2_thermal_irq_thread(int irq, void *dev)
 	return IRQ_HANDLED;
 }
 
-static const struct thermal_zone_device_ops loongson2_2k1000_of_thermal_ops = {
-	.get_temp = loongson2_2k1000_get_temp,
-	.set_trips = loongson2_thermal_set_trips,
-};
-
-static const struct thermal_zone_device_ops loongson2_2k2000_of_thermal_ops = {
-	.get_temp = loongson2_2k2000_get_temp,
-	.set_trips = loongson2_thermal_set_trips,
-};
-
 static int loongson2_thermal_probe(struct platform_device *pdev)
 {
 	const struct thermal_zone_device_ops *thermal_ops;
@@ -176,9 +209,9 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
 		if (IS_ERR(data->temp_reg))
 			return PTR_ERR(data->temp_reg);
 
-		thermal_ops = &loongson2_2k2000_of_thermal_ops;
+		thermal_ops = &ls2k2000_thermal_zone_ops;
 	} else {
-		thermal_ops = &loongson2_2k1000_of_thermal_ops;
+		thermal_ops = &ls2k1000_thermal_zone_ops;
 	}
 
 	irq = platform_get_irq(pdev, 0);
@@ -213,26 +246,14 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
 	return 0;
 }
 
-static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k1000_data = {
-	.thermal_sensor_sel = 0,
-	.flags = 0,
-	.set_trips_regs = loongson2_2k1000_set_trips_regs,
-};
-
-static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k2000_data = {
-	.thermal_sensor_sel = 0,
-	.flags = LS2K2000_THSENS_OUT_FLAG,
-	.set_trips_regs = loongson2_2k2000_set_trips_regs,
-};
-
 static const struct of_device_id of_loongson2_thermal_match[] = {
 	{
 		.compatible = "loongson,ls2k1000-thermal",
-		.data = &loongson2_thermal_ls2k1000_data,
+		.data = &ls2k1000_thermal_chip_data,
 	},
 	{
 		.compatible = "loongson,ls2k2000-thermal",
-		.data = &loongson2_thermal_ls2k2000_data,
+		.data = &ls2k2000_thermal_chip_data,
 	},
 	{ /* end */ }
 };
-- 
2.52.0


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

* [PATCH v6 5/7] thermal/drivers/loongson2: Move per-SoC ops into chip data
  2026-09-28  4:44 [PATCH v6 0/7] thermal: loongson2: Add support for Loongson-2K0300 SoC Binbin Zhou
                   ` (3 preceding siblings ...)
  2026-09-28  4:44 ` [PATCH v6 4/7] thermal/drivers/loongson2: Use bitfields and per-SoC thermal ops Binbin Zhou
@ 2026-09-28  4:44 ` Binbin Zhou
  2026-09-28  4:44 ` [PATCH v6 6/7] dt-bindings: thermal: loongson,ls2k-thermal: Add compatible for Loongson-2K0300 Binbin Zhou
  2026-09-28  4:44 ` [PATCH v6 7/7] thermal/drivers/loongson2: Add Loongson-2K0300 support Binbin Zhou
  6 siblings, 0 replies; 18+ messages in thread
From: Binbin Zhou @ 2026-09-28  4:44 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yinbo Zhu, Rafael J . Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-pm,
	Binbin Zhou

Store the thermal_zone_device_ops pointer and the optional extended
resource initializer in struct loongson2_thermal_chip_data instead of
selecting them through flags in probe.

Rename the second mapped resource from temp_reg to extend_reg, and let
Loongson-2K2000 initialize it from its own init_extend_resource()
callback. This makes adding new SoCs with different register layouts
easier.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/thermal/loongson2_thermal.c | 50 ++++++++++++++++-------------
 1 file changed, 27 insertions(+), 23 deletions(-)

diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
index 0660a74fd922..17aa1d80a2e6 100644
--- a/drivers/thermal/loongson2_thermal.c
+++ b/drivers/thermal/loongson2_thermal.c
@@ -46,24 +46,20 @@
 #define LS2K2000_TEMP_DIV		(0x4000)
 #define LS2K2000_TEMP_OFFSET		(311)
 
-/*
- * This flag is used to indicate the temperature reading
- * method of the Loongson-2K2000
- */
-#define LS2K2000_THSENS_OUT_FLAG	BIT(0)
-
 struct loongson2_thermal_data;
 
 struct loongson2_thermal_chip_data {
 	unsigned int thermal_sensor_sel;
-	unsigned int flags;
+	const struct thermal_zone_device_ops *thermal_ops;
 	void (*set_trips_regs)(struct loongson2_thermal_data *data,
 			       int temp, bool low, bool enable);
+	int (*init_extend_resource)(struct platform_device *pdev,
+				    struct loongson2_thermal_data *data);
 };
 
 struct loongson2_thermal_data {
 	void __iomem *ctrl_reg;
-	void __iomem *temp_reg;
+	void __iomem *extend_reg;
 	const struct loongson2_thermal_chip_data *chip_data;
 };
 
@@ -120,7 +116,7 @@ static int ls2k2000_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
 	struct loongson2_thermal_data *data = thermal_zone_device_priv(tz);
 	int val;
 
-	val = FIELD_GET(LOONGSON2_THSENS_OUT_16B_MASK, readl(data->temp_reg));
+	val = FIELD_GET(LOONGSON2_THSENS_OUT_16B_MASK, readl(data->extend_reg));
 	*temp = (val * LS2K2000_TEMP_MUL / LS2K2000_TEMP_DIV - LS2K2000_TEMP_OFFSET) * KILO;
 
 	return 0;
@@ -151,6 +147,17 @@ static void ls2k2000_thermal_set_trips_regs(struct loongson2_thermal_data *data,
 	writel(val, data->ctrl_reg + hi_reg);
 }
 
+static int ls2k2000_thermal_init_extend_resource(struct platform_device *pdev,
+						 struct loongson2_thermal_data *data)
+{
+	/* The temperature output register is separate for Loongson-2K2000 */
+	data->extend_reg = devm_platform_ioremap_resource(pdev, 1);
+	if (IS_ERR(data->extend_reg))
+		return PTR_ERR(data->extend_reg);
+
+	return 0;
+}
+
 static const struct thermal_zone_device_ops ls2k1000_thermal_zone_ops = {
 	.get_temp = ls2k1000_thermal_get_temp,
 	.set_trips = loognson2_thermal_set_trips,
@@ -163,14 +170,15 @@ static const struct thermal_zone_device_ops ls2k2000_thermal_zone_ops = {
 
 static const struct loongson2_thermal_chip_data ls2k1000_thermal_chip_data = {
 	.thermal_sensor_sel = 0,
-	.flags = 0,
+	.thermal_ops = &ls2k1000_thermal_zone_ops,
 	.set_trips_regs = ls2k1000_thermal_set_trips_regs,
 };
 
 static const struct loongson2_thermal_chip_data ls2k2000_thermal_chip_data = {
 	.thermal_sensor_sel = 0,
-	.flags = LS2K2000_THSENS_OUT_FLAG,
+	.thermal_ops = &ls2k2000_thermal_zone_ops,
 	.set_trips_regs = ls2k2000_thermal_set_trips_regs,
+	.init_extend_resource = ls2k2000_thermal_init_extend_resource,
 };
 
 static irqreturn_t loongson2_thermal_irq_thread(int irq, void *dev)
@@ -187,9 +195,8 @@ static irqreturn_t loongson2_thermal_irq_thread(int irq, void *dev)
 
 static int loongson2_thermal_probe(struct platform_device *pdev)
 {
-	const struct thermal_zone_device_ops *thermal_ops;
-	struct device *dev = &pdev->dev;
 	struct loongson2_thermal_data *data;
+	struct device *dev = &pdev->dev;
 	struct thermal_zone_device *tzd;
 	int ret, irq, i;
 
@@ -198,20 +205,17 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	data->chip_data = device_get_match_data(dev);
+	if (!data->chip_data)
+		return -EINVAL;
 
 	data->ctrl_reg = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(data->ctrl_reg))
 		return PTR_ERR(data->ctrl_reg);
 
-	/* The temperature output register is separate for Loongson-2K2000 */
-	if (data->chip_data->flags & LS2K2000_THSENS_OUT_FLAG) {
-		data->temp_reg = devm_platform_ioremap_resource(pdev, 1);
-		if (IS_ERR(data->temp_reg))
-			return PTR_ERR(data->temp_reg);
-
-		thermal_ops = &ls2k2000_thermal_zone_ops;
-	} else {
-		thermal_ops = &ls2k1000_thermal_zone_ops;
+	if (data->chip_data->init_extend_resource) {
+		ret = data->chip_data->init_extend_resource(pdev, data);
+		if (ret)
+			return ret;
 	}
 
 	irq = platform_get_irq(pdev, 0);
@@ -223,7 +227,7 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
 	loongson2_thermal_set_trip_temp(data, 0, 0, false);
 
 	for (i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
-		tzd = devm_thermal_of_zone_register(dev, i, data, thermal_ops);
+		tzd = devm_thermal_of_zone_register(dev, i, data, data->chip_data->thermal_ops);
 		if (!IS_ERR(tzd))
 			break;
 
-- 
2.52.0


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

* [PATCH v6 6/7] dt-bindings: thermal: loongson,ls2k-thermal: Add compatible for Loongson-2K0300
  2026-09-28  4:44 [PATCH v6 0/7] thermal: loongson2: Add support for Loongson-2K0300 SoC Binbin Zhou
                   ` (4 preceding siblings ...)
  2026-09-28  4:44 ` [PATCH v6 5/7] thermal/drivers/loongson2: Move per-SoC ops into chip data Binbin Zhou
@ 2026-09-28  4:44 ` Binbin Zhou
  2026-09-28  4:44 ` [PATCH v6 7/7] thermal/drivers/loongson2: Add Loongson-2K0300 support Binbin Zhou
  6 siblings, 0 replies; 18+ messages in thread
From: Binbin Zhou @ 2026-09-28  4:44 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yinbo Zhu, Rafael J . Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-pm,
	Binbin Zhou, Conor Dooley

Add a new compatible string `loongson,ls2k0300-thermal` for the thermal
sensor found on the Loongson-2K0300 SoC.

The hardware differs from existing SoCs in that it requires a phandle to
a syscon node that provides the CHIP ID register, used as a compensation
source in the temperature calculation.

Update the binding to make `loongson,chipid` required for this new
compatible, and clarify the register region requirements for each
variant.

Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 .../thermal/loongson,ls2k-thermal.yaml        | 73 ++++++++++++++-----
 1 file changed, 55 insertions(+), 18 deletions(-)

diff --git a/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml b/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml
index 79e691b08341..d312e27737a2 100644
--- a/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml
+++ b/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml
@@ -10,13 +10,11 @@ maintainers:
   - zhanghongchen <zhanghongchen@loongson.cn>
   - Yinbo Zhu <zhuyinbo@loongson.cn>
 
-allOf:
-  - $ref: /schemas/thermal/thermal-sensor.yaml#
-
 properties:
   compatible:
     oneOf:
       - enum:
+          - loongson,ls2k0300-thermal
           - loongson,ls2k1000-thermal
           - loongson,ls2k2000-thermal
       - items:
@@ -34,28 +32,56 @@ properties:
   '#thermal-sensor-cells':
     const: 1
 
+  loongson,chipid:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle to the syscon with the Loongson-2K0300 CHIP ID.
+
 required:
   - compatible
   - reg
   - interrupts
 
-if:
-  properties:
-    compatible:
-      contains:
-        enum:
-          - loongson,ls2k2000-thermal
+allOf:
+  - $ref: /schemas/thermal/thermal-sensor.yaml#
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: loongson,ls2k0300-thermal
+    then:
+      properties:
+        reg:
+          items:
+            - description: Thermal base register region
+      required:
+        - loongson,chipid
 
-then:
-  properties:
-    reg:
-      minItems: 2
-      maxItems: 2
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: loongson,ls2k1000-thermal
+    then:
+      properties:
+        loongson,chipid: false
+        reg:
+          items:
+            - description: Thermal base register region
 
-else:
-  properties:
-    reg:
-      maxItems: 1
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: loongson,ls2k2000-thermal
+    then:
+      properties:
+        loongson,chipid: false
+        reg:
+          items:
+            - description: Thermal base register region
+            - description: Thermal data output register region
 
 unevaluatedProperties: false
 
@@ -69,3 +95,14 @@ examples:
         interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
         #thermal-sensor-cells = <1>;
     };
+
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    thermal-sensor@16001500 {
+       compatible = "loongson,ls2k0300-thermal";
+       reg = <0x16001500 0x30>;
+       interrupt-parent = <&liointc1>;
+       interrupts = <20 IRQ_TYPE_LEVEL_HIGH>;
+       loongson,chipid = <&chipid>;
+       #thermal-sensor-cells = <1>;
+    };
-- 
2.52.0


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

* [PATCH v6 7/7] thermal/drivers/loongson2: Add Loongson-2K0300 support
  2026-09-28  4:44 [PATCH v6 0/7] thermal: loongson2: Add support for Loongson-2K0300 SoC Binbin Zhou
                   ` (5 preceding siblings ...)
  2026-09-28  4:44 ` [PATCH v6 6/7] dt-bindings: thermal: loongson,ls2k-thermal: Add compatible for Loongson-2K0300 Binbin Zhou
@ 2026-09-28  4:44 ` Binbin Zhou
  2026-09-28  4:53   ` sashiko-bot
  6 siblings, 1 reply; 18+ messages in thread
From: Binbin Zhou @ 2026-09-28  4:44 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yinbo Zhu, Rafael J . Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-pm,
	Binbin Zhou

The Loongson-2K0300 sensor provides an 11-bit output and requires
calibration data from the CHIP ID registers. Read the calibration offset
throughthe `loongson,chipid` syscon phandle, initialize the sensor
configuration register, and calculate the temperature with the
calibrated slope and offset.

Early Loongson-2K0300 fuse revisions may provide unusable calibration
data. If the calibrated formula reports a temperature outside the valid
sensor range, fall back to the old-fuse slope with a zero calibration
offset.

The set_trips callback of Loongson-2K0300 will be added in the future.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/thermal/loongson2_thermal.c | 136 ++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)

diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
index 17aa1d80a2e6..a53d70b824f3 100644
--- a/drivers/thermal/loongson2_thermal.c
+++ b/drivers/thermal/loongson2_thermal.c
@@ -8,10 +8,12 @@
 #include <linux/bitfield.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/mfd/syscon.h>
 #include <linux/minmax.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/property.h>
+#include <linux/regmap.h>
 #include <linux/thermal.h>
 #include <linux/units.h>
 
@@ -31,11 +33,37 @@
 #define LOONGSON2_THSENS_CTRL_INTEN	BIT(8)
 
 #define LOONGSON2_THSENS_OUT_8B_MASK	GENMASK(7, 0)
+#define LOONGSON2_THSENS_OUT_11B_MASK	GENMASK(10, 0)
 #define LOONGSON2_THSENS_OUT_16B_MASK	GENMASK(15, 0)
 
 #define LOONGSON2_LOWEST_VALID_TEMP     (-40000)
 #define LOONGSON2_HIGHEST_VALID_TEMP    (125000)
 
+/* Loongson-2K0300-Specific Registers */
+#define LS2K0300_CHIP_ID0		0x10
+#define LS2K0300_CHIP_ID1		0x14
+
+#define LS2K0300_THSENS_CFG_REG		0x18
+#define LS2K0300_THSENS_INIT_MASK	0xff03
+
+#define LS2K0300_EXTERN_ID		BIT(4)
+#define LS2K0300_ID0_VAL_MASK		GENMASK(31, 20)
+#define LS2K0300_ID1_VAL_MASK		GENMASK(15, 0)
+
+#define LS2K0300_COMP_VAL_MASK		GENMASK(14, 0)
+#define LS2K0300_COMP_SIGN_BIT		BIT(15)
+
+#define LS2K0300_TEMP_SLOPE		(570)	/* m°C/LSB, calibrated */
+#define LS2K0300_TEMP_SLOPE_OLD		(569)	/* m°C/LSB, old fuse */
+#define LS2K0300_TEMP_OFFSET		(394700) /* m°C */
+
+/*
+ * Set when the on-chip calibration data is unusable (early 2K0300
+ * fuse revision). The driver then uses LS2K0300_TEMP_SLOPE_OLD with
+ * calib_offset = 0 instead of the calibrated formula.
+ */
+#define LS2K0300_OLD_FUSE_FLAG		BIT(0)
+
 /* Loongson-2K2000-Specific Registers */
 #define LS2K2000_THSENS_HI_GATE_HI_REG	0x18
 #define LS2K2000_THSENS_HI_GATE_LOW_REG	0x1c
@@ -60,12 +88,18 @@ struct loongson2_thermal_chip_data {
 struct loongson2_thermal_data {
 	void __iomem *ctrl_reg;
 	void __iomem *extend_reg;
+	struct regmap *regmap_cfg;
+	unsigned int flags;
+	int calib_offset;
 	const struct loongson2_thermal_chip_data *chip_data;
 };
 
 static void loongson2_thermal_set_trip_temp(struct loongson2_thermal_data *data,
 					    int low, int high, bool enable)
 {
+	if (!data->chip_data->set_trips_regs)
+		return;
+
 	/* Set low temperature threshold */
 	data->chip_data->set_trips_regs(data, low, true, enable);
 
@@ -85,6 +119,94 @@ static int loognson2_thermal_set_trips(struct thermal_zone_device *tz, int low,
 	return 0;
 }
 
+static int ls2k0300_adc_to_temp_mc(struct loongson2_thermal_data *data, int slope)
+{
+	int raw_adc;
+
+	raw_adc = FIELD_GET(LOONGSON2_THSENS_OUT_11B_MASK,
+			    readl(data->ctrl_reg + LOONGSON2_THSENS_OUT_REG));
+
+	return (raw_adc + data->calib_offset) * slope - LS2K0300_TEMP_OFFSET;
+}
+
+static int ls2k0300_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+	struct loongson2_thermal_data *data = thermal_zone_device_priv(tz);
+	int slope = LS2K0300_TEMP_SLOPE;
+
+	if (data->flags & LS2K0300_OLD_FUSE_FLAG)
+		slope = LS2K0300_TEMP_SLOPE_OLD;
+
+	*temp = ls2k0300_adc_to_temp_mc(data, slope);
+
+	return 0;
+}
+
+static void ls2k0300_thermal_detect_old_fuse(struct device *dev,
+					     struct loongson2_thermal_data *data)
+{
+	int temp;
+
+	temp = ls2k0300_adc_to_temp_mc(data, LS2K0300_TEMP_SLOPE);
+
+	if (temp < LOONGSON2_LOWEST_VALID_TEMP ||
+	    temp > LOONGSON2_HIGHEST_VALID_TEMP) {
+		dev_warn(dev, "calibrated temperature %d m°C out of range [%d, %d], using old fuse formula\n",
+			 temp, LOONGSON2_LOWEST_VALID_TEMP, LOONGSON2_HIGHEST_VALID_TEMP);
+		data->flags = LS2K0300_OLD_FUSE_FLAG;
+		data->calib_offset = 0;
+	}
+}
+
+static int ls2k0300_thermal_read_calib(struct loongson2_thermal_data *data)
+{
+	u32 chip_id0, chip_id1;
+	int calib_data, calib_offset, ret;
+
+	ret = regmap_read(data->regmap_cfg, LS2K0300_CHIP_ID0, &chip_id0);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(data->regmap_cfg, LS2K0300_CHIP_ID1, &chip_id1);
+	if (ret)
+		return ret;
+
+	if (chip_id0 & LS2K0300_EXTERN_ID) {
+		calib_data = FIELD_GET(LS2K0300_ID1_VAL_MASK, chip_id1);
+		calib_offset = FIELD_GET(LS2K0300_COMP_VAL_MASK, calib_data);
+		if (calib_data & LS2K0300_COMP_SIGN_BIT)
+			calib_offset = -calib_offset;
+	} else {
+		calib_data = FIELD_GET(LS2K0300_ID0_VAL_MASK, chip_id0);
+		calib_offset = FIELD_GET(LS2K0300_COMP_VAL_MASK, calib_data);
+	}
+
+	data->calib_offset = calib_offset;
+	return 0;
+}
+
+static int ls2k0300_thermal_init_extend_resource(struct platform_device *pdev,
+						 struct loongson2_thermal_data *data)
+{
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	data->regmap_cfg =
+		syscon_regmap_lookup_by_phandle(dev->of_node, "loongson,chipid");
+	if (IS_ERR(data->regmap_cfg))
+		return PTR_ERR(data->regmap_cfg);
+
+	ret = ls2k0300_thermal_read_calib(data);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to read calibration data\n");
+
+	writel(LS2K0300_THSENS_INIT_MASK, data->ctrl_reg + LS2K0300_THSENS_CFG_REG);
+
+	ls2k0300_thermal_detect_old_fuse(dev, data);
+
+	return 0;
+}
+
 static int ls2k1000_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
 {
 	struct loongson2_thermal_data *data = thermal_zone_device_priv(tz);
@@ -158,6 +280,10 @@ static int ls2k2000_thermal_init_extend_resource(struct platform_device *pdev,
 	return 0;
 }
 
+static const struct thermal_zone_device_ops ls2k0300_thermal_zone_ops = {
+	.get_temp = ls2k0300_thermal_get_temp,
+};
+
 static const struct thermal_zone_device_ops ls2k1000_thermal_zone_ops = {
 	.get_temp = ls2k1000_thermal_get_temp,
 	.set_trips = loognson2_thermal_set_trips,
@@ -168,6 +294,12 @@ static const struct thermal_zone_device_ops ls2k2000_thermal_zone_ops = {
 	.set_trips = loognson2_thermal_set_trips,
 };
 
+static const struct loongson2_thermal_chip_data ls2k0300_thermal_chip_data = {
+	.thermal_sensor_sel = 0,
+	.thermal_ops = &ls2k0300_thermal_zone_ops,
+	.init_extend_resource = ls2k0300_thermal_init_extend_resource,
+};
+
 static const struct loongson2_thermal_chip_data ls2k1000_thermal_chip_data = {
 	.thermal_sensor_sel = 0,
 	.thermal_ops = &ls2k1000_thermal_zone_ops,
@@ -251,6 +383,10 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
 }
 
 static const struct of_device_id of_loongson2_thermal_match[] = {
+	{
+		.compatible = "loongson,ls2k0300-thermal",
+		.data = &ls2k0300_thermal_chip_data,
+	},
 	{
 		.compatible = "loongson,ls2k1000-thermal",
 		.data = &ls2k1000_thermal_chip_data,
-- 
2.52.0


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

* Re: [PATCH v6 4/7] thermal/drivers/loongson2: Use bitfields and per-SoC thermal ops
  2026-09-28  4:44 ` [PATCH v6 4/7] thermal/drivers/loongson2: Use bitfields and per-SoC thermal ops Binbin Zhou
@ 2026-09-28  4:52   ` sashiko-bot
  0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-09-28  4:52 UTC (permalink / raw)
  To: Binbin Zhou; +Cc: devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] thermal: misspelled function name loognson2_thermal_set_trips()
--

commit 50b534520f37f80d31dade061303171856000e41
Author: Binbin Zhou <zhoubinbin@loongson.cn>

thermal/drivers/loongson2: Use bitfields and per-SoC thermal ops

This commit replaces open-coded masks and shifts with GENMASK() and FIELD_*
helpers, and defines named constants for the Loongson-2K2000. It also
splits the temperature read and thermal zone operations into SoC-specific
variants.

> diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
> index 45fe6100134ee..0660a74fd9222 100644
> --- a/drivers/thermal/loongson2_thermal.c
> +++ b/drivers/thermal/loongson2_thermal.c
[ ... ]
> @@ -64,71 +77,101 @@ static void loongson2_thermal_set_trip_temp(struct loongson2_thermal_data *data,
>  	data->chip_data->set_trips_regs(data, high, false, enable);
>  }
>  
> -static int loongson2_thermal_set_trips(struct thermal_zone_device *tz, int low, int high)
> +static int loognson2_thermal_set_trips(struct thermal_zone_device *tz, int low, int high)

[Severity: Low]
This isn't a bug, but is there a typo in this function name? It looks like
"loongson2" was accidentally misspelled as "loognson2" here.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1790561983.git.zhoubinbin@loongson.cn?part=4

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

* Re: [PATCH v6 7/7] thermal/drivers/loongson2: Add Loongson-2K0300 support
  2026-09-28  4:44 ` [PATCH v6 7/7] thermal/drivers/loongson2: Add Loongson-2K0300 support Binbin Zhou
@ 2026-09-28  4:53   ` sashiko-bot
  0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-09-28  4:53 UTC (permalink / raw)
  To: Binbin Zhou; +Cc: conor+dt, devicetree, robh

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] thermal/loongson2: hardware race in ADC initialization triggers false fallback
- [Medium] thermal/loongson2: missing sign bit check for ID0 calibration data
--

commit 0cea5de8d422086175b271d663470bc3da490430
Author: Binbin Zhou <zhoubinbin@loongson.cn>

thermal/drivers/loongson2: Add Loongson-2K0300 support
    
This commit adds support for the Loongson-2K0300 thermal sensor by reading
calibration offsets from syscon, initializing the sensor, and calculating
the temperature. It also includes logic to detect early fuse revisions and
fall back to an older calibration formula if the initial reading is out of
bounds.

> diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
> index 17aa1d80a2e67..a53d70b824f32 100644
> --- a/drivers/thermal/loongson2_thermal.c
> +++ b/drivers/thermal/loongson2_thermal.c
[ ... ]
> +static int ls2k0300_thermal_read_calib(struct loongson2_thermal_data *data)
> +{
> +	u32 chip_id0, chip_id1;
> +	int calib_data, calib_offset, ret;
> +
> +	ret = regmap_read(data->regmap_cfg, LS2K0300_CHIP_ID0, &chip_id0);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_read(data->regmap_cfg, LS2K0300_CHIP_ID1, &chip_id1);
> +	if (ret)
> +		return ret;
> +
> +	if (chip_id0 & LS2K0300_EXTERN_ID) {
> +		calib_data = FIELD_GET(LS2K0300_ID1_VAL_MASK, chip_id1);
> +		calib_offset = FIELD_GET(LS2K0300_COMP_VAL_MASK, calib_data);
> +		if (calib_data & LS2K0300_COMP_SIGN_BIT)
> +			calib_offset = -calib_offset;
> +	} else {
> +		calib_data = FIELD_GET(LS2K0300_ID0_VAL_MASK, chip_id0);
> +		calib_offset = FIELD_GET(LS2K0300_COMP_VAL_MASK, calib_data);
> +	}

[Severity: Medium]
Is there a missing sign bit check for the chip_id0 branch?

The chip_id1 branch explicitly checks LS2K0300_COMP_SIGN_BIT to negate the
offset, but the chip_id0 branch does not. Furthermore, the extracted 
LS2K0300_ID0_VAL_MASK is 12 bits, but it is masked against 
LS2K0300_COMP_VAL_MASK which is 15 bits. 

Could negative calibration offsets provided via chip_id0 be incorrectly 
treated as large positive offsets, leading to completely wrong temperature 
readings?

> +
> +	data->calib_offset = calib_offset;
> +	return 0;
> +}
> +
> +static int ls2k0300_thermal_init_extend_resource(struct platform_device *pdev,
> +						 struct loongson2_thermal_data *data)
> +{
> +	struct device *dev = &pdev->dev;
> +	int ret;
> +
> +	data->regmap_cfg =
> +		syscon_regmap_lookup_by_phandle(dev->of_node, "loongson,chipid");
> +	if (IS_ERR(data->regmap_cfg))
> +		return PTR_ERR(data->regmap_cfg);
> +
> +	ret = ls2k0300_thermal_read_calib(data);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "failed to read calibration data\n");
> +
> +	writel(LS2K0300_THSENS_INIT_MASK, data->ctrl_reg + LS2K0300_THSENS_CFG_REG);
> +
> +	ls2k0300_thermal_detect_old_fuse(dev, data);

[Severity: Medium]
Could this trigger a false fallback to the old fuse formula due to a hardware
initialization race?

Here, the sensor is initialized by writing LS2K0300_THSENS_INIT_MASK to the
configuration register. Immediately after, ls2k0300_thermal_detect_old_fuse() 
is called, which reads the ADC value via ls2k0300_adc_to_temp_mc() without any
readiness poll or sleep delay.

If the hardware ADC requires time to complete its first conversion after being
configured, this immediate read might yield an artificially low or invalid
temperature. This would cause the out-of-bounds check in 
ls2k0300_thermal_detect_old_fuse() to fail, permanently falling back to the 
incorrect old fuse formula.

> +
> +	return 0;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1790561983.git.zhoubinbin@loongson.cn?part=7

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

* Re: [PATCH v6 3/7] thermal/drivers/loongson2: Fix Loongson-2K2000 trip point programming
  2026-09-28  4:44 ` [PATCH v6 3/7] thermal/drivers/loongson2: Fix Loongson-2K2000 trip point programming Binbin Zhou
@ 2026-09-28  4:54   ` sashiko-bot
  0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-09-28  4:54 UTC (permalink / raw)
  To: Binbin Zhou; +Cc: robh, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] thermal: torn write in loongson2_2k2000_set_trips_regs() can trigger spurious interrupts
--

commit 89df6fff6e860d7cc010f8ec79a9f1531d8e5cca
Author: Binbin Zhou <zhoubinbin@loongson.cn>

    thermal/drivers/loongson2: Fix Loongson-2K2000 trip point programming
    
    Loongson-2K2000 uses a different trip point layout than Loongson-2K1000:
    the low 8 bits of the gate value are written to the control register,
    while the high 8 bits are written to separate HI_GATE_HI/LOW registers.
    The gate value is calculated as (temp + 311) * 0x4000 / 820.
    
    Introduce a per-SoC set_trips_regs() callback. Keep the existing
    Loongson-2K1000 logic and add the Loongson-2K2000 implementation.
    Move the common clamping into loongson2_thermal_set_trips() and pass
    Celsius values to the callback.

> diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
> index bcabca3601572..45fe6100134ee 100644
> --- a/drivers/thermal/loongson2_thermal.c
> +++ b/drivers/thermal/loongson2_thermal.c

[ ... ]

> @@ -81,6 +99,26 @@ static int loongson2_2k1000_get_temp(struct thermal_zone_device *tz, int *temp)
>  	return 0;
>  }
>  
> +static void loongson2_2k2000_set_trips_regs(struct loongson2_thermal_data *data,
> +					    int temp, bool low, bool enable)
> +{
> +	int hi_reg, ctrl_reg;
> +	int val = 0, gate;
> +
> +	ctrl_reg = low ? LOONGSON2_THSENS_CTRL_LOW_REG : LOONGSON2_THSENS_CTRL_HI_REG;
> +	gate = (temp + 311) * 0x4000 / 820;
> +
> +	val = gate & 0xff;
> +	val |= enable ? 0x100 : 0;
> +	writel(val, data->ctrl_reg + ctrl_reg);
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

[Severity: Medium]
Will this writel() cause the hardware to evaluate a torn threshold?

By writing the low 8 bits and immediately enabling the hardware comparator here,
before the upper 8 bits are written to hi_reg below, there is a time window
where the active hardware uses a torn threshold (new low bits, old high bits).

If this torn value is momentarily crossed by the current live temperature, could
it fire a spurious hardware interrupt?

> +
> +	hi_reg = low ? LS2K2000_THSENS_HI_GATE_LOW_REG : LS2K2000_THSENS_HI_GATE_HI_REG;
> +
> +	val = readl(data->ctrl_reg + hi_reg);
> +	val = (val & ~0xff) | ((gate >> 8) & 0xff);
> +	writel(val, data->ctrl_reg + hi_reg);
> +}
> +
>  static int loongson2_2k2000_get_temp(struct thermal_zone_device *tz, int *temp)
>  {

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1790561983.git.zhoubinbin@loongson.cn?part=3

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

* Re: [PATCH v6 1/7] thermal/drivers/loongson2: Correct thermal sensor registration loop
  2026-09-28  4:44 ` [PATCH v6 1/7] thermal/drivers/loongson2: Correct thermal sensor registration loop Binbin Zhou
@ 2026-09-28  7:15   ` Daniel Lezcano
  2026-09-28  8:17     ` Binbin Zhou
  0 siblings, 1 reply; 18+ messages in thread
From: Daniel Lezcano @ 2026-09-28  7:15 UTC (permalink / raw)
  To: Binbin Zhou, Binbin Zhou, Huacai Chen, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Yinbo Zhu, Rafael J . Wysocki,
	Daniel Lezcano, Zhang Rui, Lukasz Luba
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-pm, stable,
	Sashiko

On 9/28/26 06:44, Binbin Zhou wrote:
> The registration loop in loongson2_thermal_probe() incorrectly uses
> dev_err_probe() when the sensor is not present (-ENODEV). In that case,
> the driver should continue to the next sensor index rather than treating
> it as a fatal error.
> 
> Fix this by correctly handling -ENODEV and only returning on other
> errors. Also add a final check to ensure at least one thermal zone was
> registered.
> 
> Cc: stable@vger.kernel.org
> Reported-by: Sashiko <sashiko-bot@kernel.org>
> Closes: https://sashiko.dev/#/patchset/cover.1783670011.git.zhoubinbin@loongson.cn?part=2
> Fixes: e7e3a7c35791 ("thermal/drivers/loongson-2: Add thermal management support")
> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> ---
>   drivers/thermal/loongson2_thermal.c | 8 +++++---
>   1 file changed, 5 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
> index 4d40fc706a53..99b7392fc68f 100644
> --- a/drivers/thermal/loongson2_thermal.c
> +++ b/drivers/thermal/loongson2_thermal.c
> @@ -160,16 +160,18 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
>   
>   	for (i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
>   		tzd = devm_thermal_of_zone_register(dev, i, data, thermal_ops);
> -
>   		if (!IS_ERR(tzd))
>   			break;
>   
> -		if (PTR_ERR(tzd) != -ENODEV)
> +		if (PTR_ERR(tzd) == -ENODEV)
>   			continue;
>   
> -		return dev_err_probe(dev, PTR_ERR(tzd), "failed to register");
> +		return dev_err_probe(dev, PTR_ERR(tzd), "failed to register sensor %d\n", i);


>   	}
>   
> +	if (IS_ERR(tzd))
> +		return dev_err_probe(dev, -ENODEV, "No thermal sensor registered\n");
> +
>   	ret = devm_request_threaded_irq(dev, irq, NULL, loongson2_thermal_irq_thread,
>   					IRQF_ONESHOT, "loongson2_thermal", tzd);
>   	if (ret < 0)


The [no]error paths are confusing and prone to error, eg: 'if there is 
no error then check if there is an error', 'when exiting the loop 
without registering any sensor, then we assume tzd is -ENODEV'.

Please make a less cumbersome and implicit checks here. For example 
count the number of sensors and then do the check against.

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

* Re: [PATCH v6 1/7] thermal/drivers/loongson2: Correct thermal sensor registration loop
  2026-09-28  7:15   ` Daniel Lezcano
@ 2026-09-28  8:17     ` Binbin Zhou
  2026-09-28 13:23       ` Daniel Lezcano
  0 siblings, 1 reply; 18+ messages in thread
From: Binbin Zhou @ 2026-09-28  8:17 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yinbo Zhu, Rafael J . Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Huacai Chen, Xuerui Wang, loongarch,
	devicetree, linux-pm, stable, Sashiko

Hi Daniel:

Thanks for your review.

On Mon, Sep 28, 2026 at 3:15 PM Daniel Lezcano
<daniel.lezcano@oss.qualcomm.com> wrote:
>
> On 9/28/26 06:44, Binbin Zhou wrote:
> > The registration loop in loongson2_thermal_probe() incorrectly uses
> > dev_err_probe() when the sensor is not present (-ENODEV). In that case,
> > the driver should continue to the next sensor index rather than treating
> > it as a fatal error.
> >
> > Fix this by correctly handling -ENODEV and only returning on other
> > errors. Also add a final check to ensure at least one thermal zone was
> > registered.
> >
> > Cc: stable@vger.kernel.org
> > Reported-by: Sashiko <sashiko-bot@kernel.org>
> > Closes: https://sashiko.dev/#/patchset/cover.1783670011.git.zhoubinbin@loongson.cn?part=2
> > Fixes: e7e3a7c35791 ("thermal/drivers/loongson-2: Add thermal management support")
> > Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> > ---
> >   drivers/thermal/loongson2_thermal.c | 8 +++++---
> >   1 file changed, 5 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
> > index 4d40fc706a53..99b7392fc68f 100644
> > --- a/drivers/thermal/loongson2_thermal.c
> > +++ b/drivers/thermal/loongson2_thermal.c
> > @@ -160,16 +160,18 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
> >
> >       for (i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
> >               tzd = devm_thermal_of_zone_register(dev, i, data, thermal_ops);
> > -
> >               if (!IS_ERR(tzd))
> >                       break;
> >
> > -             if (PTR_ERR(tzd) != -ENODEV)
> > +             if (PTR_ERR(tzd) == -ENODEV)
> >                       continue;
> >
> > -             return dev_err_probe(dev, PTR_ERR(tzd), "failed to register");
> > +             return dev_err_probe(dev, PTR_ERR(tzd), "failed to register sensor %d\n", i);
>
>
> >       }
> >
> > +     if (IS_ERR(tzd))
> > +             return dev_err_probe(dev, -ENODEV, "No thermal sensor registered\n");
> > +
> >       ret = devm_request_threaded_irq(dev, irq, NULL, loongson2_thermal_irq_thread,
> >                                       IRQF_ONESHOT, "loongson2_thermal", tzd);
> >       if (ret < 0)
>
>
> The [no]error paths are confusing and prone to error, eg: 'if there is
> no error then check if there is an error', 'when exiting the loop
> without registering any sensor, then we assume tzd is -ENODEV'.
>
> Please make a less cumbersome and implicit checks here. For example
> count the number of sensors and then do the check against.

I thought about it, and you're right—I made the logic here too complicated.
Let's use `sensor_count` as the counter. Is the following check logic clearer?

        u32 sensor_count = 0;

        for (unsigned int i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
                tzd = devm_thermal_of_zone_register(dev, i, data,
data->chip_data->thermal_ops);
                if (IS_ERR(tzd)) {
                        if (PTR_ERR(tzd) == -ENODEV)
                                continue;

                        return dev_err_probe(dev, PTR_ERR(tzd),
                                             "failed to register sensor %d", i);
                }

                sensor_count++;
                break;
        }

        if (!sensor_count)
                return dev_err_probe(dev, -ENODEV, "no thermal sensor
registered");


-- 
Thanks.
Binbin

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

* Re: [PATCH v6 1/7] thermal/drivers/loongson2: Correct thermal sensor registration loop
  2026-09-28  8:17     ` Binbin Zhou
@ 2026-09-28 13:23       ` Daniel Lezcano
  2026-09-29  3:06         ` Binbin Zhou
  0 siblings, 1 reply; 18+ messages in thread
From: Daniel Lezcano @ 2026-09-28 13:23 UTC (permalink / raw)
  To: Binbin Zhou
  Cc: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yinbo Zhu, Rafael J . Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Huacai Chen, Xuerui Wang, loongarch,
	devicetree, linux-pm, stable, Sashiko

On 9/28/26 10:17, Binbin Zhou wrote:
> Hi Daniel:
> 
> Thanks for your review.
> 
> On Mon, Sep 28, 2026 at 3:15 PM Daniel Lezcano
> <daniel.lezcano@oss.qualcomm.com> wrote:
>>
>> On 9/28/26 06:44, Binbin Zhou wrote:
>>> The registration loop in loongson2_thermal_probe() incorrectly uses
>>> dev_err_probe() when the sensor is not present (-ENODEV). In that case,
>>> the driver should continue to the next sensor index rather than treating
>>> it as a fatal error.
>>>
>>> Fix this by correctly handling -ENODEV and only returning on other
>>> errors. Also add a final check to ensure at least one thermal zone was
>>> registered.
>>>
>>> Cc: stable@vger.kernel.org
>>> Reported-by: Sashiko <sashiko-bot@kernel.org>
>>> Closes: https://sashiko.dev/#/patchset/cover.1783670011.git.zhoubinbin@loongson.cn?part=2
>>> Fixes: e7e3a7c35791 ("thermal/drivers/loongson-2: Add thermal management support")
>>> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
>>> ---
>>>    drivers/thermal/loongson2_thermal.c | 8 +++++---
>>>    1 file changed, 5 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
>>> index 4d40fc706a53..99b7392fc68f 100644
>>> --- a/drivers/thermal/loongson2_thermal.c
>>> +++ b/drivers/thermal/loongson2_thermal.c
>>> @@ -160,16 +160,18 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
>>>
>>>        for (i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
>>>                tzd = devm_thermal_of_zone_register(dev, i, data, thermal_ops);
>>> -
>>>                if (!IS_ERR(tzd))
>>>                        break;
>>>
>>> -             if (PTR_ERR(tzd) != -ENODEV)
>>> +             if (PTR_ERR(tzd) == -ENODEV)
>>>                        continue;
>>>
>>> -             return dev_err_probe(dev, PTR_ERR(tzd), "failed to register");
>>> +             return dev_err_probe(dev, PTR_ERR(tzd), "failed to register sensor %d\n", i);
>>
>>
>>>        }
>>>
>>> +     if (IS_ERR(tzd))
>>> +             return dev_err_probe(dev, -ENODEV, "No thermal sensor registered\n");
>>> +
>>>        ret = devm_request_threaded_irq(dev, irq, NULL, loongson2_thermal_irq_thread,
>>>                                        IRQF_ONESHOT, "loongson2_thermal", tzd);
>>>        if (ret < 0)
>>
>>
>> The [no]error paths are confusing and prone to error, eg: 'if there is
>> no error then check if there is an error', 'when exiting the loop
>> without registering any sensor, then we assume tzd is -ENODEV'.
>>
>> Please make a less cumbersome and implicit checks here. For example
>> count the number of sensors and then do the check against.
> 
> I thought about it, and you're right—I made the logic here too complicated.
> Let's use `sensor_count` as the counter. Is the following check logic clearer?
> 
>          u32 sensor_count = 0;
> 
>          for (unsigned int i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
>                  tzd = devm_thermal_of_zone_register(dev, i, data,
> data->chip_data->thermal_ops);
>                  if (IS_ERR(tzd)) {
>                          if (PTR_ERR(tzd) == -ENODEV)
>                                  continue;
> 
>                          return dev_err_probe(dev, PTR_ERR(tzd),
>                                               "failed to register sensor %d", i);
>                  }
> 
>                  sensor_count++;

If I'm not mistaken in the previous version, the loop exits at the first 
registered sensor

	if (!IS_ERR(tzd))
		break;

Here all the available sensors are registered

>                  break;
>          }
> 
>          if (!sensor_count)
>                  return dev_err_probe(dev, -ENODEV, "no thermal sensor
> registered");
> 
> 


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

* Re: [PATCH v6 1/7] thermal/drivers/loongson2: Correct thermal sensor registration loop
  2026-09-28 13:23       ` Daniel Lezcano
@ 2026-09-29  3:06         ` Binbin Zhou
  2026-09-29  5:46           ` Daniel Lezcano
  0 siblings, 1 reply; 18+ messages in thread
From: Binbin Zhou @ 2026-09-29  3:06 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yinbo Zhu, Rafael J . Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Huacai Chen, Xuerui Wang, loongarch,
	devicetree, linux-pm, stable, Sashiko

Hi Daniel:

On Mon, Sep 28, 2026 at 9:23 PM Daniel Lezcano
<daniel.lezcano@oss.qualcomm.com> wrote:
>
> On 9/28/26 10:17, Binbin Zhou wrote:
> > Hi Daniel:
> >
> > Thanks for your review.
> >
> > On Mon, Sep 28, 2026 at 3:15 PM Daniel Lezcano
> > <daniel.lezcano@oss.qualcomm.com> wrote:
> >>
> >> On 9/28/26 06:44, Binbin Zhou wrote:
> >>> The registration loop in loongson2_thermal_probe() incorrectly uses
> >>> dev_err_probe() when the sensor is not present (-ENODEV). In that case,
> >>> the driver should continue to the next sensor index rather than treating
> >>> it as a fatal error.
> >>>
> >>> Fix this by correctly handling -ENODEV and only returning on other
> >>> errors. Also add a final check to ensure at least one thermal zone was
> >>> registered.
> >>>
> >>> Cc: stable@vger.kernel.org
> >>> Reported-by: Sashiko <sashiko-bot@kernel.org>
> >>> Closes: https://sashiko.dev/#/patchset/cover.1783670011.git.zhoubinbin@loongson.cn?part=2
> >>> Fixes: e7e3a7c35791 ("thermal/drivers/loongson-2: Add thermal management support")
> >>> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> >>> ---
> >>>    drivers/thermal/loongson2_thermal.c | 8 +++++---
> >>>    1 file changed, 5 insertions(+), 3 deletions(-)
> >>>
> >>> diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
> >>> index 4d40fc706a53..99b7392fc68f 100644
> >>> --- a/drivers/thermal/loongson2_thermal.c
> >>> +++ b/drivers/thermal/loongson2_thermal.c
> >>> @@ -160,16 +160,18 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
> >>>
> >>>        for (i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
> >>>                tzd = devm_thermal_of_zone_register(dev, i, data, thermal_ops);
> >>> -
> >>>                if (!IS_ERR(tzd))
> >>>                        break;
> >>>
> >>> -             if (PTR_ERR(tzd) != -ENODEV)
> >>> +             if (PTR_ERR(tzd) == -ENODEV)
> >>>                        continue;
> >>>
> >>> -             return dev_err_probe(dev, PTR_ERR(tzd), "failed to register");
> >>> +             return dev_err_probe(dev, PTR_ERR(tzd), "failed to register sensor %d\n", i);
> >>
> >>
> >>>        }
> >>>
> >>> +     if (IS_ERR(tzd))
> >>> +             return dev_err_probe(dev, -ENODEV, "No thermal sensor registered\n");
> >>> +
> >>>        ret = devm_request_threaded_irq(dev, irq, NULL, loongson2_thermal_irq_thread,
> >>>                                        IRQF_ONESHOT, "loongson2_thermal", tzd);
> >>>        if (ret < 0)
> >>
> >>
> >> The [no]error paths are confusing and prone to error, eg: 'if there is
> >> no error then check if there is an error', 'when exiting the loop
> >> without registering any sensor, then we assume tzd is -ENODEV'.
> >>
> >> Please make a less cumbersome and implicit checks here. For example
> >> count the number of sensors and then do the check against.
> >
> > I thought about it, and you're right—I made the logic here too complicated.
> > Let's use `sensor_count` as the counter. Is the following check logic clearer?
> >
> >          u32 sensor_count = 0;
> >
> >          for (unsigned int i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
> >                  tzd = devm_thermal_of_zone_register(dev, i, data,
> > data->chip_data->thermal_ops);
> >                  if (IS_ERR(tzd)) {
> >                          if (PTR_ERR(tzd) == -ENODEV)
> >                                  continue;
> >
> >                          return dev_err_probe(dev, PTR_ERR(tzd),
> >                                               "failed to register sensor %d", i);
> >                  }
> >
> >                  sensor_count++;
>
> If I'm not mistaken in the previous version, the loop exits at the first
> registered sensor
>
>         if (!IS_ERR(tzd))
>                 break;
>
> Here all the available sensors are registered

The name `sensor_count` might be a bit misleading, but here the loop
exits after the first success:

                sensor_count++;
                break;

Let’s rename it:

bool registered = false;
...
for (...) {
    tzd = devm_thermal_of_zone_register(...);
    if (IS_ERR(tzd)) {
        if (PTR_ERR(tzd) == -ENODEV)
            continue;

        return dev_err_probe(dev, PTR_ERR(tzd),
                             "failed to register sensor %d", i);
    }

    registered = true;
    break;
}

if (!registered)
    return dev_err_probe(dev, -ENODEV, "no thermal sensor registered");
>
> >                  break;
> >          }
> >
> >          if (!sensor_count)
> >                  return dev_err_probe(dev, -ENODEV, "no thermal sensor
> > registered");
> >
> >
>

-- 
Thanks.
Binbin

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

* Re: [PATCH v6 1/7] thermal/drivers/loongson2: Correct thermal sensor registration loop
  2026-09-29  3:06         ` Binbin Zhou
@ 2026-09-29  5:46           ` Daniel Lezcano
  2026-09-29  8:23             ` Binbin Zhou
  0 siblings, 1 reply; 18+ messages in thread
From: Daniel Lezcano @ 2026-09-29  5:46 UTC (permalink / raw)
  To: Binbin Zhou
  Cc: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yinbo Zhu, Rafael J . Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Huacai Chen, Xuerui Wang, loongarch,
	devicetree, linux-pm, stable, Sashiko

On 9/29/26 05:06, Binbin Zhou wrote:
> Hi Daniel:

[ ... ]

>>>> Please make a less cumbersome and implicit checks here. For example
>>>> count the number of sensors and then do the check against.
>>>
>>> I thought about it, and you're right—I made the logic here too complicated.
>>> Let's use `sensor_count` as the counter. Is the following check logic clearer?
>>>
>>>           u32 sensor_count = 0;
>>>
>>>           for (unsigned int i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
>>>                   tzd = devm_thermal_of_zone_register(dev, i, data,
>>> data->chip_data->thermal_ops);
>>>                   if (IS_ERR(tzd)) {
>>>                           if (PTR_ERR(tzd) == -ENODEV)
>>>                                   continue;
>>>
>>>                           return dev_err_probe(dev, PTR_ERR(tzd),
>>>                                                "failed to register sensor %d", i);
>>>                   }
>>>
>>>                   sensor_count++;
>>
>> If I'm not mistaken in the previous version, the loop exits at the first
>> registered sensor
>>
>>          if (!IS_ERR(tzd))
>>                  break;
>>
>> Here all the available sensors are registered
> 
> The name `sensor_count` might be a bit misleading, but here the loop
> exits after the first success:
> 
>                  sensor_count++;
>                  break;
> 
> Let’s rename it:

Can you clarify why only the first sensor found stops the thermal zones 
registration ?

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

* Re: [PATCH v6 1/7] thermal/drivers/loongson2: Correct thermal sensor registration loop
  2026-09-29  5:46           ` Daniel Lezcano
@ 2026-09-29  8:23             ` Binbin Zhou
  2026-09-29  8:43               ` Daniel Lezcano
  0 siblings, 1 reply; 18+ messages in thread
From: Binbin Zhou @ 2026-09-29  8:23 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yinbo Zhu, Rafael J . Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Huacai Chen, Xuerui Wang, loongarch,
	devicetree, linux-pm, stable, Sashiko

Hi Daniel:

On Tue, Sep 29, 2026 at 1:46 PM Daniel Lezcano
<daniel.lezcano@oss.qualcomm.com> wrote:
>
> On 9/29/26 05:06, Binbin Zhou wrote:
> > Hi Daniel:
>
> [ ... ]
>
> >>>> Please make a less cumbersome and implicit checks here. For example
> >>>> count the number of sensors and then do the check against.
> >>>
> >>> I thought about it, and you're right—I made the logic here too complicated.
> >>> Let's use `sensor_count` as the counter. Is the following check logic clearer?
> >>>
> >>>           u32 sensor_count = 0;
> >>>
> >>>           for (unsigned int i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
> >>>                   tzd = devm_thermal_of_zone_register(dev, i, data,
> >>> data->chip_data->thermal_ops);
> >>>                   if (IS_ERR(tzd)) {
> >>>                           if (PTR_ERR(tzd) == -ENODEV)
> >>>                                   continue;
> >>>
> >>>                           return dev_err_probe(dev, PTR_ERR(tzd),
> >>>                                                "failed to register sensor %d", i);
> >>>                   }
> >>>
> >>>                   sensor_count++;
> >>
> >> If I'm not mistaken in the previous version, the loop exits at the first
> >> registered sensor
> >>
> >>          if (!IS_ERR(tzd))
> >>                  break;
> >>
> >> Here all the available sensors are registered
> >
> > The name `sensor_count` might be a bit misleading, but here the loop
> > exits after the first success:
> >
> >                  sensor_count++;
> >                  break;
> >
> > Let’s rename it:
>
> Can you clarify why only the first sensor found stops the thermal zones
> registration ?

This section of code is based on the original version by zhuyinbo; I
noticed that you’ve had related discussions about it before[1][2].

I’ve reviewed all Loongson-2K CPUs that support thermal monitoring,
and they all have only one temperature sensor, with “SEL” set to 0.

As I understand it, given the current situation, the loop here doesn’t
seem necessary—physically, there is only one temperature sensor (even
though its SEL value might theoretically be non-zero).
`LOONGSON2_MAX_SENSOR_SEL_NUM` does not accurately reflect the number
of temperature sensors; it is merely an interrupt input selection, so
using it here does not seem entirely appropriate.

Therefore, I believe treating the Loongson-2K thermal driver as a
single thermal sensor better aligns with the current hardware
behavior.

Just use:
         devm_thermal_of_zone_register(dev, 0, data, thermal_ops);

[1]: https://lore.kernel.org/all/ac5b3982-a658-e05b-1b5c-3aeeda1585ed@loongson.cn/
[2]: https://lore.kernel.org/all/8ca44091-35fd-cc24-9896-0317772c5620@loongson.cn/

-- 
Thanks.
Binbin

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

* Re: [PATCH v6 1/7] thermal/drivers/loongson2: Correct thermal sensor registration loop
  2026-09-29  8:23             ` Binbin Zhou
@ 2026-09-29  8:43               ` Daniel Lezcano
  0 siblings, 0 replies; 18+ messages in thread
From: Daniel Lezcano @ 2026-09-29  8:43 UTC (permalink / raw)
  To: Binbin Zhou
  Cc: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yinbo Zhu, Rafael J . Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Huacai Chen, Xuerui Wang, loongarch,
	devicetree, linux-pm, stable, Sashiko

On 9/29/26 10:23, Binbin Zhou wrote:
> Hi Daniel:
> 
> On Tue, Sep 29, 2026 at 1:46 PM Daniel Lezcano
> <daniel.lezcano@oss.qualcomm.com> wrote:
>>
>> On 9/29/26 05:06, Binbin Zhou wrote:
>>> Hi Daniel:
>>
>> [ ... ]
>>
>>>>>> Please make a less cumbersome and implicit checks here. For example
>>>>>> count the number of sensors and then do the check against.
>>>>>
>>>>> I thought about it, and you're right—I made the logic here too complicated.
>>>>> Let's use `sensor_count` as the counter. Is the following check logic clearer?
>>>>>
>>>>>            u32 sensor_count = 0;
>>>>>
>>>>>            for (unsigned int i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
>>>>>                    tzd = devm_thermal_of_zone_register(dev, i, data,
>>>>> data->chip_data->thermal_ops);
>>>>>                    if (IS_ERR(tzd)) {
>>>>>                            if (PTR_ERR(tzd) == -ENODEV)
>>>>>                                    continue;
>>>>>
>>>>>                            return dev_err_probe(dev, PTR_ERR(tzd),
>>>>>                                                 "failed to register sensor %d", i);
>>>>>                    }
>>>>>
>>>>>                    sensor_count++;
>>>>
>>>> If I'm not mistaken in the previous version, the loop exits at the first
>>>> registered sensor
>>>>
>>>>           if (!IS_ERR(tzd))
>>>>                   break;
>>>>
>>>> Here all the available sensors are registered
>>>
>>> The name `sensor_count` might be a bit misleading, but here the loop
>>> exits after the first success:
>>>
>>>                   sensor_count++;
>>>                   break;
>>>
>>> Let’s rename it:
>>
>> Can you clarify why only the first sensor found stops the thermal zones
>> registration ?
> 
> This section of code is based on the original version by zhuyinbo; I
> noticed that you’ve had related discussions about it before[1][2].
> 
> I’ve reviewed all Loongson-2K CPUs that support thermal monitoring,
> and they all have only one temperature sensor, with “SEL” set to 0.
> 
> As I understand it, given the current situation, the loop here doesn’t
> seem necessary—physically, there is only one temperature sensor (even
> though its SEL value might theoretically be non-zero).
> `LOONGSON2_MAX_SENSOR_SEL_NUM` does not accurately reflect the number
> of temperature sensors; it is merely an interrupt input selection, so
> using it here does not seem entirely appropriate.
> 
> Therefore, I believe treating the Loongson-2K thermal driver as a
> single thermal sensor better aligns with the current hardware
> behavior.

Sure, if the Loongson-2K is only one sensor based board then no need to 
have this loop.

In the future, if there are more sensors coming with a new board, they 
can be described in platform private data



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

end of thread, other threads:[~2026-09-29  8:44 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-28  4:44 [PATCH v6 0/7] thermal: loongson2: Add support for Loongson-2K0300 SoC Binbin Zhou
2026-09-28  4:44 ` [PATCH v6 1/7] thermal/drivers/loongson2: Correct thermal sensor registration loop Binbin Zhou
2026-09-28  7:15   ` Daniel Lezcano
2026-09-28  8:17     ` Binbin Zhou
2026-09-28 13:23       ` Daniel Lezcano
2026-09-29  3:06         ` Binbin Zhou
2026-09-29  5:46           ` Daniel Lezcano
2026-09-29  8:23             ` Binbin Zhou
2026-09-29  8:43               ` Daniel Lezcano
2026-09-28  4:44 ` [PATCH v6 2/7] thermal/drivers/loongson2: Fix clamp() usage for thermal trip points Binbin Zhou
2026-09-28  4:44 ` [PATCH v6 3/7] thermal/drivers/loongson2: Fix Loongson-2K2000 trip point programming Binbin Zhou
2026-09-28  4:54   ` sashiko-bot
2026-09-28  4:44 ` [PATCH v6 4/7] thermal/drivers/loongson2: Use bitfields and per-SoC thermal ops Binbin Zhou
2026-09-28  4:52   ` sashiko-bot
2026-09-28  4:44 ` [PATCH v6 5/7] thermal/drivers/loongson2: Move per-SoC ops into chip data Binbin Zhou
2026-09-28  4:44 ` [PATCH v6 6/7] dt-bindings: thermal: loongson,ls2k-thermal: Add compatible for Loongson-2K0300 Binbin Zhou
2026-09-28  4:44 ` [PATCH v6 7/7] thermal/drivers/loongson2: Add Loongson-2K0300 support Binbin Zhou
2026-09-28  4:53   ` sashiko-bot

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