]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
thermal/drivers/qcom-spmi-temp-alarm: Add support for LITE PMIC peripherals
authorAnjelique Melendez <anjelique.melendez@oss.qualcomm.com>
Thu, 10 Jul 2025 22:45:55 +0000 (15:45 -0700)
committerDaniel Lezcano <daniel.lezcano@linaro.org>
Sun, 13 Jul 2025 16:01:30 +0000 (18:01 +0200)
Add support for TEMP_ALARM LITE PMIC peripherals. This subtype
utilizes a pair of registers to configure a warning interrupt
threshold temperature and an automatic hardware shutdown
threshold temperature.

Co-developed-by: David Collins <david.collins@oss.qualcomm.com>
Signed-off-by: David Collins <david.collins@oss.qualcomm.com>
Signed-off-by: Anjelique Melendez <anjelique.melendez@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20250710224555.3047790-6-anjelique.melendez@oss.qualcomm.com
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
drivers/thermal/qcom/qcom-spmi-temp-alarm.c

index 9fbfd192017daba8185d22ed951c2e538b06db0b..f39ca0ddd17b6bc11baf81b4b812838c5de4d653 100644 (file)
@@ -23,6 +23,7 @@
 #define QPNP_TM_REG_TYPE               0x04
 #define QPNP_TM_REG_SUBTYPE            0x05
 #define QPNP_TM_REG_STATUS             0x08
+#define QPNP_TM_REG_IRQ_STATUS         0x10
 #define QPNP_TM_REG_SHUTDOWN_CTRL1     0x40
 #define QPNP_TM_REG_ALARM_CTRL         0x46
 
 #define QPNP_TM_REG_TEMP_DAC_STG1      0x47
 #define QPNP_TM_REG_TEMP_DAC_STG2      0x48
 #define QPNP_TM_REG_TEMP_DAC_STG3      0x49
+#define QPNP_TM_REG_LITE_TEMP_CFG1     0x50
+#define QPNP_TM_REG_LITE_TEMP_CFG2     0x51
 
 #define QPNP_TM_TYPE                   0x09
 #define QPNP_TM_SUBTYPE_GEN1           0x08
 #define QPNP_TM_SUBTYPE_GEN2           0x09
+#define QPNP_TM_SUBTYPE_LITE           0xC0
 
 #define STATUS_GEN1_STAGE_MASK         GENMASK(1, 0)
 #define STATUS_GEN2_STATE_MASK         GENMASK(6, 4)
 
+/* IRQ status only needed for TEMP_ALARM_LITE */
+#define IRQ_STATUS_MASK                        BIT(0)
+
 #define SHUTDOWN_CTRL1_OVERRIDE_STAGE2 BIT(6)
 #define SHUTDOWN_CTRL1_THRESHOLD_MASK  GENMASK(1, 0)
 
@@ -45,6 +52,8 @@
 
 #define ALARM_CTRL_FORCE_ENABLE                BIT(7)
 
+#define LITE_TEMP_CFG_THRESHOLD_MASK   GENMASK(3, 2)
+
 #define THRESH_COUNT                   4
 #define STAGE_COUNT                    3
 
@@ -95,6 +104,19 @@ static const long temp_dac_max[STAGE_COUNT] = {
        119375, 159375, 159375
 };
 
+/*
+ * TEMP_ALARM_LITE has two stages: warning and shutdown with independently
+ * configured threshold temperatures.
+ */
+
+static const long temp_lite_warning_map[THRESH_COUNT] = {
+       115000, 125000, 135000, 145000
+};
+
+static const long temp_lite_shutdown_map[THRESH_COUNT] = {
+       135000, 145000, 160000, 175000
+};
+
 /* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
 #define DEFAULT_TEMP                   37000
 
@@ -202,6 +224,24 @@ static int qpnp_tm_gen2_get_temp_stage(struct qpnp_tm_chip *chip)
        return alarm_state_map[ret];
 }
 
+/**
+ * qpnp_tm_lite_get_temp_stage() - return over-temperature stage
+ * @chip:              Pointer to the qpnp_tm chip
+ *
+ * Return: alarm interrupt state on success, or errno on failure.
+ */
+static int qpnp_tm_lite_get_temp_stage(struct qpnp_tm_chip *chip)
+{
+       u8 reg = 0;
+       int ret;
+
+       ret = qpnp_tm_read(chip, QPNP_TM_REG_IRQ_STATUS, &reg);
+       if (ret < 0)
+               return ret;
+
+       return FIELD_GET(IRQ_STATUS_MASK, reg);
+}
+
 /*
  * This function updates the internal temp value based on the
  * current thermal stage and threshold as well as the previous stage
@@ -383,6 +423,98 @@ static const struct thermal_zone_device_ops qpnp_tm_gen2_rev2_sensor_ops = {
        .set_trip_temp = qpnp_tm_gen2_rev2_set_trip_temp,
 };
 
+static int qpnp_tm_lite_set_temp_thresh(struct qpnp_tm_chip *chip, unsigned int trip, int temp)
+{
+       int ret, temp_cfg, i;
+       const long *temp_map;
+       u8 reg, thresh;
+       u16 addr;
+
+       WARN_ON(!mutex_is_locked(&chip->lock));
+
+       if (trip >= STAGE_COUNT) {
+               dev_err(chip->dev, "invalid TEMP_LITE trip = %d\n", trip);
+               return -EINVAL;
+       }
+
+       switch (trip) {
+       case 0:
+               temp_map = temp_lite_warning_map;
+               addr = QPNP_TM_REG_LITE_TEMP_CFG1;
+               break;
+       case 1:
+               /*
+                * The second trip point is purely in software to facilitate
+                * a controlled shutdown after the warning threshold is crossed
+                * but before the automatic hardware shutdown threshold is
+                * crossed.
+                */
+               return 0;
+       case 2:
+               temp_map = temp_lite_shutdown_map;
+               addr = QPNP_TM_REG_LITE_TEMP_CFG2;
+               break;
+       default:
+               return 0;
+       }
+
+       if (temp < temp_map[THRESH_MIN] || temp > temp_map[THRESH_MAX]) {
+               dev_err(chip->dev, "invalid TEMP_LITE temp = %d\n", temp);
+               return -EINVAL;
+       }
+
+       thresh = 0;
+       temp_cfg = temp_map[thresh];
+       for (i = THRESH_MAX; i >= THRESH_MIN; i--) {
+               if (temp >= temp_map[i]) {
+                       thresh = i;
+                       temp_cfg = temp_map[i];
+                       break;
+               }
+       }
+
+       if (temp_cfg == chip->temp_thresh_map[trip])
+               return 0;
+
+       ret = qpnp_tm_read(chip, addr, &reg);
+       if (ret < 0) {
+               dev_err(chip->dev, "LITE_TEMP_CFG read failed, ret=%d\n", ret);
+               return ret;
+       }
+
+       reg &= ~LITE_TEMP_CFG_THRESHOLD_MASK;
+       reg |= FIELD_PREP(LITE_TEMP_CFG_THRESHOLD_MASK, thresh);
+
+       ret = qpnp_tm_write(chip, addr, reg);
+       if (ret < 0) {
+               dev_err(chip->dev, "LITE_TEMP_CFG write failed, ret=%d\n", ret);
+               return ret;
+       }
+
+       chip->temp_thresh_map[trip] = temp_cfg;
+
+       return 0;
+}
+
+static int qpnp_tm_lite_set_trip_temp(struct thermal_zone_device *tz,
+                                     const struct thermal_trip *trip, int temp)
+{
+       unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv);
+       struct qpnp_tm_chip *chip = thermal_zone_device_priv(tz);
+       int ret;
+
+       mutex_lock(&chip->lock);
+       ret = qpnp_tm_lite_set_temp_thresh(chip, trip_index, temp);
+       mutex_unlock(&chip->lock);
+
+       return ret;
+}
+
+static const struct thermal_zone_device_ops qpnp_tm_lite_sensor_ops = {
+       .get_temp = qpnp_tm_get_temp,
+       .set_trip_temp = qpnp_tm_lite_set_trip_temp,
+};
+
 static irqreturn_t qpnp_tm_isr(int irq, void *data)
 {
        struct qpnp_tm_chip *chip = data;
@@ -478,6 +610,70 @@ static int qpnp_tm_gen2_rev2_sync_thresholds(struct qpnp_tm_chip *chip)
        return 0;
 }
 
+/* Configure TEMP_LITE registers based on DT thermal_zone trips */
+static int qpnp_tm_lite_configure_trip_temps_cb(struct thermal_trip *trip, void *data)
+{
+       struct qpnp_tm_chip *chip = data;
+       int ret;
+
+       mutex_lock(&chip->lock);
+       trip->priv = THERMAL_INT_TO_TRIP_PRIV(chip->ntrips);
+       ret = qpnp_tm_lite_set_temp_thresh(chip, chip->ntrips, trip->temperature);
+       chip->ntrips++;
+       mutex_unlock(&chip->lock);
+
+       return ret;
+}
+
+static int qpnp_tm_lite_configure_trip_temps(struct qpnp_tm_chip *chip)
+{
+       int ret;
+
+       ret = thermal_zone_for_each_trip(chip->tz_dev, qpnp_tm_lite_configure_trip_temps_cb, chip);
+       if (ret < 0)
+               return ret;
+
+       /* Verify that trips are strictly increasing. */
+       if (chip->temp_thresh_map[2] <= chip->temp_thresh_map[0]) {
+               dev_err(chip->dev, "Threshold 2=%ld <= threshold 0=%ld\n",
+                       chip->temp_thresh_map[2], chip->temp_thresh_map[0]);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* Read the hardware default TEMP_LITE stage threshold temperatures */
+static int qpnp_tm_lite_sync_thresholds(struct qpnp_tm_chip *chip)
+{
+       int ret, thresh;
+       u8 reg = 0;
+
+       /*
+        * Store the warning trip temp in temp_thresh_map[0] and the shutdown trip
+        * temp in temp_thresh_map[2].  The second trip point is purely in software
+        * to facilitate a controlled shutdown after the warning threshold is
+        * crossed but before the automatic hardware shutdown threshold is
+        * crossed.  Thus, there is no register to read for the second trip
+        * point.
+        */
+       ret = qpnp_tm_read(chip, QPNP_TM_REG_LITE_TEMP_CFG1, &reg);
+       if (ret < 0)
+               return ret;
+
+       thresh = FIELD_GET(LITE_TEMP_CFG_THRESHOLD_MASK, reg);
+       chip->temp_thresh_map[0] = temp_lite_warning_map[thresh];
+
+       ret = qpnp_tm_read(chip, QPNP_TM_REG_LITE_TEMP_CFG2, &reg);
+       if (ret < 0)
+               return ret;
+
+       thresh = FIELD_GET(LITE_TEMP_CFG_THRESHOLD_MASK, reg);
+       chip->temp_thresh_map[2] = temp_lite_shutdown_map[thresh];
+
+       return 0;
+}
+
 static const struct spmi_temp_alarm_data spmi_temp_alarm_data = {
        .ops = &qpnp_tm_sensor_ops,
        .temp_map = &temp_map_gen1,
@@ -509,6 +705,13 @@ static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_rev2_data = {
        .get_temp_stage = qpnp_tm_gen2_get_temp_stage,
 };
 
+static const struct spmi_temp_alarm_data spmi_temp_alarm_lite_data = {
+       .ops = &qpnp_tm_lite_sensor_ops,
+       .sync_thresholds = qpnp_tm_lite_sync_thresholds,
+       .configure_trip_temps = qpnp_tm_lite_configure_trip_temps,
+       .get_temp_stage = qpnp_tm_lite_get_temp_stage,
+};
+
 /*
  * This function initializes the internal temp value based on only the
  * current thermal stage and threshold.
@@ -614,7 +817,8 @@ static int qpnp_tm_probe(struct platform_device *pdev)
                                     "could not read dig_minor\n");
 
        if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1
-                                    && subtype != QPNP_TM_SUBTYPE_GEN2)) {
+                                    && subtype != QPNP_TM_SUBTYPE_GEN2
+                                    && subtype != QPNP_TM_SUBTYPE_LITE)) {
                dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n",
                        type, subtype);
                return -ENODEV;
@@ -629,6 +833,8 @@ static int qpnp_tm_probe(struct platform_device *pdev)
                chip->data = &spmi_temp_alarm_gen2_rev1_data;
        else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 2)
                chip->data = &spmi_temp_alarm_gen2_rev2_data;
+       else if (subtype == QPNP_TM_SUBTYPE_LITE)
+               chip->data = &spmi_temp_alarm_lite_data;
        else
                return -ENODEV;