Re: [PATCH 2/2] thermal/drivers/loongson2: Add thermal driver support for Loongson-2K0300
Huacai Chen <[email protected]> Thu, 16 Jul 2026 11:37:41 +0800
| Newsgroups | dev.linux.lists.loongarch,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-pm |
|---|---|
| Message-ID | <CAAhV-H5J1mv6gEC9=GJqVV4fRovtO-=ZpzG54Yx=qLM7yX=2ew@mail.gmail.com> |
On Thu, Jul 16, 2026 at 10:31 AM Binbin Zhou <[email protected]> wrote: > > Hi: > > On Fri, Jul 10, 2026 at 10:32 PM Huacai Chen <[email protected]> wrote: > > > > Hi, Binbin, > > > > On Fri, Jul 10, 2026 at 4:25 PM Binbin Zhou <[email protected]> wrote: > > > > > > The Loongson-2K0300 SoC uses a new thermal sensor that requires reading > > > a separate CPU ID register to obtain hardware version information. This > > > version info is used as a correction factor (fix_data) in the > > > temperature calculation formula. > > > > > > Its thermal sensor requires the following hardware-specific handling: > > > - Read chip ID register (offset 0x0 and 0x4) to get the compensation > > > value (comp_val). The value is stored in either bits [31:20] of the > > > ID0 register or bits [15:0] of the ID1 register, depending on the > > > EXTERN_ID bit. > > > > > > - The compensation value is a signed 15-bit field; extract the value > > > and apply sign accordingly. > > > > > > Additionally, some early Loongson-2K0300 chips may have an old fuse that > > > yields invalid temperature readings outside the -55 to 125 range. In > > > such cases, the driver falls back to a simplified formula (raw * 569 - > > > 394700) and logs a warning, ensuring the system can still function > > > without crashing. > > > > > > Signed-off-by: Binbin Zhou <[email protected]> > > > --- > > > drivers/thermal/loongson2_thermal.c | 83 ++++++++++++++++++++++++++--- > > > 1 file changed, 77 insertions(+), 6 deletions(-) > > > > > > diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c > > > index ea4dd2fb1f47..a7eb87070aa9 100644 > > > --- a/drivers/thermal/loongson2_thermal.c > > > +++ b/drivers/thermal/loongson2_thermal.c > > > @@ -2,9 +2,11 @@ > > > /* > > > * Author: zhanghongchen <[email protected]> > > > * Yinbo Zhu <[email protected]> > > > - * Copyright (C) 2022-2023 Loongson Technology Corporation Limited > > > + * Binbin Zhou <[email protected]> > > > + * Copyright (C) 2022-2026 Loongson Technology Corporation Limited > > > */ > > > > > > +#include <linux/bitfield.h> > > > #include <linux/interrupt.h> > > > #include <linux/io.h> > > > #include <linux/minmax.h> > > > @@ -23,27 +25,44 @@ > > > #define LOONGSON2_THSENS_CTRL_LOW_REG 0x8 > > > #define LOONGSON2_THSENS_STATUS_REG 0x10 > > > #define LOONGSON2_THSENS_OUT_REG 0x14 > > > +#define LOONGSON2_THSENS_CFG_REG 0x18 > > > > > > #define LOONGSON2_THSENS_INT_LO BIT(0) > > > #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 LS2K0300_THSENS_OUT_MASK GENMASK(10, 0) > > The naming is a little strange, maybe use LOONGSON2_THSENS_OUT_8B_MASK > > and LOONGSON2_THSENS_OUT_10B_MASK? > > > > > + > > > +#define LS2K0300_CHIP_ID1 0x4 > > Also define LS2K0300_CHIP_ID0 here? > > > > > +#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_LOWEST_VALID_TEMP (-55000) > > > +#define LS2K0300_HIGHEST_VALID_TEMP (125000) > > > > > > /* > > > * This flag is used to indicate the temperature reading > > > * method of the Loongson-2K2000 > > > */ > > > #define LS2K2000_THSENS_OUT_FLAG BIT(0) > > > +#define LS2K0300_CHIP_ID_FLAG BIT(1) > > > > > > struct loongson2_thermal_chip_data { > > > unsigned int thermal_sensor_sel; > > > unsigned int flags; > > > + const struct thermal_zone_device_ops *thermal_ops; > > > }; > > > > > > struct loongson2_thermal_data { > > > + struct device *dev; > > > void __iomem *ctrl_reg; > > > void __iomem *temp_reg; > > > + void __iomem *id_reg; > > > const struct loongson2_thermal_chip_data *chip_data; > > > }; > > > > > > @@ -71,6 +90,38 @@ static int loongson2_thermal_set(struct loongson2_thermal_data *data, > > > return 0; > > > } > > > > > > +static int loongson2_2k0300_get_temp(struct thermal_zone_device *tz, int *temp) > > > +{ > > > + struct loongson2_thermal_data *tdata = thermal_zone_device_priv(tz); > > > + int calib_data, calib_offset, temp_mc, raw_adc; > > > + u32 chip_id0, chip_id1; > > > + > > > + raw_adc = FIELD_GET(LS2K0300_THSENS_OUT_MASK, > > > + readl(tdata->ctrl_reg + LOONGSON2_THSENS_OUT_REG)); > > > + chip_id0 = readl(tdata->id_reg); > > > + chip_id1 = readl(tdata->id_reg + LS2K0300_CHIP_ID1); > > > + > > > + if (chip_id0 & LS2K0300_EXTERN_ID) > > > + calib_data = FIELD_GET(LS2K0300_ID1_VAL_MASK, chip_id1); > > > + else > > > + calib_data = FIELD_GET(LS2K0300_ID0_VAL_MASK, chip_id0); > > > + > > > + calib_offset = FIELD_GET(LS2K0300_COMP_VAL_MASK, calib_data); > > > + if (calib_data & LS2K0300_COMP_SIGN_BIT) > > > + calib_offset = -calib_offset; > > > + > > > + temp_mc = (raw_adc + calib_offset) * 570 - 394700; > > > + > > > + /* For old fuse which can not read right thermal data */ > > > + if (temp_mc < LS2K0300_LOWEST_VALID_TEMP || temp_mc > LS2K0300_HIGHEST_VALID_TEMP) { > > Is there a better way to detect the old fuse? Because I think temp_mc > > between LS2K0300_LOWEST_VALID_TEMP and LS2K0300_HIGHEST_VALID_TEMP is > > also not valid for the old fuse. > > Try communicating in person; there’s no better way. The current > judgment criteria can only accommodate the standard temperature range. I suggest #define LS2K0300_OLD_FUSE_FLAG BIT(2) Then when you get a temp out of normal range, set this flag. And use "raw_adc * 569 - 394700" directly for the future if this flag is set. Huacai > > Also, the `old fuse` refers to the very first batch of chips, so I > don’t think the scope of the issue will be very broad. > > > > Huacai > > > > > + dev_warn_once(tdata->dev, "It's an old fuse, thermal %d is not right\n", temp_mc); > > > + temp_mc = raw_adc * 569 - 394700; > > > + } > > > + *temp = temp_mc; > > > + > > > + return 0; > > > +} > > > + > > > static int loongson2_2k1000_get_temp(struct thermal_zone_device *tz, int *temp) > > > { > > > int val; > > > @@ -112,6 +163,11 @@ static int loongson2_thermal_set_trips(struct thermal_zone_device *tz, int low, > > > return loongson2_thermal_set(data, low/MILLI, high/MILLI, true); > > > } > > > > > > +static const struct thermal_zone_device_ops loongson2_2k0300_of_thermal_ops = { > > > + .get_temp = loongson2_2k0300_get_temp, > > > + .set_trips = loongson2_thermal_set_trips, > > > +}; > > > + > > > static const struct thermal_zone_device_ops loongson2_2k1000_of_thermal_ops = { > > > .get_temp = loongson2_2k1000_get_temp, > > > .set_trips = loongson2_thermal_set_trips, > > > @@ -124,7 +180,6 @@ static const struct thermal_zone_device_ops loongson2_2k2000_of_thermal_ops = { > > > > > > 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 thermal_zone_device *tzd; > > > @@ -134,6 +189,7 @@ static int loongson2_thermal_probe(struct platform_device *pdev) > > > if (!data) > > > return -ENOMEM; > > > > > > + data->dev = dev; > > > data->chip_data = device_get_match_data(dev); > > > > > > data->ctrl_reg = devm_platform_ioremap_resource(pdev, 0); > > > @@ -145,10 +201,13 @@ static int loongson2_thermal_probe(struct platform_device *pdev) > > > data->temp_reg = devm_platform_ioremap_resource(pdev, 1); > > > if (IS_ERR(data->temp_reg)) > > > return PTR_ERR(data->temp_reg); > > > + } > > > > > > - thermal_ops = &loongson2_2k2000_of_thermal_ops; > > > - } else { > > > - thermal_ops = &loongson2_2k1000_of_thermal_ops; > > > + /* The chip id register is needed for Loongson-2K0300 */ > > > + if (data->chip_data->flags & LS2K0300_CHIP_ID_FLAG) { > > > + data->id_reg = devm_platform_ioremap_resource(pdev, 1); > > > + if (IS_ERR(data->id_reg)) > > > + return PTR_ERR(data->id_reg); > > > } > > > > > > irq = platform_get_irq(pdev, 0); > > > @@ -160,7 +219,7 @@ static int loongson2_thermal_probe(struct platform_device *pdev) > > > loongson2_thermal_set(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; > > > @@ -181,17 +240,29 @@ static int loongson2_thermal_probe(struct platform_device *pdev) > > > return 0; > > > } > > > > > > +static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k0300_data = { > > > + .thermal_sensor_sel = 0, > > > + .flags = LS2K0300_CHIP_ID_FLAG, > > > + .thermal_ops = &loongson2_2k0300_of_thermal_ops, > > > +}; > > > + > > > static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k1000_data = { > > > .thermal_sensor_sel = 0, > > > .flags = 0, > > > + .thermal_ops = &loongson2_2k1000_of_thermal_ops, > > > }; > > > > > > static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k2000_data = { > > > .thermal_sensor_sel = 0, > > > .flags = LS2K2000_THSENS_OUT_FLAG, > > > + .thermal_ops = &loongson2_2k2000_of_thermal_ops, > > > }; > > > > > > static const struct of_device_id of_loongson2_thermal_match[] = { > > > + { > > > + .compatible = "loongson,ls2k0300-thermal", > > > + .data = &loongson2_thermal_ls2k0300_data, > > > + }, > > > { > > > .compatible = "loongson,ls2k1000-thermal", > > > .data = &loongson2_thermal_ls2k1000_data, > > > -- > > > 2.52.0 > > > > > -- > Thanks. > Binbin