airoha: replace thermal patch with upstream version
authorChristian Marangi <[email protected]>
Sat, 5 Jul 2025 10:44:03 +0000 (12:44 +0200)
committerChristian Marangi <[email protected]>
Tue, 2 Sep 2025 22:58:47 +0000 (00:58 +0200)
Replace thermal patch with upstream version. The thermal maintainer
reported that the sysfs entry are considered deprecated and that slope
and offset should be handled internally to the driver.

Link: https://github.com/openwrt/openwrt/pull/19816
Signed-off-by: Christian Marangi <[email protected]>
target/linux/airoha/patches-6.6/049-01-v6.16-thermal-drivers-Add-support-for-Airoha-EN7581-therma.patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/049-02-v6.16-thermal-drivers-airoha-Fix-spelling-mistake.patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/101-01-thermal-of-Add-devm_thermal_of_zone_register_with_pa.patch [deleted file]
target/linux/airoha/patches-6.6/101-02-thermal-Add-support-for-Airoha-EN7581-thermal-sensor.patch [deleted file]

diff --git a/target/linux/airoha/patches-6.6/049-01-v6.16-thermal-drivers-Add-support-for-Airoha-EN7581-therma.patch b/target/linux/airoha/patches-6.6/049-01-v6.16-thermal-drivers-Add-support-for-Airoha-EN7581-therma.patch
new file mode 100644 (file)
index 0000000..e168cda
--- /dev/null
@@ -0,0 +1,550 @@
+From 42de37f40e1bc818df216dfa0918c114cfb5941d Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+Date: Sun, 11 May 2025 20:49:55 +0200
+Subject: [PATCH] thermal/drivers: Add support for Airoha EN7581 thermal sensor
+
+Add support for Airoha EN7581 thermal sensor. This provide support for
+reading the CPU or SoC Package sensor and to setup trip points for hot
+and critical condition. An interrupt is fired to react on this and
+doesn't require passive poll to read the temperature.
+
+The thermal regs provide a way to read the ADC value from an external
+register placed in the Chip SCU regs. Monitor will read this value and
+fire an interrupt if the trip condition configured is reached.
+
+The Thermal Trip and Interrupt logic is conceptually similar to Mediatek
+LVTS Thermal but differ in register mapping and actual function/bug
+workaround. The implementation only share some register names but from
+functionality observation it's very different and used only for the
+basic function of periodically poll the temp and trip the interrupt.
+
+Signed-off-by: Christian Marangi <[email protected]>
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Daniel Lezcano <[email protected]>
+---
+ drivers/thermal/Kconfig          |   9 +
+ drivers/thermal/Makefile         |   1 +
+ drivers/thermal/airoha_thermal.c | 489 +++++++++++++++++++++++++++++++
+ 3 files changed, 499 insertions(+)
+ create mode 100644 drivers/thermal/airoha_thermal.c
+
+--- a/drivers/thermal/Kconfig
++++ b/drivers/thermal/Kconfig
+@@ -317,6 +317,15 @@ config QORIQ_THERMAL
+         cpufreq is used as the cooling device to throttle CPUs when the
+         passive trip is crossed.
++config AIROHA_THERMAL
++      tristate "Airoha thermal sensor driver"
++      depends on ARCH_AIROHA || COMPILE_TEST
++      depends on MFD_SYSCON
++      depends on OF
++      help
++        Enable this to plug the Airoha thermal sensor driver into the Linux
++        thermal framework.
++
+ config SPEAR_THERMAL
+       tristate "SPEAr thermal sensor driver"
+       depends on PLAT_SPEAR || COMPILE_TEST
+--- a/drivers/thermal/Makefile
++++ b/drivers/thermal/Makefile
+@@ -34,6 +34,7 @@ obj-$(CONFIG_K3_THERMAL)     += k3_bandgap.o
+ # platform thermal drivers
+ obj-y                         += broadcom/
+ obj-$(CONFIG_THERMAL_MMIO)            += thermal_mmio.o
++obj-$(CONFIG_AIROHA_THERMAL)  += airoha_thermal.o
+ obj-$(CONFIG_SPEAR_THERMAL)   += spear_thermal.o
+ obj-$(CONFIG_SUN8I_THERMAL)     += sun8i_thermal.o
+ obj-$(CONFIG_ROCKCHIP_THERMAL)        += rockchip_thermal.o
+--- /dev/null
++++ b/drivers/thermal/airoha_thermal.c
+@@ -0,0 +1,489 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++
++#include <linux/module.h>
++#include <linux/bitfield.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/mfd/syscon.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/thermal.h>
++
++/* SCU regs */
++#define EN7581_PLLRG_PROTECT                  0x268
++#define EN7581_PWD_TADC                               0x2ec
++#define   EN7581_MUX_TADC                     GENMASK(3, 1)
++#define EN7581_DOUT_TADC                      0x2f8
++#define   EN7581_DOUT_TADC_MASK                       GENMASK(15, 0)
++
++/* PTP_THERMAL regs */
++#define EN7581_TEMPMONCTL0                    0x800
++#define   EN7581_SENSE3_EN                    BIT(3)
++#define   EN7581_SENSE2_EN                    BIT(2)
++#define   EN7581_SENSE1_EN                    BIT(1)
++#define   EN7581_SENSE0_EN                    BIT(0)
++#define EN7581_TEMPMONCTL1                    0x804
++/* period unit calculated in BUS clock * 256 scaling-up */
++#define   EN7581_PERIOD_UNIT                  GENMASK(9, 0)
++#define EN7581_TEMPMONCTL2                    0x808
++#define   EN7581_FILT_INTERVAL                        GENMASK(25, 16)
++#define   EN7581_SEN_INTERVAL                 GENMASK(9, 0)
++#define EN7581_TEMPMONINT                     0x80C
++#define   EN7581_STAGE3_INT_EN                        BIT(31)
++#define   EN7581_STAGE2_INT_EN                        BIT(30)
++#define   EN7581_STAGE1_INT_EN                        BIT(29)
++#define   EN7581_FILTER_INT_EN_3              BIT(28)
++#define   EN7581_IMMD_INT_EN3                 BIT(27)
++#define   EN7581_NOHOTINTEN3                  BIT(26)
++#define   EN7581_HOFSINTEN3                   BIT(25)
++#define   EN7581_LOFSINTEN3                   BIT(24)
++#define   EN7581_HINTEN3                      BIT(23)
++#define   EN7581_CINTEN3                      BIT(22)
++#define   EN7581_FILTER_INT_EN_2              BIT(21)
++#define   EN7581_FILTER_INT_EN_1              BIT(20)
++#define   EN7581_FILTER_INT_EN_0              BIT(19)
++#define   EN7581_IMMD_INT_EN2                 BIT(18)
++#define   EN7581_IMMD_INT_EN1                 BIT(17)
++#define   EN7581_IMMD_INT_EN0                 BIT(16)
++#define   EN7581_TIME_OUT_INT_EN              BIT(15)
++#define   EN7581_NOHOTINTEN2                  BIT(14)
++#define   EN7581_HOFSINTEN2                   BIT(13)
++#define   EN7581_LOFSINTEN2                   BIT(12)
++#define   EN7581_HINTEN2                      BIT(11)
++#define   EN7581_CINTEN2                      BIT(10)
++#define   EN7581_NOHOTINTEN1                  BIT(9)
++#define   EN7581_HOFSINTEN1                   BIT(8)
++#define   EN7581_LOFSINTEN1                   BIT(7)
++#define   EN7581_HINTEN1                      BIT(6)
++#define   EN7581_CINTEN1                      BIT(5)
++#define   EN7581_NOHOTINTEN0                  BIT(4)
++/* Similar to COLD and HOT also these seems to be swapped in documentation */
++#define   EN7581_LOFSINTEN0                   BIT(3) /* In documentation: BIT(2) */
++#define   EN7581_HOFSINTEN0                   BIT(2) /* In documentation: BIT(3) */
++/* It seems documentation have these swapped as the HW
++ * - Fire BIT(1) when lower than EN7581_COLD_THRE
++ * - Fire BIT(0) and BIT(5) when higher than EN7581_HOT2NORMAL_THRE or
++ *     EN7581_HOT_THRE
++ */
++#define   EN7581_CINTEN0                      BIT(1) /* In documentation: BIT(0) */
++#define   EN7581_HINTEN0                      BIT(0) /* In documentation: BIT(1) */
++#define EN7581_TEMPMONINTSTS                  0x810
++#define   EN7581_STAGE3_INT_STAT              BIT(31)
++#define   EN7581_STAGE2_INT_STAT              BIT(30)
++#define   EN7581_STAGE1_INT_STAT              BIT(29)
++#define   EN7581_FILTER_INT_STAT_3            BIT(28)
++#define   EN7581_IMMD_INT_STS3                        BIT(27)
++#define   EN7581_NOHOTINTSTS3                 BIT(26)
++#define   EN7581_HOFSINTSTS3                  BIT(25)
++#define   EN7581_LOFSINTSTS3                  BIT(24)
++#define   EN7581_HINTSTS3                     BIT(23)
++#define   EN7581_CINTSTS3                     BIT(22)
++#define   EN7581_FILTER_INT_STAT_2            BIT(21)
++#define   EN7581_FILTER_INT_STAT_1            BIT(20)
++#define   EN7581_FILTER_INT_STAT_0            BIT(19)
++#define   EN7581_IMMD_INT_STS2                        BIT(18)
++#define   EN7581_IMMD_INT_STS1                        BIT(17)
++#define   EN7581_IMMD_INT_STS0                        BIT(16)
++#define   EN7581_TIME_OUT_INT_STAT            BIT(15)
++#define   EN7581_NOHOTINTSTS2                 BIT(14)
++#define   EN7581_HOFSINTSTS2                  BIT(13)
++#define   EN7581_LOFSINTSTS2                  BIT(12)
++#define   EN7581_HINTSTS2                     BIT(11)
++#define   EN7581_CINTSTS2                     BIT(10)
++#define   EN7581_NOHOTINTSTS1                 BIT(9)
++#define   EN7581_HOFSINTSTS1                  BIT(8)
++#define   EN7581_LOFSINTSTS1                  BIT(7)
++#define   EN7581_HINTSTS1                     BIT(6)
++#define   EN7581_CINTSTS1                     BIT(5)
++#define   EN7581_NOHOTINTSTS0                 BIT(4)
++/* Similar to COLD and HOT also these seems to be swapped in documentation */
++#define   EN7581_LOFSINTSTS0                  BIT(3) /* In documentation: BIT(2) */
++#define   EN7581_HOFSINTSTS0                  BIT(2) /* In documentation: BIT(3) */
++/* It seems documentation have these swapped as the HW
++ * - Fire BIT(1) when lower than EN7581_COLD_THRE
++ * - Fire BIT(0) and BIT(5) when higher than EN7581_HOT2NORMAL_THRE or
++ *     EN7581_HOT_THRE
++ *
++ * To clear things, we swap the define but we keep them documented here.
++ */
++#define   EN7581_CINTSTS0                     BIT(1) /* In documentation: BIT(0) */
++#define   EN7581_HINTSTS0                     BIT(0) /* In documentation: BIT(1)*/
++/* Monitor will take the bigger threshold between HOT2NORMAL and HOT
++ * and will fire both HOT2NORMAL and HOT interrupt when higher than the 2
++ *
++ * It has also been observed that not setting HOT2NORMAL makes the monitor
++ * treat COLD threshold as HOT2NORMAL.
++ */
++#define EN7581_TEMPH2NTHRE                    0x824
++/* It seems HOT2NORMAL is actually NORMAL2HOT */
++#define   EN7581_HOT2NORMAL_THRE              GENMASK(11, 0)
++#define EN7581_TEMPHTHRE                      0x828
++#define   EN7581_HOT_THRE                     GENMASK(11, 0)
++/* Monitor will use this as HOT2NORMAL (fire interrupt when lower than...)*/
++#define EN7581_TEMPCTHRE                      0x82c
++#define   EN7581_COLD_THRE                    GENMASK(11, 0)
++/* Also LOW and HIGH offset register are swapped */
++#define EN7581_TEMPOFFSETL                    0x830 /* In documentation: 0x834 */
++#define   EN7581_LOW_OFFSET                   GENMASK(11, 0)
++#define EN7581_TEMPOFFSETH                    0x834 /* In documentation: 0x830 */
++#define   EN7581_HIGH_OFFSET                  GENMASK(11, 0)
++#define EN7581_TEMPMSRCTL0                    0x838
++#define   EN7581_MSRCTL3                      GENMASK(11, 9)
++#define   EN7581_MSRCTL2                      GENMASK(8, 6)
++#define   EN7581_MSRCTL1                      GENMASK(5, 3)
++#define   EN7581_MSRCTL0                      GENMASK(2, 0)
++#define EN7581_TEMPADCVALIDADDR                       0x878
++#define   EN7581_ADC_VALID_ADDR                       GENMASK(31, 0)
++#define EN7581_TEMPADCVOLTADDR                        0x87c
++#define   EN7581_ADC_VOLT_ADDR                        GENMASK(31, 0)
++#define EN7581_TEMPRDCTRL                     0x880
++/*
++ * NOTICE: AHB have this set to 0 by default. Means that
++ * the same addr is used for ADC volt and valid reading.
++ * In such case, VALID ADDR is used and volt addr is ignored.
++ */
++#define   EN7581_RD_CTRL_DIFF                 BIT(0)
++#define EN7581_TEMPADCVALIDMASK                       0x884
++#define   EN7581_ADV_RD_VALID_POLARITY                BIT(5)
++#define   EN7581_ADV_RD_VALID_POS             GENMASK(4, 0)
++#define EN7581_TEMPADCVOLTAGESHIFT            0x888
++#define   EN7581_ADC_VOLTAGE_SHIFT            GENMASK(4, 0)
++/*
++ * Same values for each CTL.
++ * Can operate in:
++ * - 1 sample
++ * - 2 sample and make average of them
++ * - 4,6,10,16 sample, drop max and min and make avgerage of them
++ */
++#define   EN7581_MSRCTL_1SAMPLE                       0x0
++#define   EN7581_MSRCTL_AVG2SAMPLE            0x1
++#define   EN7581_MSRCTL_4SAMPLE_MAX_MIX_AVG2  0x2
++#define   EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4  0x3
++#define   EN7581_MSRCTL_10SAMPLE_MAX_MIX_AVG8 0x4
++#define   EN7581_MSRCTL_18SAMPLE_MAX_MIX_AVG16        0x5
++#define EN7581_TEMPAHBPOLL                    0x840
++#define   EN7581_ADC_POLL_INTVL                       GENMASK(31, 0)
++/* PTPSPARE0,2 reg are used to store efuse info for calibrated temp offset */
++#define EN7581_EFUSE_TEMP_OFFSET_REG          0xf20 /* PTPSPARE0 */
++#define   EN7581_EFUSE_TEMP_OFFSET            GENMASK(31, 16)
++#define EN7581_PTPSPARE1                      0xf24 /* PTPSPARE1 */
++#define EN7581_EFUSE_TEMP_CPU_SENSOR_REG      0xf28 /* PTPSPARE2 */
++
++#define EN7581_SLOPE_X100_DIO_DEFAULT         5645
++#define EN7581_SLOPE_X100_DIO_AVS             5645
++
++#define EN7581_INIT_TEMP_CPK_X10              300
++#define EN7581_INIT_TEMP_FTK_X10              620
++#define EN7581_INIT_TEMP_NONK_X10             550
++
++#define EN7581_SCU_THERMAL_PROTECT_KEY                0x12
++#define EN7581_SCU_THERMAL_MUX_DIODE1         0x7
++
++/* Convert temp to raw value as read from ADC ((((temp / 100) - init) * slope) / 1000) + offset */
++#define TEMP_TO_RAW(priv, temp)                       ((((((temp) / 100) - (priv)->init_temp) * \
++                                                (priv)->default_slope) / 1000) + \
++                                               (priv)->default_offset)
++
++/* Convert raw to temp                                ((((temp - offset) * 1000) / slope + init) * 100) */
++#define RAW_TO_TEMP(priv, raw)                        (((((raw) - (priv)->default_offset) * 1000) / \
++                                                (priv)->default_slope + \
++                                                (priv)->init_temp) * 100)
++
++#define AIROHA_MAX_SAMPLES                    6
++
++struct airoha_thermal_priv {
++      void __iomem *base;
++      struct regmap *chip_scu;
++      struct resource scu_adc_res;
++
++      struct thermal_zone_device *tz;
++      int init_temp;
++      int default_slope;
++      int default_offset;
++};
++
++static int airoha_get_thermal_ADC(struct airoha_thermal_priv *priv)
++{
++      u32 val;
++
++      regmap_read(priv->chip_scu, EN7581_DOUT_TADC, &val);
++      return FIELD_GET(EN7581_DOUT_TADC_MASK, val);
++}
++
++static void airoha_init_thermal_ADC_mode(struct airoha_thermal_priv *priv)
++{
++      u32 adc_mux, pllrg;
++
++      /* Save PLLRG current value */
++      regmap_read(priv->chip_scu, EN7581_PLLRG_PROTECT, &pllrg);
++
++      /* Give access to thermal regs */
++      regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, EN7581_SCU_THERMAL_PROTECT_KEY);
++      adc_mux = FIELD_PREP(EN7581_MUX_TADC, EN7581_SCU_THERMAL_MUX_DIODE1);
++      regmap_write(priv->chip_scu, EN7581_PWD_TADC, adc_mux);
++
++      /* Restore PLLRG value on exit */
++      regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, pllrg);
++}
++
++static int airoha_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
++{
++      struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz);
++      int min_value, max_value, avg_value, value;
++      int i;
++
++      avg_value = 0;
++      min_value = INT_MAX;
++      max_value = INT_MIN;
++
++      for (i = 0; i < AIROHA_MAX_SAMPLES; i++) {
++              value = airoha_get_thermal_ADC(priv);
++              min_value = min(value, min_value);
++              max_value = max(value, max_value);
++              avg_value += value;
++      }
++
++      /* Drop min and max and average for the remaining sample */
++      avg_value -= (min_value + max_value);
++      avg_value /= AIROHA_MAX_SAMPLES - 2;
++
++      *temp = RAW_TO_TEMP(priv, avg_value);
++      return 0;
++}
++
++static int airoha_thermal_set_trips(struct thermal_zone_device *tz, int low,
++                                  int high)
++{
++      struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz);
++      bool enable_monitor = false;
++
++      if (high != INT_MAX) {
++              /* Validate high and clamp it a supported value */
++              high = clamp_t(int, high, RAW_TO_TEMP(priv, 0),
++                             RAW_TO_TEMP(priv, FIELD_MAX(EN7581_DOUT_TADC_MASK)));
++
++              /* We offset the high temp of 1°C to trigger correct event */
++              writel(TEMP_TO_RAW(priv, high) >> 4,
++                     priv->base + EN7581_TEMPOFFSETH);
++
++              enable_monitor = true;
++      }
++
++      if (low != -INT_MAX) {
++              /* Validate low and clamp it to a supported value */
++              low = clamp_t(int, high, RAW_TO_TEMP(priv, 0),
++                            RAW_TO_TEMP(priv, FIELD_MAX(EN7581_DOUT_TADC_MASK)));
++
++              /* We offset the low temp of 1°C to trigger correct event */
++              writel(TEMP_TO_RAW(priv, low) >> 4,
++                     priv->base + EN7581_TEMPOFFSETL);
++
++              enable_monitor = true;
++      }
++
++      /* Enable sensor 0 monitor after trip are set */
++      if (enable_monitor)
++              writel(EN7581_SENSE0_EN, priv->base + EN7581_TEMPMONCTL0);
++
++      return 0;
++}
++
++static const struct thermal_zone_device_ops thdev_ops = {
++      .get_temp = airoha_thermal_get_temp,
++      .set_trips = airoha_thermal_set_trips,
++};
++
++static irqreturn_t airoha_thermal_irq(int irq, void *data)
++{
++      struct airoha_thermal_priv *priv = data;
++      enum thermal_notify_event event;
++      bool update = false;
++      u32 status;
++
++      status = readl(priv->base + EN7581_TEMPMONINTSTS);
++      switch (status & (EN7581_HOFSINTSTS0 | EN7581_LOFSINTSTS0)) {
++      case EN7581_HOFSINTSTS0:
++              event = THERMAL_TRIP_VIOLATED;
++              update = true;
++              break;
++      case EN7581_LOFSINTSTS0:
++              event = THERMAL_EVENT_UNSPECIFIED;
++              update = true;
++              break;
++      default:
++              /* Should be impossible as we enable only these Interrupt */
++              break;
++      }
++
++      /* Reset Interrupt */
++      writel(status, priv->base + EN7581_TEMPMONINTSTS);
++
++      if (update)
++              thermal_zone_device_update(priv->tz, event);
++
++      return IRQ_HANDLED;
++}
++
++static void airoha_thermal_setup_adc_val(struct device *dev,
++                                       struct airoha_thermal_priv *priv)
++{
++      u32 efuse_calib_info, cpu_sensor;
++
++      /* Setup thermal sensor to ADC mode and setup the mux to DIODE1 */
++      airoha_init_thermal_ADC_mode(priv);
++      /* sleep 10 ms for ADC to enable */
++      usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC);
++
++      efuse_calib_info = readl(priv->base + EN7581_EFUSE_TEMP_OFFSET_REG);
++      if (efuse_calib_info) {
++              priv->default_offset = FIELD_GET(EN7581_EFUSE_TEMP_OFFSET, efuse_calib_info);
++              /* Different slope are applied if the sensor is used for CPU or for package */
++              cpu_sensor = readl(priv->base + EN7581_EFUSE_TEMP_CPU_SENSOR_REG);
++              if (cpu_sensor) {
++                      priv->default_slope = EN7581_SLOPE_X100_DIO_DEFAULT;
++                      priv->init_temp = EN7581_INIT_TEMP_FTK_X10;
++              } else {
++                      priv->default_slope = EN7581_SLOPE_X100_DIO_AVS;
++                      priv->init_temp = EN7581_INIT_TEMP_CPK_X10;
++              }
++      } else {
++              priv->default_offset = airoha_get_thermal_ADC(priv);
++              priv->default_slope = EN7581_SLOPE_X100_DIO_DEFAULT;
++              priv->init_temp = EN7581_INIT_TEMP_NONK_X10;
++              dev_info(dev, "missing thermal calibrarion EFUSE, using non calibrated value\n");
++      }
++}
++
++static void airoha_thermal_setup_monitor(struct airoha_thermal_priv *priv)
++{
++      /* Set measure mode */
++      writel(FIELD_PREP(EN7581_MSRCTL0, EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4),
++             priv->base + EN7581_TEMPMSRCTL0);
++
++      /*
++       * Configure ADC valid reading addr
++       * The AHB temp monitor system doesn't have direct access to the
++       * thermal sensor. It does instead work by providing all kind of
++       * address to configure how to access and setup an ADC for the
++       * sensor. EN7581 supports only one sensor hence the
++       * implementation is greatly simplified but the AHB supports
++       * up to 4 different sensor from the same ADC that can be
++       * switched by tuning the ADC mux or wiriting address.
++       *
++       * We set valid instead of volt as we don't enable valid/volt
++       * split reading and AHB read valid addr in such case.
++       */
++      writel(priv->scu_adc_res.start + EN7581_DOUT_TADC,
++             priv->base + EN7581_TEMPADCVALIDADDR);
++
++      /*
++       * Configure valid bit on a fake value of bit 16. The ADC outputs
++       * max of 2 bytes for voltage.
++       */
++      writel(FIELD_PREP(EN7581_ADV_RD_VALID_POS, 16),
++             priv->base + EN7581_TEMPADCVALIDMASK);
++
++      /*
++       * AHB supports max 12 bytes for ADC voltage. Shift the read
++       * value 4 bit to the right. Precision lost by this is minimal
++       * in the order of half a °C and is acceptable in the context
++       * of triggering interrupt in critical condition.
++       */
++      writel(FIELD_PREP(EN7581_ADC_VOLTAGE_SHIFT, 4),
++             priv->base + EN7581_TEMPADCVOLTAGESHIFT);
++
++      /* BUS clock is 300MHz counting unit is 3 * 68.64 * 256 = 52.715us */
++      writel(FIELD_PREP(EN7581_PERIOD_UNIT, 3),
++             priv->base + EN7581_TEMPMONCTL1);
++
++      /*
++       * filt interval is 1 * 52.715us = 52.715us,
++       * sen interval is 379 * 52.715us = 19.97ms
++       */
++      writel(FIELD_PREP(EN7581_FILT_INTERVAL, 1) |
++             FIELD_PREP(EN7581_FILT_INTERVAL, 379),
++             priv->base + EN7581_TEMPMONCTL2);
++
++      /* AHB poll is set to 146 * 68.64 = 10.02us */
++      writel(FIELD_PREP(EN7581_ADC_POLL_INTVL, 146),
++             priv->base + EN7581_TEMPAHBPOLL);
++}
++
++static int airoha_thermal_probe(struct platform_device *pdev)
++{
++      struct airoha_thermal_priv *priv;
++      struct device_node *chip_scu_np;
++      struct device *dev = &pdev->dev;
++      int irq, ret;
++
++      priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++
++      priv->base = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(priv->base))
++              return PTR_ERR(priv->base);
++
++      chip_scu_np = of_parse_phandle(dev->of_node, "airoha,chip-scu", 0);
++      if (!chip_scu_np)
++              return -EINVAL;
++
++      priv->chip_scu = syscon_node_to_regmap(chip_scu_np);
++      if (IS_ERR(priv->chip_scu))
++              return PTR_ERR(priv->chip_scu);
++
++      of_address_to_resource(chip_scu_np, 0, &priv->scu_adc_res);
++      of_node_put(chip_scu_np);
++
++      irq = platform_get_irq(pdev, 0);
++      if (irq < 0)
++              return irq;
++
++      ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
++                                      airoha_thermal_irq, IRQF_ONESHOT,
++                                      pdev->name, priv);
++      if (ret) {
++              dev_err(dev, "Can't get interrupt working.\n");
++              return ret;
++      }
++
++      airoha_thermal_setup_monitor(priv);
++      airoha_thermal_setup_adc_val(dev, priv);
++
++      /* register of thermal sensor and get info from DT */
++      priv->tz = devm_thermal_of_zone_register(dev, 0, priv, &thdev_ops);
++      if (IS_ERR(priv->tz)) {
++              dev_err(dev, "register thermal zone sensor failed\n");
++              return PTR_ERR(priv->tz);
++      }
++
++      platform_set_drvdata(pdev, priv);
++
++      /* Enable LOW and HIGH interrupt */
++      writel(EN7581_HOFSINTEN0 | EN7581_LOFSINTEN0,
++             priv->base + EN7581_TEMPMONINT);
++
++      return 0;
++}
++
++static const struct of_device_id airoha_thermal_match[] = {
++      { .compatible = "airoha,en7581-thermal" },
++      {},
++};
++MODULE_DEVICE_TABLE(of, airoha_thermal_match);
++
++static struct platform_driver airoha_thermal_driver = {
++      .driver = {
++              .name = "airoha-thermal",
++              .of_match_table = airoha_thermal_match,
++      },
++      .probe = airoha_thermal_probe,
++};
++
++module_platform_driver(airoha_thermal_driver);
++
++MODULE_AUTHOR("Christian Marangi <[email protected]>");
++MODULE_DESCRIPTION("Airoha thermal driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/airoha/patches-6.6/049-02-v6.16-thermal-drivers-airoha-Fix-spelling-mistake.patch b/target/linux/airoha/patches-6.6/049-02-v6.16-thermal-drivers-airoha-Fix-spelling-mistake.patch
new file mode 100644 (file)
index 0000000..7b1b947
--- /dev/null
@@ -0,0 +1,44 @@
+From e23cba0ab49a9cf95e9bc3a86cfbf336b0e285f6 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+Date: Wed, 14 May 2025 23:39:12 +0200
+Subject: [PATCH] thermal/drivers/airoha: Fix spelling mistake
+
+Fix various spelling mistake in airoha_thermal_setup_monitor() and
+define.
+
+Reported-by: Alok Tiwari <[email protected]>
+Signed-off-by: Christian Marangi <[email protected]>
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Daniel Lezcano <[email protected]>
+---
+ drivers/thermal/airoha_thermal.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/thermal/airoha_thermal.c
++++ b/drivers/thermal/airoha_thermal.c
+@@ -155,7 +155,7 @@
+  * Can operate in:
+  * - 1 sample
+  * - 2 sample and make average of them
+- * - 4,6,10,16 sample, drop max and min and make avgerage of them
++ * - 4,6,10,16 sample, drop max and min and make average of them
+  */
+ #define   EN7581_MSRCTL_1SAMPLE                       0x0
+ #define   EN7581_MSRCTL_AVG2SAMPLE            0x1
+@@ -365,12 +365,12 @@ static void airoha_thermal_setup_monitor
+       /*
+        * Configure ADC valid reading addr
+        * The AHB temp monitor system doesn't have direct access to the
+-       * thermal sensor. It does instead work by providing all kind of
+-       * address to configure how to access and setup an ADC for the
++       * thermal sensor. It does instead work by providing various
++       * addresses to configure how to access and setup an ADC for the
+        * sensor. EN7581 supports only one sensor hence the
+        * implementation is greatly simplified but the AHB supports
+-       * up to 4 different sensor from the same ADC that can be
+-       * switched by tuning the ADC mux or wiriting address.
++       * up to 4 different sensors from the same ADC that can be
++       * switched by tuning the ADC mux or writing address.
+        *
+        * We set valid instead of volt as we don't enable valid/volt
+        * split reading and AHB read valid addr in such case.
diff --git a/target/linux/airoha/patches-6.6/101-01-thermal-of-Add-devm_thermal_of_zone_register_with_pa.patch b/target/linux/airoha/patches-6.6/101-01-thermal-of-Add-devm_thermal_of_zone_register_with_pa.patch
deleted file mode 100644 (file)
index d646e05..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-From 1f194995c3648e20da53137d4c9110b39e779f41 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <[email protected]>
-Date: Fri, 18 Oct 2024 11:34:35 +0200
-Subject: [PATCH 2/3] thermal: of: Add
- devm_thermal_of_zone_register_with_params() variant
-
-Commit b1ae92dcfa8e ("thermal: core: Make struct thermal_zone_device
-definition internal") moved the thermal_zone_device struct from global
-thermal.h to internal thermal_core.h making the internal variables of
-the struct not accessible from the user drivers (without inclusing
-thermal_core.h).
-
-One case where the internal variables might be needed is for the
-thermal_zone_params in the context of OF probe.
-
-In such case a thermal driver might have default params that can only be
-parsed at runtime (example present in EFUSE or derived from other values)
-and wants to update the values in the thermal_zone_params for the
-thermal device. (to use the helper like get_slope() or get_offset())
-
-To account for this scenario, introduce a variant of
-devm_thermal_of_zone_register(),
-devm_thermal_of_zone_register_with_params(), that takes and additional
-variable and permits to register the thermal device with default
-thermal_zone_params.
-
-To follow OF implementation, these params are only treated as default
-params and are ignored if a related one is defined in DT. (example a
-slope or offset value defined in DT have priority to the default one
-passed in a thermal_device_params struct)
-
-This permits to support both implementation, use the helpers and expose
-these values in sysfs.
-
-Signed-off-by: Christian Marangi <[email protected]>
----
- drivers/thermal/thermal_of.c | 67 +++++++++++++++++++++++++++++-------
- include/linux/thermal.h      | 13 +++++++
- 2 files changed, 68 insertions(+), 12 deletions(-)
-
---- a/drivers/thermal/thermal_of.c
-+++ b/drivers/thermal/thermal_of.c
-@@ -246,7 +246,7 @@ static void thermal_of_parameters_init(s
- {
-       int coef[2];
-       int ncoef = ARRAY_SIZE(coef);
--      int prop, ret;
-+      int prop;
-       tzp->no_hwmon = true;
-@@ -258,14 +258,11 @@ static void thermal_of_parameters_init(s
-        * thermal zone. Thus, we are considering only the first two
-        * values as slope and offset.
-        */
--      ret = of_property_read_u32_array(np, "coefficients", coef, ncoef);
--      if (ret) {
--              coef[0] = 1;
--              coef[1] = 0;
-+      if (!of_property_read_u32_array(np, "coefficients", coef, ncoef)) {
-+              tzp->slope = coef[0];
-+              tzp->offset = coef[1];
-       }
--      tzp->slope = coef[0];
--      tzp->offset = coef[1];
- }
- static struct device_node *thermal_of_zone_get_by_name(struct thermal_zone_device *tz)
-@@ -459,10 +456,15 @@ static void thermal_of_zone_unregister(s
-  * zone properties and registers new thermal zone with those
-  * properties.
-  *
-+ * The passed thermal zone params are treated as default values and ignored if
-+ * the related property is found in DT. (DT params have priority to
-+ * default values)
-+ *
-  * @sensor: A device node pointer corresponding to the sensor in the device tree
-  * @id: An integer as sensor identifier
-  * @data: A private data to be stored in the thermal zone dedicated private area
-  * @ops: A set of thermal sensor ops
-+ * @tzp: a pointer to the default thermal zone params structure associated with the sensor
-  *
-  * Return: a valid thermal zone structure pointer on success.
-  *    - EINVAL: if the device tree thermal description is malformed
-@@ -470,11 +472,11 @@ static void thermal_of_zone_unregister(s
-  *    - Other negative errors are returned by the underlying called functions
-  */
- static struct thermal_zone_device *thermal_of_zone_register(struct device_node *sensor, int id, void *data,
--                                                          const struct thermal_zone_device_ops *ops)
-+                                                          const struct thermal_zone_device_ops *ops,
-+                                                          struct thermal_zone_params *tzp)
- {
-       struct thermal_zone_device *tz;
-       struct thermal_trip *trips;
--      struct thermal_zone_params tzp = {};
-       struct thermal_zone_device_ops *of_ops;
-       struct device_node *np;
-       int delay, pdelay;
-@@ -509,7 +511,7 @@ static struct thermal_zone_device *therm
-               goto out_kfree_trips;
-       }
--      thermal_of_parameters_init(np, &tzp);
-+      thermal_of_parameters_init(np, tzp);
-       of_ops->bind = thermal_of_bind;
-       of_ops->unbind = thermal_of_unbind;
-@@ -517,7 +519,7 @@ static struct thermal_zone_device *therm
-       mask = GENMASK_ULL((ntrips) - 1, 0);
-       tz = thermal_zone_device_register_with_trips(np->name, trips, ntrips,
--                                                   mask, data, of_ops, &tzp,
-+                                                   mask, data, of_ops, tzp,
-                                                    pdelay, delay);
-       if (IS_ERR(tz)) {
-               ret = PTR_ERR(tz);
-@@ -572,6 +574,7 @@ static int devm_thermal_of_zone_match(st
- struct thermal_zone_device *devm_thermal_of_zone_register(struct device *dev, int sensor_id, void *data,
-                                                         const struct thermal_zone_device_ops *ops)
- {
-+      struct thermal_zone_params tzp = { .slope = 1 };
-       struct thermal_zone_device **ptr, *tzd;
-       ptr = devres_alloc(devm_thermal_of_zone_release, sizeof(*ptr),
-@@ -579,7 +582,7 @@ struct thermal_zone_device *devm_thermal
-       if (!ptr)
-               return ERR_PTR(-ENOMEM);
--      tzd = thermal_of_zone_register(dev->of_node, sensor_id, data, ops);
-+      tzd = thermal_of_zone_register(dev->of_node, sensor_id, data, ops, &tzp);
-       if (IS_ERR(tzd)) {
-               devres_free(ptr);
-               return tzd;
-@@ -593,6 +596,46 @@ struct thermal_zone_device *devm_thermal
- EXPORT_SYMBOL_GPL(devm_thermal_of_zone_register);
- /**
-+ * devm_thermal_of_zone_register_with_params - register a thermal tied with the sensor life cycle
-+ *                                           with default params
-+ *
-+ * This function is the device version of the thermal_of_zone_register() function.
-+ *
-+ * @dev: a device structure pointer to sensor to be tied with the thermal zone OF life cycle
-+ * @sensor_id: the sensor identifier
-+ * @data: a pointer to a private data to be stored in the thermal zone 'devdata' field
-+ * @ops: a pointer to the ops structure associated with the sensor
-+ * @tzp: a pointer to the default thermal zone params structure associated with the sensor
-+ *
-+ * The thermal zone params are treated as default values and ignored if the related property is
-+ * found in DT. (DT params have priority to default values)
-+ */
-+struct thermal_zone_device *devm_thermal_of_zone_register_with_params(struct device *dev, int sensor_id,
-+                                                                    void *data,
-+                                                                    const struct thermal_zone_device_ops *ops,
-+                                                                    struct thermal_zone_params *tzp)
-+{
-+      struct thermal_zone_device **ptr, *tzd;
-+
-+      ptr = devres_alloc(devm_thermal_of_zone_release, sizeof(*ptr),
-+                         GFP_KERNEL);
-+      if (!ptr)
-+              return ERR_PTR(-ENOMEM);
-+
-+      tzd = thermal_of_zone_register(dev->of_node, sensor_id, data, ops, tzp);
-+      if (IS_ERR(tzd)) {
-+              devres_free(ptr);
-+              return tzd;
-+      }
-+
-+      *ptr = tzd;
-+      devres_add(dev, ptr);
-+
-+      return tzd;
-+}
-+EXPORT_SYMBOL_GPL(devm_thermal_of_zone_register_with_params);
-+
-+/**
-  * devm_thermal_of_zone_unregister - Resource managed version of
-  *                            thermal_of_zone_unregister().
-  * @dev: Device for which which resource was allocated.
---- a/include/linux/thermal.h
-+++ b/include/linux/thermal.h
-@@ -263,6 +263,10 @@ struct thermal_zone_params {
- #ifdef CONFIG_THERMAL_OF
- struct thermal_zone_device *devm_thermal_of_zone_register(struct device *dev, int id, void *data,
-                                                         const struct thermal_zone_device_ops *ops);
-+struct thermal_zone_device *devm_thermal_of_zone_register_with_params(struct device *dev, int id,
-+                                                                    void *data,
-+                                                                    const struct thermal_zone_device_ops *ops,
-+                                                                    struct thermal_zone_params *tzp);
- void devm_thermal_of_zone_unregister(struct device *dev, struct thermal_zone_device *tz);
-@@ -274,6 +278,15 @@ struct thermal_zone_device *devm_thermal
- {
-       return ERR_PTR(-ENOTSUPP);
- }
-+
-+static inline
-+struct thermal_zone_device *devm_thermal_of_zone_register_with_params(struct device *dev, int id,
-+                                                                    void *data,
-+                                                                    const struct thermal_zone_device_ops *ops,
-+                                                                    struct thermal_zone_params *tzp)
-+{
-+      return ERR_PTR(-ENOTSUPP);
-+}
- static inline void devm_thermal_of_zone_unregister(struct device *dev,
-                                                  struct thermal_zone_device *tz)
diff --git a/target/linux/airoha/patches-6.6/101-02-thermal-Add-support-for-Airoha-EN7581-thermal-sensor.patch b/target/linux/airoha/patches-6.6/101-02-thermal-Add-support-for-Airoha-EN7581-thermal-sensor.patch
deleted file mode 100644 (file)
index ffacbba..0000000
+++ /dev/null
@@ -1,535 +0,0 @@
-From bc6a6a4ec6c28467683121cc165e5681b4acdf8d Mon Sep 17 00:00:00 2001
-From: Christian Marangi <[email protected]>
-Date: Tue, 27 Aug 2024 23:04:53 +0200
-Subject: [PATCH 3/3] thermal: Add support for Airoha EN7581 thermal sensor
-
-Add support for Airoha EN7581 thermal sensor. This provide support for
-reading the CPU or SoC Package sensor and to setup trip points for hot
-and critical condition. An interrupt is fired to react on this and
-doesn't require passive poll to read the temperature.
-
-The thermal regs provide a way to read the ADC value from an external
-register placed in the Chip SCU regs. Monitor will read this value and
-fire an interrupt if the trip condition configured is reached.
-
-Signed-off-by: Christian Marangi <[email protected]>
----
- drivers/thermal/Kconfig          |   9 +
- drivers/thermal/Makefile         |   1 +
- drivers/thermal/airoha_thermal.c | 482 +++++++++++++++++++++++++++++++
- 3 files changed, 492 insertions(+)
- create mode 100644 drivers/thermal/airoha_thermal.c
-
---- a/drivers/thermal/Kconfig
-+++ b/drivers/thermal/Kconfig
-@@ -317,6 +317,15 @@ config QORIQ_THERMAL
-         cpufreq is used as the cooling device to throttle CPUs when the
-         passive trip is crossed.
-+config AIROHA_THERMAL
-+      tristate "Airoha thermal sensor driver"
-+      depends on ARCH_AIROHA || COMPILE_TEST
-+      depends on MFD_SYSCON
-+      depends on OF
-+      help
-+        Enable this to plug the Airoha thermal sensor driver into the Linux
-+        thermal framework.
-+
- config SPEAR_THERMAL
-       tristate "SPEAr thermal sensor driver"
-       depends on PLAT_SPEAR || COMPILE_TEST
---- a/drivers/thermal/Makefile
-+++ b/drivers/thermal/Makefile
-@@ -34,6 +34,7 @@ obj-$(CONFIG_K3_THERMAL)     += k3_bandgap.o
- # platform thermal drivers
- obj-y                         += broadcom/
- obj-$(CONFIG_THERMAL_MMIO)            += thermal_mmio.o
-+obj-$(CONFIG_AIROHA_THERMAL)  += airoha_thermal.o
- obj-$(CONFIG_SPEAR_THERMAL)   += spear_thermal.o
- obj-$(CONFIG_SUN8I_THERMAL)     += sun8i_thermal.o
- obj-$(CONFIG_ROCKCHIP_THERMAL)        += rockchip_thermal.o
---- /dev/null
-+++ b/drivers/thermal/airoha_thermal.c
-@@ -0,0 +1,482 @@
-+// SPDX-License-Identifier: GPL-2.0-or-later
-+
-+#include <linux/module.h>
-+#include <linux/bitfield.h>
-+#include <linux/delay.h>
-+#include <linux/interrupt.h>
-+#include <linux/mfd/syscon.h>
-+#include <linux/of.h>
-+#include <linux/of_address.h>
-+#include <linux/platform_device.h>
-+#include <linux/regmap.h>
-+#include <linux/thermal.h>
-+
-+/* SCU regs */
-+#define EN7581_PLLRG_PROTECT                  0x268
-+#define EN7581_PWD_TADC                               0x2ec
-+#define   EN7581_MUX_TADC                     GENMASK(3, 1)
-+#define EN7581_DOUT_TADC                      0x2f8
-+#define   EN7581_DOUT_TADC_MASK                       GENMASK(15, 0)
-+
-+/* PTP_THERMAL regs */
-+#define EN7581_TEMPMONCTL0                    0x800
-+#define   EN7581_SENSE3_EN                    BIT(3)
-+#define   EN7581_SENSE2_EN                    BIT(2)
-+#define   EN7581_SENSE1_EN                    BIT(1)
-+#define   EN7581_SENSE0_EN                    BIT(0)
-+#define EN7581_TEMPMONCTL1                    0x804
-+/* period unit calculated in BUS clock * 256 scaling-up */
-+#define   EN7581_PERIOD_UNIT                  GENMASK(9, 0)
-+#define EN7581_TEMPMONCTL2                    0x808
-+#define   EN7581_FILT_INTERVAL                        GENMASK(25, 16)
-+#define   EN7581_SEN_INTERVAL                 GENMASK(9, 0)
-+#define EN7581_TEMPMONINT                     0x80C
-+#define   EN7581_STAGE3_INT_EN                        BIT(31)
-+#define   EN7581_STAGE2_INT_EN                        BIT(30)
-+#define   EN7581_STAGE1_INT_EN                        BIT(29)
-+#define   EN7581_FILTER_INT_EN_3              BIT(28)
-+#define   EN7581_IMMD_INT_EN3                 BIT(27)
-+#define   EN7581_NOHOTINTEN3                  BIT(26)
-+#define   EN7581_HOFSINTEN3                   BIT(25)
-+#define   EN7581_LOFSINTEN3                   BIT(24)
-+#define   EN7581_HINTEN3                      BIT(23)
-+#define   EN7581_CINTEN3                      BIT(22)
-+#define   EN7581_FILTER_INT_EN_2              BIT(21)
-+#define   EN7581_FILTER_INT_EN_1              BIT(20)
-+#define   EN7581_FILTER_INT_EN_0              BIT(19)
-+#define   EN7581_IMMD_INT_EN2                 BIT(18)
-+#define   EN7581_IMMD_INT_EN1                 BIT(17)
-+#define   EN7581_IMMD_INT_EN0                 BIT(16)
-+#define   EN7581_TIME_OUT_INT_EN              BIT(15)
-+#define   EN7581_NOHOTINTEN2                  BIT(14)
-+#define   EN7581_HOFSINTEN2                   BIT(13)
-+#define   EN7581_LOFSINTEN2                   BIT(12)
-+#define   EN7581_HINTEN2                      BIT(11)
-+#define   EN7581_CINTEN2                      BIT(10)
-+#define   EN7581_NOHOTINTEN1                  BIT(9)
-+#define   EN7581_HOFSINTEN1                   BIT(8)
-+#define   EN7581_LOFSINTEN1                   BIT(7)
-+#define   EN7581_HINTEN1                      BIT(6)
-+#define   EN7581_CINTEN1                      BIT(5)
-+#define   EN7581_NOHOTINTEN0                  BIT(4)
-+/* Similar to COLD and HOT also these seems to be swapped in documentation */
-+#define   EN7581_LOFSINTEN0                   BIT(3) /* In documentation: BIT(2) */
-+#define   EN7581_HOFSINTEN0                   BIT(2) /* In documentation: BIT(3) */
-+/* It seems documentation have these swapped as the HW
-+ * - Fire BIT(1) when lower than EN7581_COLD_THRE
-+ * - Fire BIT(0) and BIT(5) when higher than EN7581_HOT2NORMAL_THRE or
-+ *     EN7581_HOT_THRE
-+ */
-+#define   EN7581_CINTEN0                      BIT(1) /* In documentation: BIT(0) */
-+#define   EN7581_HINTEN0                      BIT(0) /* In documentation: BIT(1) */
-+#define EN7581_TEMPMONINTSTS                  0x810
-+#define   EN7581_STAGE3_INT_STAT              BIT(31)
-+#define   EN7581_STAGE2_INT_STAT              BIT(30)
-+#define   EN7581_STAGE1_INT_STAT              BIT(29)
-+#define   EN7581_FILTER_INT_STAT_3            BIT(28)
-+#define   EN7581_IMMD_INT_STS3                        BIT(27)
-+#define   EN7581_NOHOTINTSTS3                 BIT(26)
-+#define   EN7581_HOFSINTSTS3                  BIT(25)
-+#define   EN7581_LOFSINTSTS3                  BIT(24)
-+#define   EN7581_HINTSTS3                     BIT(23)
-+#define   EN7581_CINTSTS3                     BIT(22)
-+#define   EN7581_FILTER_INT_STAT_2            BIT(21)
-+#define   EN7581_FILTER_INT_STAT_1            BIT(20)
-+#define   EN7581_FILTER_INT_STAT_0            BIT(19)
-+#define   EN7581_IMMD_INT_STS2                        BIT(18)
-+#define   EN7581_IMMD_INT_STS1                        BIT(17)
-+#define   EN7581_IMMD_INT_STS0                        BIT(16)
-+#define   EN7581_TIME_OUT_INT_STAT            BIT(15)
-+#define   EN7581_NOHOTINTSTS2                 BIT(14)
-+#define   EN7581_HOFSINTSTS2                  BIT(13)
-+#define   EN7581_LOFSINTSTS2                  BIT(12)
-+#define   EN7581_HINTSTS2                     BIT(11)
-+#define   EN7581_CINTSTS2                     BIT(10)
-+#define   EN7581_NOHOTINTSTS1                 BIT(9)
-+#define   EN7581_HOFSINTSTS1                  BIT(8)
-+#define   EN7581_LOFSINTSTS1                  BIT(7)
-+#define   EN7581_HINTSTS1                     BIT(6)
-+#define   EN7581_CINTSTS1                     BIT(5)
-+#define   EN7581_NOHOTINTSTS0                 BIT(4)
-+/* Similar to COLD and HOT also these seems to be swapped in documentation */
-+#define   EN7581_LOFSINTSTS0                  BIT(3) /* In documentation: BIT(2) */
-+#define   EN7581_HOFSINTSTS0                  BIT(2) /* In documentation: BIT(3) */
-+/* It seems documentation have these swapped as the HW
-+ * - Fire BIT(1) when lower than EN7581_COLD_THRE
-+ * - Fire BIT(0) and BIT(5) when higher than EN7581_HOT2NORMAL_THRE or
-+ *     EN7581_HOT_THRE
-+ *
-+ * To clear things, we swap the define but we keep them documented here.
-+ */
-+#define   EN7581_CINTSTS0                     BIT(1) /* In documentation: BIT(0) */
-+#define   EN7581_HINTSTS0                     BIT(0) /* In documentation: BIT(1)*/
-+/* Monitor will take the bigger threshold between HOT2NORMAL and HOT
-+ * and will fire both HOT2NORMAL and HOT interrupt when higher than the 2
-+ *
-+ * It has also been observed that not setting HOT2NORMAL makes the monitor
-+ * treat COLD threshold as HOT2NORMAL.
-+ */
-+#define EN7581_TEMPH2NTHRE                    0x824
-+/* It seems HOT2NORMAL is actually NORMAL2HOT */
-+#define   EN7581_HOT2NORMAL_THRE              GENMASK(11, 0)
-+#define EN7581_TEMPHTHRE                      0x828
-+#define   EN7581_HOT_THRE                     GENMASK(11, 0)
-+/* Monitor will use this as HOT2NORMAL (fire interrupt when lower than...)*/
-+#define EN7581_TEMPCTHRE                      0x82c
-+#define   EN7581_COLD_THRE                    GENMASK(11, 0)
-+/* Also LOW and HIGH offset register are swapped */
-+#define EN7581_TEMPOFFSETL                    0x830 /* In documentation: 0x834 */
-+#define   EN7581_LOW_OFFSET                   GENMASK(11, 0)
-+#define EN7581_TEMPOFFSETH                    0x834 /* In documentation: 0x830 */
-+#define   EN7581_HIGH_OFFSET                  GENMASK(11, 0)
-+#define EN7581_TEMPMSRCTL0                    0x838
-+#define   EN7581_MSRCTL3                      GENMASK(11, 9)
-+#define   EN7581_MSRCTL2                      GENMASK(8, 6)
-+#define   EN7581_MSRCTL1                      GENMASK(5, 3)
-+#define   EN7581_MSRCTL0                      GENMASK(2, 0)
-+#define EN7581_TEMPADCVALIDADDR                       0x878
-+#define   EN7581_ADC_VALID_ADDR                       GENMASK(31, 0)
-+#define EN7581_TEMPADCVOLTADDR                        0x87c
-+#define   EN7581_ADC_VOLT_ADDR                        GENMASK(31, 0)
-+#define EN7581_TEMPRDCTRL                     0x880
-+/*
-+ * NOTICE: AHB have this set to 0 by default. Means that
-+ * the same addr is used for ADC volt and valid reading.
-+ * In such case, VALID ADDR is used and volt addr is ignored.
-+ */
-+#define   EN7581_RD_CTRL_DIFF                 BIT(0)
-+#define EN7581_TEMPADCVALIDMASK                       0x884
-+#define   EN7581_ADV_RD_VALID_POLARITY                BIT(5)
-+#define   EN7581_ADV_RD_VALID_POS             GENMASK(4, 0)
-+#define EN7581_TEMPADCVOLTAGESHIFT            0x888
-+#define   EN7581_ADC_VOLTAGE_SHIFT            GENMASK(4, 0)
-+/*
-+ * Same values for each CTL.
-+ * Can operate in:
-+ * - 1 sample
-+ * - 2 sample and make average of them
-+ * - 4,6,10,16 sample, drop max and min and make avgerage of them
-+ */
-+#define   EN7581_MSRCTL_1SAMPLE                       0x0
-+#define   EN7581_MSRCTL_AVG2SAMPLE            0x1
-+#define   EN7581_MSRCTL_4SAMPLE_MAX_MIX_AVG2  0x2
-+#define   EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4  0x3
-+#define   EN7581_MSRCTL_10SAMPLE_MAX_MIX_AVG8 0x4
-+#define   EN7581_MSRCTL_18SAMPLE_MAX_MIX_AVG16        0x5
-+#define EN7581_TEMPAHBPOLL                    0x840
-+#define   EN7581_ADC_POLL_INTVL                       GENMASK(31, 0)
-+/* PTPSPARE0,2 reg are used to store efuse info for calibrated temp offset */
-+#define EN7581_EFUSE_TEMP_OFFSET_REG          0xf20 /* PTPSPARE0 */
-+#define   EN7581_EFUSE_TEMP_OFFSET            GENMASK(31, 16)
-+#define EN7581_PTPSPARE1                      0xf24 /* PTPSPARE1 */
-+#define EN7581_EFUSE_TEMP_CPU_SENSOR_REG      0xf28 /* PTPSPARE2 */
-+
-+#define EN7581_SLOPE_X100_DIO_DEFAULT         5645
-+#define EN7581_SLOPE_X100_DIO_AVS             5645
-+
-+#define EN7581_INIT_TEMP_CPK_X10              300
-+#define EN7581_INIT_TEMP_FTK_X10              620
-+#define EN7581_INIT_TEMP_NONK_X10             550
-+
-+#define EN7581_SCU_THERMAL_PROTECT_KEY                0x12
-+#define EN7581_SCU_THERMAL_MUX_DIODE1         0x7
-+
-+/* Convert temp to raw value as read from ADC ((((temp / 100) - init) * slope) / 1000) + offset */
-+#define TEMP_TO_RAW(priv, tz, temp)           ((((((temp) / 100) - (priv)->init_temp) * \
-+                                                thermal_zone_get_slope(tz)) / 1000) + \
-+                                                thermal_zone_get_offset(tz))
-+
-+/* Convert raw to temp                                ((((temp - offset) * 1000) / slope + init) * 100) */
-+#define RAW_TO_TEMP(priv, tz, raw)            (((((raw) - thermal_zone_get_offset(tz)) * 1000) / \
-+                                                thermal_zone_get_slope(tz) + \
-+                                                (priv)->init_temp) * 100)
-+
-+struct airoha_thermal_priv {
-+      void __iomem *base;
-+      struct regmap *chip_scu;
-+      struct resource scu_adc_res;
-+
-+      struct thermal_zone_device *tz;
-+      int init_temp;
-+};
-+
-+static int airoha_get_thermal_ADC(struct airoha_thermal_priv *priv)
-+{
-+      u32 val;
-+
-+      regmap_read(priv->chip_scu, EN7581_DOUT_TADC, &val);
-+      return FIELD_GET(EN7581_DOUT_TADC_MASK, val);
-+}
-+
-+static void airoha_init_thermal_ADC_mode(struct airoha_thermal_priv *priv)
-+{
-+      u32 adc_mux, pllrg;
-+
-+      /* Save PLLRG current value */
-+      regmap_read(priv->chip_scu, EN7581_PLLRG_PROTECT, &pllrg);
-+
-+      /* Give access to thermal regs */
-+      regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, EN7581_SCU_THERMAL_PROTECT_KEY);
-+      adc_mux = FIELD_PREP(EN7581_MUX_TADC, EN7581_SCU_THERMAL_MUX_DIODE1);
-+      regmap_write(priv->chip_scu, EN7581_PWD_TADC, adc_mux);
-+
-+      /* Restore PLLRG value on exit */
-+      regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, pllrg);
-+}
-+
-+static int airoha_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
-+{
-+      struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz);
-+      int min, max, avg_temp, temp_adc;
-+      int i;
-+
-+      /* Get the starting temp */
-+      temp_adc = airoha_get_thermal_ADC(priv);
-+      min = temp_adc;
-+      max = temp_adc;
-+      avg_temp = temp_adc;
-+
-+      /* Make 5 more measurement and average the temp ADC difference */
-+      for (i = 0; i < 5; i++) {
-+              temp_adc = airoha_get_thermal_ADC(priv);
-+              avg_temp += temp_adc;
-+              if (temp_adc > max)
-+                      max = temp_adc;
-+              if (temp_adc < min)
-+                      min = temp_adc;
-+      }
-+      avg_temp = avg_temp - max - min;
-+      avg_temp /= 4;
-+
-+      *temp = RAW_TO_TEMP(priv, tz, avg_temp);
-+      return 0;
-+}
-+
-+static int airoha_thermal_set_trips(struct thermal_zone_device *tz, int low,
-+                                  int high)
-+{
-+      struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz);
-+
-+      if (high != INT_MAX) {
-+              /* Validate high and clamp them a sane value */
-+              if (high > RAW_TO_TEMP(priv, tz, FIELD_MAX(EN7581_DOUT_TADC_MASK)))
-+                      high = 110000;
-+
-+              /* We offset the high temp of 1°C to trigger correct event */
-+              writel(TEMP_TO_RAW(priv, tz, high) >> 4,
-+                     priv->base + EN7581_TEMPOFFSETH);
-+      }
-+
-+      if (low != -INT_MAX) {
-+              /* Validate low and clamp them to a sane value */
-+              if (low < RAW_TO_TEMP(priv, tz, 0))
-+                      low = -33000;
-+
-+              /* We offset the low temp of 1°C to trigger correct event */
-+              writel(TEMP_TO_RAW(priv, tz, low) >> 4,
-+                     priv->base + EN7581_TEMPOFFSETL);
-+      }
-+
-+      /* Enable sensor 0 monitor */
-+      writel(EN7581_SENSE0_EN, priv->base + EN7581_TEMPMONCTL0);
-+
-+      return 0;
-+}
-+
-+static const struct thermal_zone_device_ops thdev_ops = {
-+      .get_temp = airoha_thermal_get_temp,
-+      .set_trips = airoha_thermal_set_trips,
-+};
-+
-+static irqreturn_t airoha_thermal_irq(int irq, void *data)
-+{
-+      struct airoha_thermal_priv *priv = data;
-+      enum thermal_notify_event event;
-+      u32 status;
-+
-+      status = readl(priv->base + EN7581_TEMPMONINTSTS);
-+      switch (status & (EN7581_HOFSINTSTS0 | EN7581_LOFSINTSTS0)) {
-+      case EN7581_HOFSINTSTS0:
-+              event = THERMAL_TRIP_VIOLATED;
-+              break;
-+      case EN7581_LOFSINTSTS0:
-+              event = THERMAL_EVENT_UNSPECIFIED;
-+              break;
-+      default:
-+              goto exit;
-+      }
-+
-+      thermal_zone_device_update(priv->tz, event);
-+
-+exit:
-+      /* reset interrupt */
-+      writel(status, priv->base + EN7581_TEMPMONINTSTS);
-+
-+      return IRQ_HANDLED;
-+}
-+
-+static void airoha_thermal_setup_adc_val(struct device *dev,
-+                                       struct airoha_thermal_priv *priv,
-+                                       struct thermal_zone_params *tzp)
-+{
-+      u32 efuse_calib_info, cpu_sensor;
-+
-+      /* Setup thermal sensor to ADC mode and setup the mux to DIODE1 */
-+      airoha_init_thermal_ADC_mode(priv);
-+      /* sleep 10 ms for ADC to enable */
-+      usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC);
-+
-+      efuse_calib_info = readl(priv->base + EN7581_EFUSE_TEMP_OFFSET_REG);
-+      if (efuse_calib_info) {
-+              tzp->offset = FIELD_GET(EN7581_EFUSE_TEMP_OFFSET, efuse_calib_info);
-+              /* Different slope are applied if the sensor is used for CPU or for package */
-+              cpu_sensor = readl(priv->base + EN7581_EFUSE_TEMP_CPU_SENSOR_REG);
-+              if (cpu_sensor) {
-+                      tzp->slope = EN7581_SLOPE_X100_DIO_DEFAULT;
-+                      priv->init_temp = EN7581_INIT_TEMP_FTK_X10;
-+              } else {
-+                      tzp->slope = EN7581_SLOPE_X100_DIO_AVS;
-+                      priv->init_temp = EN7581_INIT_TEMP_CPK_X10;
-+              }
-+      } else {
-+              tzp->offset = airoha_get_thermal_ADC(priv);
-+              tzp->slope = EN7581_SLOPE_X100_DIO_DEFAULT;
-+              priv->init_temp = EN7581_INIT_TEMP_NONK_X10;
-+              dev_info(dev, "missing thermal calibrarion EFUSE, using non calibrated value\n");
-+      }
-+}
-+
-+static void airoha_thermal_setup_monitor(struct airoha_thermal_priv *priv)
-+{
-+      /* Set measure mode */
-+      writel(FIELD_PREP(EN7581_MSRCTL0, EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4),
-+             priv->base + EN7581_TEMPMSRCTL0);
-+
-+      /*
-+       * Configure ADC valid reading addr
-+       * The AHB temp monitor system doesn't have direct access to the
-+       * thermal sensor. It does instead work by providing all kind of
-+       * address to configure how to access and setup an ADC for the
-+       * sensor. EN7581 supports only one sensor hence the
-+       * implementation is greatly simplified but the AHB supports
-+       * up to 4 different sensor from the same ADC that can be
-+       * switched by tuning the ADC mux or wiriting address.
-+       *
-+       * We set valid instead of volt as we don't enable valid/volt
-+       * split reading and AHB read valid addr in such case.
-+       */
-+      writel(priv->scu_adc_res.start + EN7581_DOUT_TADC,
-+             priv->base + EN7581_TEMPADCVALIDADDR);
-+
-+      /*
-+       * Configure valid bit on a fake value of bit 16. The ADC outputs
-+       * max of 2 bytes for voltage.
-+       */
-+      writel(FIELD_PREP(EN7581_ADV_RD_VALID_POS, 16),
-+             priv->base + EN7581_TEMPADCVALIDMASK);
-+
-+      /*
-+       * AHB supports max 12 bytes for ADC voltage. Shift the read
-+       * value 4 bit to the right. Precision lost by this is minimal
-+       * in the order of half a °C and is acceptable in the context
-+       * of triggering interrupt in critical condition.
-+       */
-+      writel(FIELD_PREP(EN7581_ADC_VOLTAGE_SHIFT, 4),
-+             priv->base + EN7581_TEMPADCVOLTAGESHIFT);
-+
-+      /* BUS clock is 300MHz counting unit is 3 * 68.64 * 256 = 52.715us */
-+      writel(FIELD_PREP(EN7581_PERIOD_UNIT, 3),
-+             priv->base + EN7581_TEMPMONCTL1);
-+
-+      /*
-+       * filt interval is 1 * 52.715us = 52.715us,
-+       * sen interval is 379 * 52.715us = 19.97ms
-+       */
-+      writel(FIELD_PREP(EN7581_FILT_INTERVAL, 1) |
-+             FIELD_PREP(EN7581_FILT_INTERVAL, 379),
-+             priv->base + EN7581_TEMPMONCTL2);
-+
-+      /* AHB poll is set to 146 * 68.64 = 10.02us */
-+      writel(FIELD_PREP(EN7581_ADC_POLL_INTVL, 146),
-+             priv->base + EN7581_TEMPAHBPOLL);
-+}
-+
-+static int airoha_thermal_probe(struct platform_device *pdev)
-+{
-+      struct thermal_zone_params tzp = { };
-+      struct airoha_thermal_priv *priv;
-+      struct device_node *chip_scu_np;
-+      struct device *dev = &pdev->dev;
-+      int irq, ret;
-+
-+      priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-+      if (!priv)
-+              return -ENOMEM;
-+
-+      priv->base = devm_platform_ioremap_resource(pdev, 0);
-+      if (IS_ERR(priv->base))
-+              return PTR_ERR(priv->base);
-+
-+      chip_scu_np = of_parse_phandle(dev->of_node, "airoha,chip-scu", 0);
-+      if (!chip_scu_np)
-+              return -EINVAL;
-+
-+      priv->chip_scu = syscon_node_to_regmap(chip_scu_np);
-+      if (IS_ERR(priv->chip_scu))
-+              return PTR_ERR(priv->chip_scu);
-+
-+      of_address_to_resource(chip_scu_np, 0, &priv->scu_adc_res);
-+      of_node_put(chip_scu_np);
-+
-+      irq = platform_get_irq(pdev, 0);
-+      if (irq < 0)
-+              return irq;
-+
-+      ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
-+                                      airoha_thermal_irq, IRQF_ONESHOT,
-+                                      pdev->name, (void *)priv);
-+      if (ret) {
-+              dev_err(dev, "Can't get interrupt working.\n");
-+              return ret;
-+      }
-+
-+      airoha_thermal_setup_monitor(priv);
-+      airoha_thermal_setup_adc_val(dev, priv, &tzp);
-+
-+      /* register of thermal sensor and get info from DT */
-+      priv->tz = devm_thermal_of_zone_register_with_params(dev, 0, priv,
-+                                                           &thdev_ops,
-+                                                           &tzp);
-+      if (IS_ERR(priv->tz)) {
-+              dev_err(dev, "register thermal zone sensor failed\n");
-+              return PTR_ERR(priv->tz);
-+      }
-+
-+      platform_set_drvdata(pdev, priv);
-+
-+      /* Enable LOW and HIGH interrupt */
-+      writel(EN7581_HOFSINTEN0 | EN7581_LOFSINTEN0,
-+             priv->base + EN7581_TEMPMONINT);
-+
-+      return 0;
-+}
-+
-+static const struct of_device_id airoha_thermal_match[] = {
-+      { .compatible = "airoha,en7581-thermal" },
-+      {},
-+};
-+MODULE_DEVICE_TABLE(of, airoha_thermal_match);
-+
-+static struct platform_driver airoha_thermal_driver = {
-+      .driver = {
-+              .name = "airoha-thermal",
-+              .of_match_table = airoha_thermal_match,
-+      },
-+      .probe = airoha_thermal_probe,
-+};
-+
-+module_platform_driver(airoha_thermal_driver);
-+
-+MODULE_AUTHOR("Christian Marangi <[email protected]>");
-+MODULE_DESCRIPTION("Airoha thermal driver");
-+MODULE_LICENSE("GPL");