summaryrefslogtreecommitdiffstats
path: root/drivers/thermal/airoha_thermal.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thermal/airoha_thermal.c')
-rw-r--r--drivers/thermal/airoha_thermal.c733
1 files changed, 733 insertions, 0 deletions
diff --git a/drivers/thermal/airoha_thermal.c b/drivers/thermal/airoha_thermal.c
new file mode 100644
index 000000000..77b3a1af2
--- /dev/null
+++ b/drivers/thermal/airoha_thermal.c
@@ -0,0 +1,733 @@
+// 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)
+
+#define AN7583_MUX_SENSOR 0x2a0
+#define AN7583_LOAD_ADJ GENMASK(3, 2)
+#define AN7583_MUX_TADC 0x2e4
+#define AN7583_MUX_TADC_MASK GENMASK(3, 1)
+#define AN7583_DOUT_TADC 0x2f0
+
+/* 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 average 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
+
+#define AN7583_SCU_THERMAL_PROTECT_KEY 0x80
+#define AN7583_NUM_SENSOR 3
+
+#define AIROHA_THERMAL_NO_MUX_SENSOR -1
+
+/* 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
+
+/*
+ * AN7583 supports all these ADC mux but the original driver
+ * always checked temp with the AN7583_BGP_TEMP_SENSOR.
+ * Assume using the other sensor temperature is invalid and
+ * always read from AN7583_BGP_TEMP_SENSOR.
+ *
+ * On top of this it's defined that AN7583 supports 3
+ * sensor: AN7583_BGP_TEMP_SENSOR, AN7583_GBE_TEMP_SENSOR,
+ * AN7583_CPU_TEMP_SENSOR.
+ *
+ * Provide the ADC mux for reference.
+ */
+enum an7583_thermal_adc_mux {
+ AN7583_BGP_TEMP_SENSOR,
+ AN7583_PAD_AVS,
+ AN7583_CORE_POWER,
+ AN7583_AVSDAC_OUT,
+ AN7583_VCM,
+ AN7583_GBE_TEMP_SENSOR,
+ AN7583_CPU_TEMP_SENSOR,
+
+ AN7583_ADC_MUX_MAX,
+};
+
+enum an7583_thermal_diode_mux {
+ AN7583_D0_TADC,
+ AN7583_ZERO_TADC,
+ AN7583_D1_TADC,
+};
+
+enum airoha_thermal_chip_scu_field {
+ AIROHA_THERMAL_DOUT_TADC,
+ AIROHA_THERMAL_MUX_SENSOR,
+ AIROHA_THERMAL_MUX_TADC,
+
+ /* keep last */
+ AIROHA_THERMAL_FIELD_MAX,
+};
+
+struct airoha_thermal_priv {
+ struct regmap *map;
+ struct regmap *chip_scu;
+ struct regmap_field *chip_scu_fields[AIROHA_THERMAL_FIELD_MAX];
+ struct resource scu_adc_res;
+
+ u32 pllrg_protect;
+ int current_adc;
+
+ struct thermal_zone_device *tz;
+ int init_temp;
+ int default_slope;
+ int default_offset;
+};
+
+struct airoha_thermal_soc_data {
+ u32 pllrg_protect;
+
+ const struct thermal_zone_device_ops *thdev_ops;
+ int (*probe)(struct platform_device *pdev,
+ struct airoha_thermal_priv *priv);
+ int (*post_probe)(struct platform_device *pdev);
+};
+
+static const unsigned int an7583_thermal_coeff[AN7583_ADC_MUX_MAX] = {
+ [AN7583_BGP_TEMP_SENSOR] = 973,
+ [AN7583_GBE_TEMP_SENSOR] = 995,
+ [AN7583_CPU_TEMP_SENSOR] = 1035,
+};
+
+static const unsigned int an7583_thermal_slope[AN7583_ADC_MUX_MAX] = {
+ [AN7583_BGP_TEMP_SENSOR] = 7440,
+ [AN7583_GBE_TEMP_SENSOR] = 7620,
+ [AN7583_CPU_TEMP_SENSOR] = 8390,
+};
+
+static const unsigned int an7583_thermal_offset[AN7583_ADC_MUX_MAX] = {
+ [AN7583_BGP_TEMP_SENSOR] = 294,
+ [AN7583_GBE_TEMP_SENSOR] = 298,
+ [AN7583_CPU_TEMP_SENSOR] = 344,
+};
+
+static int airoha_get_thermal_ADC(struct airoha_thermal_priv *priv)
+{
+ u32 val;
+
+ regmap_field_read(priv->chip_scu_fields[AIROHA_THERMAL_DOUT_TADC],
+ &val);
+ return val;
+}
+
+static void airoha_set_thermal_mux(struct airoha_thermal_priv *priv,
+ int tdac_idx, int sensor_idx)
+{
+ u32 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,
+ priv->pllrg_protect);
+
+ /*
+ * Configure Thermal Sensor mux to sensor_idx.
+ * (if not supported, sensor_idx is AIROHA_THERMAL_NO_MUX_SENSOR)
+ */
+ if (sensor_idx != AIROHA_THERMAL_NO_MUX_SENSOR)
+ regmap_field_write(priv->chip_scu_fields[AIROHA_THERMAL_MUX_SENSOR],
+ sensor_idx);
+
+ /* Configure Thermal ADC mux to tdac_idx */
+ if (priv->current_adc != tdac_idx) {
+ regmap_field_write(priv->chip_scu_fields[AIROHA_THERMAL_MUX_TADC],
+ tdac_idx);
+ priv->current_adc = tdac_idx;
+ }
+
+ /* Restore PLLRG value on exit */
+ regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, pllrg);
+
+ /* Sleep 10 ms for Thermal ADC to enable */
+ usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC);
+}
+
+static int en7581_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 en7581_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 */
+ regmap_write(priv->map, EN7581_TEMPOFFSETH,
+ TEMP_TO_RAW(priv, high) >> 4);
+
+ enable_monitor = true;
+ }
+
+ if (low != -INT_MAX) {
+ /* Validate low and clamp it to a supported value */
+ low = clamp_t(int, low, 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 */
+ regmap_write(priv->map, EN7581_TEMPOFFSETL,
+ TEMP_TO_RAW(priv, low) >> 4);
+
+ enable_monitor = true;
+ }
+
+ /* Enable sensor 0 monitor after trip are set */
+ if (enable_monitor)
+ regmap_write(priv->map, EN7581_TEMPMONCTL0, EN7581_SENSE0_EN);
+
+ return 0;
+}
+
+static const struct thermal_zone_device_ops en7581_thdev_ops = {
+ .get_temp = en7581_thermal_get_temp,
+ .set_trips = en7581_thermal_set_trips,
+};
+
+static irqreturn_t en7581_thermal_irq(int irq, void *data)
+{
+ struct airoha_thermal_priv *priv = data;
+ enum thermal_notify_event event;
+ bool update = false;
+ u32 status = 0;
+
+ regmap_read(priv->map, EN7581_TEMPMONINTSTS, &status);
+ 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 */
+ regmap_write(priv->map, EN7581_TEMPMONINTSTS, status);
+
+ if (update)
+ thermal_zone_device_update(priv->tz, event);
+
+ return IRQ_HANDLED;
+}
+
+static void en7581_thermal_setup_adc_val(struct device *dev,
+ struct airoha_thermal_priv *priv)
+{
+ u32 efuse_calib_info = 0;
+ u32 cpu_sensor = 0;
+
+ /* Setup Thermal Sensor to ADC mode and setup the mux to DIODE1 */
+ airoha_set_thermal_mux(priv, EN7581_SCU_THERMAL_MUX_DIODE1,
+ AIROHA_THERMAL_NO_MUX_SENSOR);
+
+ regmap_read(priv->map, EN7581_EFUSE_TEMP_OFFSET_REG, &efuse_calib_info);
+ 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 */
+ regmap_read(priv->map, EN7581_EFUSE_TEMP_CPU_SENSOR_REG, &cpu_sensor);
+ 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 calibration EFUSE, using non calibrated value\n");
+ }
+}
+
+static void en7581_thermal_setup_monitor(struct airoha_thermal_priv *priv)
+{
+ /* Set measure mode */
+ regmap_write(priv->map, EN7581_TEMPMSRCTL0,
+ FIELD_PREP(EN7581_MSRCTL0, EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4));
+
+ /*
+ * 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 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 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.
+ */
+ regmap_write(priv->map, EN7581_TEMPADCVALIDADDR,
+ priv->scu_adc_res.start + EN7581_DOUT_TADC);
+
+ /*
+ * Configure valid bit on a fake value of bit 16. The ADC outputs
+ * max of 2 bytes for voltage.
+ */
+ regmap_write(priv->map, EN7581_TEMPADCVALIDMASK,
+ FIELD_PREP(EN7581_ADV_RD_VALID_POS, 16));
+
+ /*
+ * 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.
+ */
+ regmap_write(priv->map, EN7581_TEMPADCVOLTAGESHIFT,
+ FIELD_PREP(EN7581_ADC_VOLTAGE_SHIFT, 4));
+
+ /* BUS clock is 300MHz counting unit is 3 * 68.64 * 256 = 52.715us */
+ regmap_write(priv->map, EN7581_TEMPMONCTL1,
+ FIELD_PREP(EN7581_PERIOD_UNIT, 3));
+
+ /*
+ * filt interval is 1 * 52.715us = 52.715us,
+ * sen interval is 379 * 52.715us = 19.97ms
+ */
+ regmap_write(priv->map, EN7581_TEMPMONCTL2,
+ FIELD_PREP(EN7581_FILT_INTERVAL, 1) |
+ FIELD_PREP(EN7581_SEN_INTERVAL, 379));
+
+ /* AHB poll is set to 146 * 68.64 = 10.02us */
+ regmap_write(priv->map, EN7581_TEMPAHBPOLL,
+ FIELD_PREP(EN7581_ADC_POLL_INTVL, 146));
+}
+
+static const struct regmap_config en7581_thermal_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+};
+
+static const struct reg_field en7581_chip_scu_fields[AIROHA_THERMAL_FIELD_MAX] = {
+ [AIROHA_THERMAL_DOUT_TADC] = REG_FIELD(EN7581_DOUT_TADC, 0, 15),
+ [AIROHA_THERMAL_MUX_TADC] = REG_FIELD(EN7581_PWD_TADC, 1, 3),
+};
+
+static int en7581_thermal_probe(struct platform_device *pdev,
+ struct airoha_thermal_priv *priv)
+{
+ struct device_node *chip_scu_np;
+ struct device *dev = &pdev->dev;
+ void __iomem *base;
+ int i, irq, ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->map = devm_regmap_init_mmio(dev, base,
+ &en7581_thermal_regmap_config);
+ if (IS_ERR(priv->map))
+ return PTR_ERR(priv->map);
+
+ 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);
+
+ for (i = 0; i < AIROHA_THERMAL_FIELD_MAX; i++) {
+ struct regmap_field *field;
+
+ /* Skip registering MUX_SENSOR field as not supported */
+ if (i == AIROHA_THERMAL_MUX_SENSOR)
+ continue;
+
+ field = devm_regmap_field_alloc(dev, priv->chip_scu,
+ en7581_chip_scu_fields[i]);
+ if (IS_ERR(field)) {
+ of_node_put(chip_scu_np);
+ return PTR_ERR(field);
+ }
+
+ priv->chip_scu_fields[i] = field;
+ }
+
+ 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,
+ en7581_thermal_irq, IRQF_ONESHOT,
+ pdev->name, priv);
+ if (ret)
+ return ret;
+
+ en7581_thermal_setup_monitor(priv);
+ en7581_thermal_setup_adc_val(dev, priv);
+
+ return 0;
+}
+
+static int en7581_thermal_post_probe(struct platform_device *pdev)
+{
+ struct airoha_thermal_priv *priv = platform_get_drvdata(pdev);
+
+ /* Enable LOW and HIGH interrupt (if supported) */
+ regmap_write(priv->map, EN7581_TEMPMONINT,
+ EN7581_HOFSINTEN0 | EN7581_LOFSINTEN0);
+
+ return 0;
+}
+
+static int an7583_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+ struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz);
+ int sensor_idx;
+ int delta_diode, delta_gain;
+ int coeff, slope, offset;
+
+ int diode_zero, diode_d0, diode_d1;
+
+ /* Always read sensor AN7583_BGP_TEMP_SENSOR */
+ sensor_idx = AN7583_BGP_TEMP_SENSOR;
+
+ coeff = an7583_thermal_coeff[sensor_idx];
+ slope = an7583_thermal_slope[sensor_idx];
+ offset = an7583_thermal_offset[sensor_idx];
+
+ airoha_set_thermal_mux(priv, AN7583_ZERO_TADC, sensor_idx);
+ diode_zero = airoha_get_thermal_ADC(priv);
+ airoha_set_thermal_mux(priv, AN7583_D0_TADC, sensor_idx);
+ diode_d0 = airoha_get_thermal_ADC(priv);
+ airoha_set_thermal_mux(priv, AN7583_D1_TADC, sensor_idx);
+ diode_d1 = airoha_get_thermal_ADC(priv);
+
+ delta_diode = diode_d1 - diode_d0;
+ delta_gain = (delta_diode * coeff) / 100 + (diode_zero - diode_d1);
+ if (!delta_gain)
+ return -EINVAL;
+
+ *temp = (slope * delta_diode * 10) / delta_gain - offset * 10;
+ *temp *= 100;
+
+ return 0;
+}
+
+static const struct thermal_zone_device_ops an7583_tz_ops = {
+ .get_temp = an7583_thermal_get_temp,
+};
+
+static const struct reg_field an7583_chip_scu_fields[AIROHA_THERMAL_FIELD_MAX] = {
+ [AIROHA_THERMAL_DOUT_TADC] = REG_FIELD(AN7583_DOUT_TADC, 0, 31),
+ [AIROHA_THERMAL_MUX_TADC] = REG_FIELD(AN7583_MUX_TADC, 1, 3),
+ [AIROHA_THERMAL_MUX_SENSOR] = REG_FIELD(AN7583_MUX_SENSOR, 2, 3),
+};
+
+static int an7583_thermal_probe(struct platform_device *pdev,
+ struct airoha_thermal_priv *priv)
+{
+ struct device *dev = &pdev->dev;
+ int i;
+
+ priv->chip_scu = device_node_to_regmap(dev->of_node);
+ if (IS_ERR(priv->chip_scu))
+ return PTR_ERR(priv->chip_scu);
+
+ for (i = 0; i < AIROHA_THERMAL_FIELD_MAX; i++) {
+ struct regmap_field *field;
+
+ field = devm_regmap_field_alloc(dev, priv->chip_scu,
+ an7583_chip_scu_fields[i]);
+ if (IS_ERR(field))
+ return PTR_ERR(field);
+
+ priv->chip_scu_fields[i] = field;
+ }
+
+ return 0;
+}
+
+static int airoha_thermal_probe(struct platform_device *pdev)
+{
+ const struct airoha_thermal_soc_data *soc_data;
+ struct airoha_thermal_priv *priv;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ soc_data = device_get_match_data(dev);
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->pllrg_protect = soc_data->pllrg_protect;
+ priv->current_adc = -1;
+
+ if (!soc_data->probe)
+ return -EINVAL;
+
+ ret = soc_data->probe(pdev, priv);
+ if (ret)
+ return ret;
+
+ /* register of thermal sensor and get info from DT */
+ priv->tz = devm_thermal_of_zone_register(dev, 0, priv,
+ soc_data->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);
+
+ return soc_data->post_probe ? soc_data->post_probe(pdev) : 0;
+}
+
+static const struct airoha_thermal_soc_data en7581_data = {
+ .pllrg_protect = EN7581_SCU_THERMAL_PROTECT_KEY,
+ .thdev_ops = &en7581_thdev_ops,
+ .probe = &en7581_thermal_probe,
+ .post_probe = &en7581_thermal_post_probe,
+};
+
+static const struct airoha_thermal_soc_data an7583_data = {
+ .pllrg_protect = AN7583_SCU_THERMAL_PROTECT_KEY,
+ .thdev_ops = &an7583_tz_ops,
+ .probe = &an7583_thermal_probe,
+};
+
+static const struct of_device_id airoha_thermal_match[] = {
+ { .compatible = "airoha,en7581-thermal", .data = &en7581_data },
+ { .compatible = "airoha,an7583-chip-scu", .data = &an7583_data },
+ {},
+};
+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 <ansuelsmth@gmail.com>");
+MODULE_DESCRIPTION("Airoha thermal driver");
+MODULE_LICENSE("GPL");