* [PATCH v7 0/8] thermal/drivers/loongson2: Fix trip programming and add Loongson-2K0300 support
@ 2026-10-09 2:57 Binbin Zhou
2026-10-09 2:57 ` [PATCH v7 1/8] thermal/drivers/loongson2: Fix negative temperature conversion in set_trips Binbin Zhou
` (7 more replies)
0 siblings, 8 replies; 9+ messages in thread
From: Binbin Zhou @ 2026-10-09 2:57 UTC (permalink / raw)
To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Yinbo Zhu, zhanghongchen, Rafael J . Wysocki,
Daniel Lezcano, Zhang Rui, Lukasz Luba
Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-pm,
Binbin Zhou
This series fixes trip-point handling in the Loongson-2 thermal driver,
refactors the driver around per-SoC chip data, and adds support for the
Loongson-2K0300 thermal sensor.
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
(-40 to 125), the driver falls back to a simpler calculation and prints
a warning.
The first three patches are fixes:
- Patch 1 fixes signed division when converting negative milli-degree
trip points to degrees Celsius.
- Patch 2 fixes the arguments to clamp() so trip points are limited to
the supported sensor range.
- Patch 3 fixes Loongson-2K2000 trip-point programming by adding a
per-SoC register callback and handling the split HI_GATE register.
Patches 4-6 are cleanups and preparation work:
- Patch 4 removes the unnecessary multi-sensor registration loop.
- Patch 5 replaces open-coded bit operations with bitfield helpers and
splits the per-SoC thermal ops.
- Patch 6 moves the per-SoC ops and hardware initialization into chip
data.
Patch 7 adds the Loongson-2K0300 DT binding, and patch 8 adds driver
support for the Loongson-2K0300 thermal sensor.
The patchset have been validated on the Loongson-2K series development
boards.
Thanks.
Binbin
=========
V7:
Patch (1/8):
- New patch;
- Fix negative temperature conversion in set_trips;
Patch (3/8):
- Use writew()/writeb() in loongson2_2k2000_set_trips_regs;
Patch (4/8):
- Rewite the thermal sensor registration loop to clearer;
Patch (5/8):
- Fix `loognson2_thermal_set_trips` typo;
Patch (8/8):
- Read CHIP_ID1 only when EXTERN_ID is set;
- Simplify the Internal ID calculation;
- Add necessary comments.
Link to V6:
https://lore.kernel.org/all/cover.1790561983.git.zhoubinbin@loongson.cn/
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 (8):
thermal/drivers/loongson2: Fix negative temperature conversion in
set_trips
thermal/drivers/loongson2: Fix clamp() usage for thermal trip points
thermal/drivers/loongson2: Fix Loongson-2K2000 trip point programming
thermal/drivers/loongson2: Simplify thermal sensor registration
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 | 359 ++++++++++++++----
2 files changed, 332 insertions(+), 100 deletions(-)
base-commit: ec0d89150a9381d591344a9f6f5428655c227a7f
--
2.52.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v7 1/8] thermal/drivers/loongson2: Fix negative temperature conversion in set_trips
2026-10-09 2:57 [PATCH v7 0/8] thermal/drivers/loongson2: Fix trip programming and add Loongson-2K0300 support Binbin Zhou
@ 2026-10-09 2:57 ` Binbin Zhou
2026-10-09 2:57 ` [PATCH v7 2/8] thermal/drivers/loongson2: Fix clamp() usage for thermal trip points Binbin Zhou
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Binbin Zhou @ 2026-10-09 2:57 UTC (permalink / raw)
To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Yinbo Zhu, zhanghongchen, Rafael J . Wysocki,
Daniel Lezcano, Zhang Rui, Lukasz Luba
Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-pm,
Binbin Zhou, stable
The `MILLI` is defined as an unsigned long constant. When the
temperatures passed to loongson2_thermal_set_trips() are negative, the
expression `low / MILLI` triggers implicit conversion of the signed
operand to unsigned long, and the division is then carried out in
unsigned arithmetic.
For example, with a low trip of -40000 (milli-degree Celsius):
-40000 / 1000UL = 18446744073709511 /* unsigned long */
After truncation to the 32-bit register field this becomes
1271310379, instead of the expected 60 (i.e. -40 + HECTO). Writing such
bogus value into the trip register leaves most of the upper bits set,
which may hit status/trigger bits and cause spurious thermal interrupts.
Fix this by explicitly casting MILLI to int so that the division is
performed in signed arithmetic.
Cc: stable@vger.kernel.org
Fixes: e7e3a7c35791 ("thermal/drivers/loongson-2: Add thermal management support")
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
drivers/thermal/loongson2_thermal.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
index 4d40fc706a53..f5ff0cac1442 100644
--- a/drivers/thermal/loongson2_thermal.c
+++ b/drivers/thermal/loongson2_thermal.c
@@ -108,7 +108,7 @@ static int loongson2_thermal_set_trips(struct thermal_zone_device *tz, int low,
{
struct loongson2_thermal_data *data = thermal_zone_device_priv(tz);
- return loongson2_thermal_set(data, low/MILLI, high/MILLI, true);
+ return loongson2_thermal_set(data, low / (int)MILLI, high / (int)MILLI, true);
}
static const struct thermal_zone_device_ops loongson2_2k1000_of_thermal_ops = {
--
2.52.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v7 2/8] thermal/drivers/loongson2: Fix clamp() usage for thermal trip points
2026-10-09 2:57 [PATCH v7 0/8] thermal/drivers/loongson2: Fix trip programming and add Loongson-2K0300 support Binbin Zhou
2026-10-09 2:57 ` [PATCH v7 1/8] thermal/drivers/loongson2: Fix negative temperature conversion in set_trips Binbin Zhou
@ 2026-10-09 2:57 ` Binbin Zhou
2026-10-09 2:58 ` [PATCH v7 3/8] thermal/drivers/loongson2: Fix Loongson-2K2000 trip point programming Binbin Zhou
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Binbin Zhou @ 2026-10-09 2:57 UTC (permalink / raw)
To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Yinbo Zhu, zhanghongchen, 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 f5ff0cac1442..46785bafab16 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] 9+ messages in thread
* [PATCH v7 3/8] thermal/drivers/loongson2: Fix Loongson-2K2000 trip point programming
2026-10-09 2:57 [PATCH v7 0/8] thermal/drivers/loongson2: Fix trip programming and add Loongson-2K0300 support Binbin Zhou
2026-10-09 2:57 ` [PATCH v7 1/8] thermal/drivers/loongson2: Fix negative temperature conversion in set_trips Binbin Zhou
2026-10-09 2:57 ` [PATCH v7 2/8] thermal/drivers/loongson2: Fix clamp() usage for thermal trip points Binbin Zhou
@ 2026-10-09 2:58 ` Binbin Zhou
2026-10-09 2:58 ` [PATCH v7 4/8] thermal/drivers/loongson2: Simplify thermal sensor registration Binbin Zhou
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Binbin Zhou @ 2026-10-09 2:58 UTC (permalink / raw)
To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Yinbo Zhu, zhanghongchen, Rafael J . Wysocki,
Daniel Lezcano, Zhang Rui, Lukasz Luba
Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-pm,
Binbin Zhou, stable
The Loongson-2K2000 splits its 16-bit gate value across the control
register (low byte) and a dedicated HI_GATE register (high byte), and
derives the gate value from the target temperature with a different
formula than the Loongson-2K1000.
Add a per-SoC set_trips_regs callback and implement the Loongson-2K2000
variant alongside the existing Loongson-2K1000 one.
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 | 78 +++++++++++++++++++++--------
1 file changed, 56 insertions(+), 22 deletions(-)
diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
index 46785bafab16..2a31a1f8673e 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;
+ low = clamp(low, -40000, 125000);
+ high = clamp(high, -40000, 125000);
- reg_ctrl = ctrl_data + HECTO;
- reg_ctrl |= enable ? 0x100 : 0;
- writew(reg_ctrl, data->ctrl_reg + ctrl_reg + reg_off);
+ /* Set low temperature threshold */
+ data->chip_data->set_trips_regs(data, low / (int)MILLI, true, enable);
+
+ /* Set high temperature threshold */
+ data->chip_data->set_trips_regs(data, high / (int)MILLI, 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);
+ loongson2_thermal_set_trip_temp(data, low, high, 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,27 @@ 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)
+{
+ unsigned int sel = data->chip_data->thermal_sensor_sel;
+ int hi_reg, ctrl_reg, val, gate;
+
+ gate = (temp + 311) * 0x4000 / 820;
+
+ /* Low byte of the gate value goes to the control register */
+ ctrl_reg = low ? LOONGSON2_THSENS_CTRL_LOW_REG : LOONGSON2_THSENS_CTRL_HI_REG;
+ val = gate & 0xff;
+ val |= enable ? 0x100 : 0;
+ val |= sel << 10;
+ writew(val, data->ctrl_reg + ctrl_reg + sel * 2);
+
+ /* High byte of the gate value goes to the low byte of the HI_GATE register */
+ hi_reg = low ? LS2K2000_THSENS_HI_GATE_LOW_REG : LS2K2000_THSENS_HI_GATE_HI_REG;
+ val = (gate >> 8) & 0xff;
+ writeb(val, data->ctrl_reg + hi_reg + sel);
+}
+
static int loongson2_2k2000_get_temp(struct thermal_zone_device *tz, int *temp)
{
int val;
@@ -104,13 +143,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 / (int)MILLI, high / (int)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 +188,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);
@@ -183,11 +215,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] 9+ messages in thread
* [PATCH v7 4/8] thermal/drivers/loongson2: Simplify thermal sensor registration
2026-10-09 2:57 [PATCH v7 0/8] thermal/drivers/loongson2: Fix trip programming and add Loongson-2K0300 support Binbin Zhou
` (2 preceding siblings ...)
2026-10-09 2:58 ` [PATCH v7 3/8] thermal/drivers/loongson2: Fix Loongson-2K2000 trip point programming Binbin Zhou
@ 2026-10-09 2:58 ` Binbin Zhou
2026-10-09 2:58 ` [PATCH v7 5/8] thermal/drivers/loongson2: Use bitfields and per-SoC thermal ops Binbin Zhou
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Binbin Zhou @ 2026-10-09 2:58 UTC (permalink / raw)
To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Yinbo Zhu, zhanghongchen, Rafael J . Wysocki,
Daniel Lezcano, Zhang Rui, Lukasz Luba
Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-pm,
Binbin Zhou
The Loongson-2K SoCs expose a single on-chip thermal sensor, so the
loop over LOONGSON2_MAX_SENSOR_SEL_NUM only ever registers one thermal
zone. The additional iterations never succeed and the surrounding
error handling ends up relying on the value of tzd after the loop,
which is fragile and hard to follow.
Register sensor index 0 directly and fail the probe if that single
registration fails.
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
drivers/thermal/loongson2_thermal.c | 18 ++++--------------
1 file changed, 4 insertions(+), 14 deletions(-)
diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
index 2a31a1f8673e..817b0dab3412 100644
--- a/drivers/thermal/loongson2_thermal.c
+++ b/drivers/thermal/loongson2_thermal.c
@@ -16,8 +16,6 @@
#include "thermal_hwmon.h"
-#define LOONGSON2_MAX_SENSOR_SEL_NUM 3
-
#define LOONGSON2_THSENS_CTRL_HI_REG 0x0
#define LOONGSON2_THSENS_CTRL_LOW_REG 0x8
#define LOONGSON2_THSENS_STATUS_REG 0x10
@@ -159,7 +157,7 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct loongson2_thermal_data *data;
struct thermal_zone_device *tzd;
- int ret, irq, i;
+ int ret, irq;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
@@ -190,17 +188,9 @@ 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);
-
- if (!IS_ERR(tzd))
- break;
-
- if (PTR_ERR(tzd) != -ENODEV)
- continue;
-
- return dev_err_probe(dev, PTR_ERR(tzd), "failed to register");
- }
+ tzd = devm_thermal_of_zone_register(dev, 0, data, thermal_ops);
+ if (IS_ERR(tzd))
+ return dev_err_probe(dev, PTR_ERR(tzd), "failed to register sensor");
ret = devm_request_threaded_irq(dev, irq, NULL, loongson2_thermal_irq_thread,
IRQF_ONESHOT, "loongson2_thermal", tzd);
--
2.52.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v7 5/8] thermal/drivers/loongson2: Use bitfields and per-SoC thermal ops
2026-10-09 2:57 [PATCH v7 0/8] thermal/drivers/loongson2: Fix trip programming and add Loongson-2K0300 support Binbin Zhou
` (3 preceding siblings ...)
2026-10-09 2:58 ` [PATCH v7 4/8] thermal/drivers/loongson2: Simplify thermal sensor registration Binbin Zhou
@ 2026-10-09 2:58 ` Binbin Zhou
2026-10-09 2:58 ` [PATCH v7 6/8] thermal/drivers/loongson2: Move per-SoC ops into chip data Binbin Zhou
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Binbin Zhou @ 2026-10-09 2:58 UTC (permalink / raw)
To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Yinbo Zhu, zhanghongchen, 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 | 125 ++++++++++++++++------------
1 file changed, 71 insertions(+), 54 deletions(-)
diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
index 817b0dab3412..02f7545eaac1 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>
@@ -25,12 +26,25 @@
#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_GATE_MASK GENMASK(7, 0)
+#define LOONGSON2_THSENS_CTRL_INTEN BIT(8)
+#define LOONGSON2_THSENS_CTRL_SEL_MASK GENMASK(11, 10)
+
+#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_TEMP_MUL (820)
+#define LS2K2000_TEMP_DIV (0x4000)
+#define LS2K2000_TEMP_OFFSET (311)
+
/*
* This flag is used to indicate the temperature reading
* method of the Loongson-2K2000
@@ -55,8 +69,8 @@ struct loongson2_thermal_data {
static void loongson2_thermal_set_trip_temp(struct loongson2_thermal_data *data,
int low, int high, bool enable)
{
- 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);
/* Set low temperature threshold */
data->chip_data->set_trips_regs(data, low / (int)MILLI, true, enable);
@@ -74,60 +88,85 @@ static int loongson2_thermal_set_trips(struct thermal_zone_device *tz, int low,
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;
+ u32 sel = data->chip_data->thermal_sensor_sel;
+ int ctrl_reg;
+ u16 val;
- reg_ctrl = temp + HECTO;
- reg_ctrl |= enable ? 0x100 : 0;
- writew(reg_ctrl, data->ctrl_reg + ctrl_reg + reg_off);
+ ctrl_reg = low ? LOONGSON2_THSENS_CTRL_LOW_REG : LOONGSON2_THSENS_CTRL_HI_REG;
+ val = FIELD_PREP(LOONGSON2_THSENS_CTRL_GATE_MASK, temp + HECTO) |
+ FIELD_PREP(LOONGSON2_THSENS_CTRL_INTEN, enable) |
+ FIELD_PREP(LOONGSON2_THSENS_CTRL_SEL_MASK, sel);
+ writew(val, data->ctrl_reg + ctrl_reg + sel * 2);
}
-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)
{
unsigned int sel = data->chip_data->thermal_sensor_sel;
int hi_reg, ctrl_reg, val, gate;
- gate = (temp + 311) * 0x4000 / 820;
+ gate = (temp + LS2K2000_TEMP_OFFSET) * LS2K2000_TEMP_DIV / LS2K2000_TEMP_MUL;
/* Low byte of the gate value goes to the control register */
ctrl_reg = low ? LOONGSON2_THSENS_CTRL_LOW_REG : LOONGSON2_THSENS_CTRL_HI_REG;
- val = gate & 0xff;
- val |= enable ? 0x100 : 0;
- val |= sel << 10;
+ val = FIELD_PREP(LOONGSON2_THSENS_CTRL_GATE_MASK, gate) |
+ FIELD_PREP(LOONGSON2_THSENS_CTRL_INTEN, enable) |
+ FIELD_PREP(LOONGSON2_THSENS_CTRL_SEL_MASK, sel);
writew(val, data->ctrl_reg + ctrl_reg + sel * 2);
/* High byte of the gate value goes to the low byte of the HI_GATE register */
hi_reg = low ? LS2K2000_THSENS_HI_GATE_LOW_REG : LS2K2000_THSENS_HI_GATE_HI_REG;
- val = (gate >> 8) & 0xff;
+ val = FIELD_GET(GENMASK(15, 8), gate);
writeb(val, data->ctrl_reg + hi_reg + sel);
}
-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 = loongson2_thermal_set_trips,
+};
+
+static const struct thermal_zone_device_ops ls2k2000_thermal_zone_ops = {
+ .get_temp = ls2k2000_thermal_get_temp,
+ .set_trips = loongson2_thermal_set_trips,
+};
- val = readl(data->temp_reg);
- *temp = ((val & 0xffff) * 820 / 0x4000 - 311) * KILO;
+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,
+};
- return 0;
-}
+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)
{
@@ -141,16 +180,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;
@@ -175,9 +204,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);
@@ -202,26 +231,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] 9+ messages in thread
* [PATCH v7 6/8] thermal/drivers/loongson2: Move per-SoC ops into chip data
2026-10-09 2:57 [PATCH v7 0/8] thermal/drivers/loongson2: Fix trip programming and add Loongson-2K0300 support Binbin Zhou
` (4 preceding siblings ...)
2026-10-09 2:58 ` [PATCH v7 5/8] thermal/drivers/loongson2: Use bitfields and per-SoC thermal ops Binbin Zhou
@ 2026-10-09 2:58 ` Binbin Zhou
2026-10-09 2:58 ` [PATCH v7 7/8] dt-bindings: thermal: loongson,ls2k-thermal: Add compatible for Loongson-2K0300 Binbin Zhou
2026-10-09 2:58 ` [PATCH v7 8/8] thermal/drivers/loongson2: Add Loongson-2K0300 support Binbin Zhou
7 siblings, 0 replies; 9+ messages in thread
From: Binbin Zhou @ 2026-10-09 2:58 UTC (permalink / raw)
To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Yinbo Zhu, zhanghongchen, 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 hw_init() callback. This
makes adding new SoCs with different register layouts easier.
No functional change intended.
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
drivers/thermal/loongson2_thermal.c | 66 +++++++++++++++++------------
1 file changed, 38 insertions(+), 28 deletions(-)
diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
index 02f7545eaac1..dbfb03aa0833 100644
--- a/drivers/thermal/loongson2_thermal.c
+++ b/drivers/thermal/loongson2_thermal.c
@@ -45,24 +45,19 @@
#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 (*hw_init)(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;
};
@@ -114,12 +109,21 @@ static void ls2k1000_thermal_set_trips_regs(struct loongson2_thermal_data *data,
writew(val, data->ctrl_reg + ctrl_reg + sel * 2);
}
+static int ls2k1000_thermal_hw_init(struct platform_device *pdev,
+ struct loongson2_thermal_data *data)
+{
+ writeb(LOONGSON2_THSENS_INT_EN, data->ctrl_reg + LOONGSON2_THSENS_STATUS_REG);
+ loongson2_thermal_set_trip_temp(data, 0, 0, false);
+
+ return 0;
+}
+
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;
@@ -146,6 +150,20 @@ static void ls2k2000_thermal_set_trips_regs(struct loongson2_thermal_data *data,
writeb(val, data->ctrl_reg + hi_reg + sel);
}
+static int ls2k2000_thermal_hw_init(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);
+
+ writeb(LOONGSON2_THSENS_INT_EN, data->ctrl_reg + LOONGSON2_THSENS_STATUS_REG);
+ loongson2_thermal_set_trip_temp(data, 0, 0, false);
+
+ return 0;
+}
+
static const struct thermal_zone_device_ops ls2k1000_thermal_zone_ops = {
.get_temp = ls2k1000_thermal_get_temp,
.set_trips = loongson2_thermal_set_trips,
@@ -158,14 +176,16 @@ 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,
+ .hw_init = ls2k1000_thermal_hw_init,
};
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,
+ .hw_init = ls2k2000_thermal_hw_init,
};
static irqreturn_t loongson2_thermal_irq_thread(int irq, void *dev)
@@ -182,9 +202,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;
@@ -193,31 +212,22 @@ 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;
- }
-
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
- writeb(LOONGSON2_THSENS_INT_EN, data->ctrl_reg + LOONGSON2_THSENS_STATUS_REG);
-
- loongson2_thermal_set_trip_temp(data, 0, 0, false);
+ ret = data->chip_data->hw_init(pdev, data);
+ if (ret)
+ return ret;
- tzd = devm_thermal_of_zone_register(dev, 0, data, thermal_ops);
+ tzd = devm_thermal_of_zone_register(dev, 0, data, data->chip_data->thermal_ops);
if (IS_ERR(tzd))
return dev_err_probe(dev, PTR_ERR(tzd), "failed to register sensor");
--
2.52.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v7 7/8] dt-bindings: thermal: loongson,ls2k-thermal: Add compatible for Loongson-2K0300
2026-10-09 2:57 [PATCH v7 0/8] thermal/drivers/loongson2: Fix trip programming and add Loongson-2K0300 support Binbin Zhou
` (5 preceding siblings ...)
2026-10-09 2:58 ` [PATCH v7 6/8] thermal/drivers/loongson2: Move per-SoC ops into chip data Binbin Zhou
@ 2026-10-09 2:58 ` Binbin Zhou
2026-10-09 2:58 ` [PATCH v7 8/8] thermal/drivers/loongson2: Add Loongson-2K0300 support Binbin Zhou
7 siblings, 0 replies; 9+ messages in thread
From: Binbin Zhou @ 2026-10-09 2:58 UTC (permalink / raw)
To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Yinbo Zhu, zhanghongchen, 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] 9+ messages in thread
* [PATCH v7 8/8] thermal/drivers/loongson2: Add Loongson-2K0300 support
2026-10-09 2:57 [PATCH v7 0/8] thermal/drivers/loongson2: Fix trip programming and add Loongson-2K0300 support Binbin Zhou
` (6 preceding siblings ...)
2026-10-09 2:58 ` [PATCH v7 7/8] dt-bindings: thermal: loongson,ls2k-thermal: Add compatible for Loongson-2K0300 Binbin Zhou
@ 2026-10-09 2:58 ` Binbin Zhou
7 siblings, 0 replies; 9+ messages in thread
From: Binbin Zhou @ 2026-10-09 2:58 UTC (permalink / raw)
To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Yinbo Zhu, zhanghongchen, 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
through the `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.
Trip-point programming for Loongson-2K0300 will be added later.
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
drivers/thermal/loongson2_thermal.c | 144 ++++++++++++++++++++++++++++
1 file changed, 144 insertions(+)
diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
index dbfb03aa0833..1db61509dbf3 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>
@@ -32,11 +34,41 @@
#define LOONGSON2_THSENS_CTRL_SEL_MASK GENMASK(11, 10)
#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 CHIP ID syscon registers */
+#define LS2K0300_SYSCON_CHIP_ID0 0x10
+#define LS2K0300_SYSCON_CHIP_ID1 0x14
+
+/* Loongson-2K0300 thermal sensor registers */
+#define LS2K0300_THSENS_CFG_REG 0x18
+
+#define LS2K0300_THSENS_INIT_MASK 0xff03
+
+#define LS2K0300_EXTERN_ID BIT(4)
+/* Internal ID: 12-bit unsigned compensation value */
+#define LS2K0300_ID0_VAL_MASK GENMASK(31, 20)
+/* External ID: 16-bit signed compensation value */
+#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) /* calibrated */
+#define LS2K0300_TEMP_SLOPE_OLD (569) /* old fuse */
+#define LS2K0300_TEMP_OFFSET (394700)
+
+/*
+ * Set when the on-chip calibration data is unusable (early LS2K0300
+ * fuse revision). The driver then uses LS2K0300_TEMP_SLOPE_OLD with
+ * calib_offset = 0 instead of the calibrated formula.
+ */
+#define LS2K0300_UNCALIBRATED_FLAG BIT(0)
+
/* Loongson-2K2000-Specific Registers */
#define LS2K2000_THSENS_HI_GATE_HI_REG 0x18
#define LS2K2000_THSENS_HI_GATE_LOW_REG 0x1c
@@ -58,6 +90,9 @@ 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;
};
@@ -83,6 +118,101 @@ static int loongson2_thermal_set_trips(struct thermal_zone_device *tz, int low,
return 0;
}
+static int ls2k0300_thermal_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_UNCALIBRATED_FLAG)
+ slope = LS2K0300_TEMP_SLOPE_OLD;
+
+ *temp = ls2k0300_thermal_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_thermal_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 mC out of range [%d, %d], using old fuse formula",
+ temp, LOONGSON2_LOWEST_VALID_TEMP, LOONGSON2_HIGHEST_VALID_TEMP);
+ data->flags = LS2K0300_UNCALIBRATED_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_SYSCON_CHIP_ID0, &chip_id0);
+ if (ret)
+ return ret;
+
+ if (chip_id0 & LS2K0300_EXTERN_ID) {
+ ret = regmap_read(data->regmap_cfg, LS2K0300_SYSCON_CHIP_ID1, &chip_id1);
+ if (ret)
+ return ret;
+
+ /*
+ * External ID: the signed compensation value is stored in
+ * CHIP_ID1, with bit 15 as the sign and bits 14:0 as the
+ * magnitude.
+ */
+ 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 {
+ /*
+ * Internal ID: the compensation value is unsigned and is
+ * stored in CHIP_ID0[31:20].
+ */
+ calib_offset = FIELD_GET(LS2K0300_ID0_VAL_MASK, chip_id0);
+ }
+
+ data->calib_offset = calib_offset;
+ return 0;
+}
+
+static int ls2k0300_thermal_hw_init(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");
+
+ 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);
@@ -164,6 +294,10 @@ static int ls2k2000_thermal_hw_init(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 = loongson2_thermal_set_trips,
@@ -174,6 +308,12 @@ static const struct thermal_zone_device_ops ls2k2000_thermal_zone_ops = {
.set_trips = loongson2_thermal_set_trips,
};
+static const struct loongson2_thermal_chip_data ls2k0300_thermal_chip_data = {
+ .thermal_sensor_sel = 0,
+ .thermal_ops = &ls2k0300_thermal_zone_ops,
+ .hw_init = ls2k0300_thermal_hw_init,
+};
+
static const struct loongson2_thermal_chip_data ls2k1000_thermal_chip_data = {
.thermal_sensor_sel = 0,
.thermal_ops = &ls2k1000_thermal_zone_ops,
@@ -242,6 +382,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] 9+ messages in thread
end of thread, other threads:[~2026-10-09 2:59 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-09 2:57 [PATCH v7 0/8] thermal/drivers/loongson2: Fix trip programming and add Loongson-2K0300 support Binbin Zhou
2026-10-09 2:57 ` [PATCH v7 1/8] thermal/drivers/loongson2: Fix negative temperature conversion in set_trips Binbin Zhou
2026-10-09 2:57 ` [PATCH v7 2/8] thermal/drivers/loongson2: Fix clamp() usage for thermal trip points Binbin Zhou
2026-10-09 2:58 ` [PATCH v7 3/8] thermal/drivers/loongson2: Fix Loongson-2K2000 trip point programming Binbin Zhou
2026-10-09 2:58 ` [PATCH v7 4/8] thermal/drivers/loongson2: Simplify thermal sensor registration Binbin Zhou
2026-10-09 2:58 ` [PATCH v7 5/8] thermal/drivers/loongson2: Use bitfields and per-SoC thermal ops Binbin Zhou
2026-10-09 2:58 ` [PATCH v7 6/8] thermal/drivers/loongson2: Move per-SoC ops into chip data Binbin Zhou
2026-10-09 2:58 ` [PATCH v7 7/8] dt-bindings: thermal: loongson,ls2k-thermal: Add compatible for Loongson-2K0300 Binbin Zhou
2026-10-09 2:58 ` [PATCH v7 8/8] thermal/drivers/loongson2: Add Loongson-2K0300 support Binbin Zhou
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox