Skip to content

Commit 97d4d77

Browse files
Anjelique Melendezdlezcano
authored andcommitted
thermal/drivers/qcom-spmi-temp-alarm: Add support for LITE PMIC peripherals
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>
1 parent 348e104 commit 97d4d77

1 file changed

Lines changed: 207 additions & 1 deletion

File tree

drivers/thermal/qcom/qcom-spmi-temp-alarm.c

Lines changed: 207 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,37 @@
2323
#define QPNP_TM_REG_TYPE 0x04
2424
#define QPNP_TM_REG_SUBTYPE 0x05
2525
#define QPNP_TM_REG_STATUS 0x08
26+
#define QPNP_TM_REG_IRQ_STATUS 0x10
2627
#define QPNP_TM_REG_SHUTDOWN_CTRL1 0x40
2728
#define QPNP_TM_REG_ALARM_CTRL 0x46
2829

2930
/* TEMP_DAC_STGx registers are only present for TEMP_GEN2 v2.0 */
3031
#define QPNP_TM_REG_TEMP_DAC_STG1 0x47
3132
#define QPNP_TM_REG_TEMP_DAC_STG2 0x48
3233
#define QPNP_TM_REG_TEMP_DAC_STG3 0x49
34+
#define QPNP_TM_REG_LITE_TEMP_CFG1 0x50
35+
#define QPNP_TM_REG_LITE_TEMP_CFG2 0x51
3336

3437
#define QPNP_TM_TYPE 0x09
3538
#define QPNP_TM_SUBTYPE_GEN1 0x08
3639
#define QPNP_TM_SUBTYPE_GEN2 0x09
40+
#define QPNP_TM_SUBTYPE_LITE 0xC0
3741

3842
#define STATUS_GEN1_STAGE_MASK GENMASK(1, 0)
3943
#define STATUS_GEN2_STATE_MASK GENMASK(6, 4)
4044

45+
/* IRQ status only needed for TEMP_ALARM_LITE */
46+
#define IRQ_STATUS_MASK BIT(0)
47+
4148
#define SHUTDOWN_CTRL1_OVERRIDE_STAGE2 BIT(6)
4249
#define SHUTDOWN_CTRL1_THRESHOLD_MASK GENMASK(1, 0)
4350

4451
#define SHUTDOWN_CTRL1_RATE_25HZ BIT(3)
4552

4653
#define ALARM_CTRL_FORCE_ENABLE BIT(7)
4754

55+
#define LITE_TEMP_CFG_THRESHOLD_MASK GENMASK(3, 2)
56+
4857
#define THRESH_COUNT 4
4958
#define STAGE_COUNT 3
5059

@@ -95,6 +104,19 @@ static const long temp_dac_max[STAGE_COUNT] = {
95104
119375, 159375, 159375
96105
};
97106

107+
/*
108+
* TEMP_ALARM_LITE has two stages: warning and shutdown with independently
109+
* configured threshold temperatures.
110+
*/
111+
112+
static const long temp_lite_warning_map[THRESH_COUNT] = {
113+
115000, 125000, 135000, 145000
114+
};
115+
116+
static const long temp_lite_shutdown_map[THRESH_COUNT] = {
117+
135000, 145000, 160000, 175000
118+
};
119+
98120
/* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
99121
#define DEFAULT_TEMP 37000
100122

@@ -202,6 +224,24 @@ static int qpnp_tm_gen2_get_temp_stage(struct qpnp_tm_chip *chip)
202224
return alarm_state_map[ret];
203225
}
204226

227+
/**
228+
* qpnp_tm_lite_get_temp_stage() - return over-temperature stage
229+
* @chip: Pointer to the qpnp_tm chip
230+
*
231+
* Return: alarm interrupt state on success, or errno on failure.
232+
*/
233+
static int qpnp_tm_lite_get_temp_stage(struct qpnp_tm_chip *chip)
234+
{
235+
u8 reg = 0;
236+
int ret;
237+
238+
ret = qpnp_tm_read(chip, QPNP_TM_REG_IRQ_STATUS, &reg);
239+
if (ret < 0)
240+
return ret;
241+
242+
return FIELD_GET(IRQ_STATUS_MASK, reg);
243+
}
244+
205245
/*
206246
* This function updates the internal temp value based on the
207247
* 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 = {
383423
.set_trip_temp = qpnp_tm_gen2_rev2_set_trip_temp,
384424
};
385425

426+
static int qpnp_tm_lite_set_temp_thresh(struct qpnp_tm_chip *chip, unsigned int trip, int temp)
427+
{
428+
int ret, temp_cfg, i;
429+
const long *temp_map;
430+
u8 reg, thresh;
431+
u16 addr;
432+
433+
WARN_ON(!mutex_is_locked(&chip->lock));
434+
435+
if (trip >= STAGE_COUNT) {
436+
dev_err(chip->dev, "invalid TEMP_LITE trip = %d\n", trip);
437+
return -EINVAL;
438+
}
439+
440+
switch (trip) {
441+
case 0:
442+
temp_map = temp_lite_warning_map;
443+
addr = QPNP_TM_REG_LITE_TEMP_CFG1;
444+
break;
445+
case 1:
446+
/*
447+
* The second trip point is purely in software to facilitate
448+
* a controlled shutdown after the warning threshold is crossed
449+
* but before the automatic hardware shutdown threshold is
450+
* crossed.
451+
*/
452+
return 0;
453+
case 2:
454+
temp_map = temp_lite_shutdown_map;
455+
addr = QPNP_TM_REG_LITE_TEMP_CFG2;
456+
break;
457+
default:
458+
return 0;
459+
}
460+
461+
if (temp < temp_map[THRESH_MIN] || temp > temp_map[THRESH_MAX]) {
462+
dev_err(chip->dev, "invalid TEMP_LITE temp = %d\n", temp);
463+
return -EINVAL;
464+
}
465+
466+
thresh = 0;
467+
temp_cfg = temp_map[thresh];
468+
for (i = THRESH_MAX; i >= THRESH_MIN; i--) {
469+
if (temp >= temp_map[i]) {
470+
thresh = i;
471+
temp_cfg = temp_map[i];
472+
break;
473+
}
474+
}
475+
476+
if (temp_cfg == chip->temp_thresh_map[trip])
477+
return 0;
478+
479+
ret = qpnp_tm_read(chip, addr, &reg);
480+
if (ret < 0) {
481+
dev_err(chip->dev, "LITE_TEMP_CFG read failed, ret=%d\n", ret);
482+
return ret;
483+
}
484+
485+
reg &= ~LITE_TEMP_CFG_THRESHOLD_MASK;
486+
reg |= FIELD_PREP(LITE_TEMP_CFG_THRESHOLD_MASK, thresh);
487+
488+
ret = qpnp_tm_write(chip, addr, reg);
489+
if (ret < 0) {
490+
dev_err(chip->dev, "LITE_TEMP_CFG write failed, ret=%d\n", ret);
491+
return ret;
492+
}
493+
494+
chip->temp_thresh_map[trip] = temp_cfg;
495+
496+
return 0;
497+
}
498+
499+
static int qpnp_tm_lite_set_trip_temp(struct thermal_zone_device *tz,
500+
const struct thermal_trip *trip, int temp)
501+
{
502+
unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv);
503+
struct qpnp_tm_chip *chip = thermal_zone_device_priv(tz);
504+
int ret;
505+
506+
mutex_lock(&chip->lock);
507+
ret = qpnp_tm_lite_set_temp_thresh(chip, trip_index, temp);
508+
mutex_unlock(&chip->lock);
509+
510+
return ret;
511+
}
512+
513+
static const struct thermal_zone_device_ops qpnp_tm_lite_sensor_ops = {
514+
.get_temp = qpnp_tm_get_temp,
515+
.set_trip_temp = qpnp_tm_lite_set_trip_temp,
516+
};
517+
386518
static irqreturn_t qpnp_tm_isr(int irq, void *data)
387519
{
388520
struct qpnp_tm_chip *chip = data;
@@ -478,6 +610,70 @@ static int qpnp_tm_gen2_rev2_sync_thresholds(struct qpnp_tm_chip *chip)
478610
return 0;
479611
}
480612

613+
/* Configure TEMP_LITE registers based on DT thermal_zone trips */
614+
static int qpnp_tm_lite_configure_trip_temps_cb(struct thermal_trip *trip, void *data)
615+
{
616+
struct qpnp_tm_chip *chip = data;
617+
int ret;
618+
619+
mutex_lock(&chip->lock);
620+
trip->priv = THERMAL_INT_TO_TRIP_PRIV(chip->ntrips);
621+
ret = qpnp_tm_lite_set_temp_thresh(chip, chip->ntrips, trip->temperature);
622+
chip->ntrips++;
623+
mutex_unlock(&chip->lock);
624+
625+
return ret;
626+
}
627+
628+
static int qpnp_tm_lite_configure_trip_temps(struct qpnp_tm_chip *chip)
629+
{
630+
int ret;
631+
632+
ret = thermal_zone_for_each_trip(chip->tz_dev, qpnp_tm_lite_configure_trip_temps_cb, chip);
633+
if (ret < 0)
634+
return ret;
635+
636+
/* Verify that trips are strictly increasing. */
637+
if (chip->temp_thresh_map[2] <= chip->temp_thresh_map[0]) {
638+
dev_err(chip->dev, "Threshold 2=%ld <= threshold 0=%ld\n",
639+
chip->temp_thresh_map[2], chip->temp_thresh_map[0]);
640+
return -EINVAL;
641+
}
642+
643+
return 0;
644+
}
645+
646+
/* Read the hardware default TEMP_LITE stage threshold temperatures */
647+
static int qpnp_tm_lite_sync_thresholds(struct qpnp_tm_chip *chip)
648+
{
649+
int ret, thresh;
650+
u8 reg = 0;
651+
652+
/*
653+
* Store the warning trip temp in temp_thresh_map[0] and the shutdown trip
654+
* temp in temp_thresh_map[2]. The second trip point is purely in software
655+
* to facilitate a controlled shutdown after the warning threshold is
656+
* crossed but before the automatic hardware shutdown threshold is
657+
* crossed. Thus, there is no register to read for the second trip
658+
* point.
659+
*/
660+
ret = qpnp_tm_read(chip, QPNP_TM_REG_LITE_TEMP_CFG1, &reg);
661+
if (ret < 0)
662+
return ret;
663+
664+
thresh = FIELD_GET(LITE_TEMP_CFG_THRESHOLD_MASK, reg);
665+
chip->temp_thresh_map[0] = temp_lite_warning_map[thresh];
666+
667+
ret = qpnp_tm_read(chip, QPNP_TM_REG_LITE_TEMP_CFG2, &reg);
668+
if (ret < 0)
669+
return ret;
670+
671+
thresh = FIELD_GET(LITE_TEMP_CFG_THRESHOLD_MASK, reg);
672+
chip->temp_thresh_map[2] = temp_lite_shutdown_map[thresh];
673+
674+
return 0;
675+
}
676+
481677
static const struct spmi_temp_alarm_data spmi_temp_alarm_data = {
482678
.ops = &qpnp_tm_sensor_ops,
483679
.temp_map = &temp_map_gen1,
@@ -509,6 +705,13 @@ static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_rev2_data = {
509705
.get_temp_stage = qpnp_tm_gen2_get_temp_stage,
510706
};
511707

708+
static const struct spmi_temp_alarm_data spmi_temp_alarm_lite_data = {
709+
.ops = &qpnp_tm_lite_sensor_ops,
710+
.sync_thresholds = qpnp_tm_lite_sync_thresholds,
711+
.configure_trip_temps = qpnp_tm_lite_configure_trip_temps,
712+
.get_temp_stage = qpnp_tm_lite_get_temp_stage,
713+
};
714+
512715
/*
513716
* This function initializes the internal temp value based on only the
514717
* current thermal stage and threshold.
@@ -614,7 +817,8 @@ static int qpnp_tm_probe(struct platform_device *pdev)
614817
"could not read dig_minor\n");
615818

616819
if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1
617-
&& subtype != QPNP_TM_SUBTYPE_GEN2)) {
820+
&& subtype != QPNP_TM_SUBTYPE_GEN2
821+
&& subtype != QPNP_TM_SUBTYPE_LITE)) {
618822
dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n",
619823
type, subtype);
620824
return -ENODEV;
@@ -629,6 +833,8 @@ static int qpnp_tm_probe(struct platform_device *pdev)
629833
chip->data = &spmi_temp_alarm_gen2_rev1_data;
630834
else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 2)
631835
chip->data = &spmi_temp_alarm_gen2_rev2_data;
836+
else if (subtype == QPNP_TM_SUBTYPE_LITE)
837+
chip->data = &spmi_temp_alarm_lite_data;
632838
else
633839
return -ENODEV;
634840

0 commit comments

Comments
 (0)