All of lore.kernel.org
 help / color / mirror / Atom feed
* [RESEND PATCH v3 0/3] thermal: qcom-spmi-temp-alarm: add support for new TEMP_ALARM subtypes
@ 2023-01-24 23:46 David Collins
  2023-01-24 23:46 ` [RESEND PATCH v3 1/3] thermal: qcom-spmi-temp-alarm: enable stage 2 shutdown when required David Collins
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: David Collins @ 2023-01-24 23:46 UTC (permalink / raw)
  To: Daniel Lezcano, Rafael J . Wysocki, Amit Kucheria,
	Thara Gopinath, Andy Gross, Bjorn Andersson
  Cc: David Collins, Konrad Dybcio, Zhang Rui, linux-arm-msm, linux-pm,
	linux-kernel

Add support in the qcom-spmi-temp-alarm driver for the new PMIC
TEMP_ALARM peripheral subtypes: GEN2 rev 2 and LITE.  The GEN2 rev 2
subtype provides greater flexibility in temperature threshold
specification by using an independent register value to configure
each of the three thresholds.  The LITE subtype utilizes a simplified
set of control registers to configure two thresholds: warning and
shutdown.

Also add support to avoid a potential issue on certain versions of
the TEMP_ALARM GEN2 subtype when automatic stage 2 partial shutdown
is disabled.

Changes since v2 [1]:
* Added missing header <linux/bitfield.h> in the third patch

Changes since v1 [2]:
* Updated the thermal API usage in the patches to work with the recent commit
  ca1b9a9eb3fd ("thermal/drivers/qcom: Switch to new of API")

[1]: https://lore.kernel.org/lkml/cover.1670375556.git.quic_collinsd@quicinc.com/
[2]: https://lore.kernel.org/lkml/cover.1663282895.git.quic_collinsd@quicinc.com/

David Collins (3):
  thermal: qcom-spmi-temp-alarm: enable stage 2 shutdown when required
  thermal: qcom-spmi-temp-alarm: add support for GEN2 rev 2 PMIC
    peripherals
  thermal: qcom-spmi-temp-alarm: add support for LITE PMIC peripherals

 drivers/thermal/qcom/qcom-spmi-temp-alarm.c | 407 +++++++++++++++++++-
 1 file changed, 392 insertions(+), 15 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RESEND PATCH v3 1/3] thermal: qcom-spmi-temp-alarm: enable stage 2 shutdown when required
  2023-01-24 23:46 [RESEND PATCH v3 0/3] thermal: qcom-spmi-temp-alarm: add support for new TEMP_ALARM subtypes David Collins
@ 2023-01-24 23:46 ` David Collins
  2023-03-21  0:57   ` Dmitry Baryshkov
  2023-01-24 23:46 ` [RESEND PATCH v3 2/3] thermal: qcom-spmi-temp-alarm: add support for GEN2 rev 2 PMIC peripherals David Collins
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 7+ messages in thread
From: David Collins @ 2023-01-24 23:46 UTC (permalink / raw)
  To: Daniel Lezcano, Rafael J . Wysocki, Amit Kucheria,
	Thara Gopinath, Andy Gross, Bjorn Andersson
  Cc: David Collins, Konrad Dybcio, Zhang Rui, linux-arm-msm, linux-pm,
	linux-kernel

Certain TEMP_ALARM GEN2 PMIC peripherals need over-temperature
stage 2 automatic PMIC partial shutdown to be enabled in order to
avoid repeated faults in the event of reaching over-temperature
stage 3.  Modify the stage 2 shutdown control logic to ensure that
stage 2 shutdown is enabled on all affected PMICs.  Read the
digital major and minor revision registers to identify these
PMICs.

Signed-off-by: David Collins <quic_collinsd@quicinc.com>
---
 drivers/thermal/qcom/qcom-spmi-temp-alarm.c | 32 +++++++++++++++++++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
index ad84978109e6..e2e52703ac4d 100644
--- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
+++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2011-2015, 2017, 2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/bitops.h>
@@ -18,6 +19,7 @@
 #include "../thermal_core.h"
 #include "../thermal_hwmon.h"
 
+#define QPNP_TM_REG_DIG_MINOR		0x00
 #define QPNP_TM_REG_DIG_MAJOR		0x01
 #define QPNP_TM_REG_TYPE		0x04
 #define QPNP_TM_REG_SUBTYPE		0x05
@@ -73,6 +75,7 @@ struct qpnp_tm_chip {
 	struct device			*dev;
 	struct thermal_zone_device	*tz_dev;
 	unsigned int			subtype;
+	unsigned int			dig_revision;
 	long				temp;
 	unsigned int			thresh;
 	unsigned int			stage;
@@ -224,6 +227,7 @@ static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
 	long stage2_threshold_min = (*chip->temp_map)[THRESH_MIN][1];
 	long stage2_threshold_max = (*chip->temp_map)[THRESH_MAX][1];
 	bool disable_s2_shutdown = false;
+	bool require_s2_shutdown = false;
 	u8 reg;
 
 	WARN_ON(!mutex_is_locked(&chip->lock));
@@ -256,9 +260,25 @@ static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
 				 temp, stage2_threshold_max, stage2_threshold_max);
 	}
 
+	if (chip->subtype == QPNP_TM_SUBTYPE_GEN2) {
+		/*
+		 * Check if stage 2 automatic partial shutdown must remain
+		 * enabled to avoid potential repeated faults upon reaching
+		 * over-temperature stage 3.
+		 */
+		switch (chip->dig_revision) {
+		case 0x0001:
+		case 0x0002:
+		case 0x0100:
+		case 0x0101:
+			require_s2_shutdown = true;
+			break;
+		}
+	}
+
 skip:
 	reg |= chip->thresh;
-	if (disable_s2_shutdown)
+	if (disable_s2_shutdown && !require_s2_shutdown)
 		reg |= SHUTDOWN_CTRL1_OVERRIDE_S2;
 
 	return qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
@@ -373,7 +393,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
 {
 	struct qpnp_tm_chip *chip;
 	struct device_node *node;
-	u8 type, subtype, dig_major;
+	u8 type, subtype, dig_major, dig_minor;
 	u32 res;
 	int ret, irq;
 
@@ -429,6 +449,14 @@ static int qpnp_tm_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	ret = qpnp_tm_read(chip, QPNP_TM_REG_DIG_MINOR, &dig_minor);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "could not read dig_minor\n");
+		return ret;
+	}
+
+	chip->dig_revision = (dig_major << 8) | dig_minor;
+
 	if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1
 				     && subtype != QPNP_TM_SUBTYPE_GEN2)) {
 		dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n",
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RESEND PATCH v3 2/3] thermal: qcom-spmi-temp-alarm: add support for GEN2 rev 2 PMIC peripherals
  2023-01-24 23:46 [RESEND PATCH v3 0/3] thermal: qcom-spmi-temp-alarm: add support for new TEMP_ALARM subtypes David Collins
  2023-01-24 23:46 ` [RESEND PATCH v3 1/3] thermal: qcom-spmi-temp-alarm: enable stage 2 shutdown when required David Collins
@ 2023-01-24 23:46 ` David Collins
  2023-01-24 23:46 ` [RESEND PATCH v3 3/3] thermal: qcom-spmi-temp-alarm: add support for LITE " David Collins
  2023-03-20 23:51 ` [RESEND PATCH v3 0/3] thermal: qcom-spmi-temp-alarm: add support for new TEMP_ALARM subtypes David Collins
  3 siblings, 0 replies; 7+ messages in thread
From: David Collins @ 2023-01-24 23:46 UTC (permalink / raw)
  To: Daniel Lezcano, Rafael J . Wysocki, Amit Kucheria,
	Thara Gopinath, Andy Gross, Bjorn Andersson
  Cc: David Collins, Konrad Dybcio, Zhang Rui, linux-arm-msm, linux-pm,
	linux-kernel

Add support for TEMP_ALARM GEN2 PMIC peripherals with digital
major revision 2.  This revision utilizes individual registers
to set the threshold temperature for over-temperature stages 1,
2, and 3 instead of a single register to specify a set of
thresholds.

Signed-off-by: David Collins <quic_collinsd@quicinc.com>
---
 drivers/thermal/qcom/qcom-spmi-temp-alarm.c | 162 +++++++++++++++++++-
 1 file changed, 156 insertions(+), 6 deletions(-)

diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
index e2e52703ac4d..1fe8ff7a274e 100644
--- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
+++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
@@ -26,6 +26,10 @@
 #define QPNP_TM_REG_STATUS		0x08
 #define QPNP_TM_REG_SHUTDOWN_CTRL1	0x40
 #define QPNP_TM_REG_ALARM_CTRL		0x46
+/* TEMP_DAC_* registers are only present for TEMP_GEN2 v2.0 */
+#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_TYPE			0x09
 #define QPNP_TM_SUBTYPE_GEN1		0x08
@@ -67,6 +71,25 @@ static const long temp_map_gen2_v1[THRESH_COUNT][STAGE_COUNT] = {
 
 #define TEMP_STAGE_HYSTERESIS		2000
 
+/*
+ * For TEMP_GEN2 v2.0, TEMP_DAC_STG1/2/3 registers are used to set the threshold
+ * for each stage independently.
+ * TEMP_DAC_STG* = 0 --> 80 C
+ * Each 8 step increase in TEMP_DAC_STG* value corresponds to 5 C (5000 mC).
+ */
+#define TEMP_DAC_MIN			80000
+#define TEMP_DAC_SCALE_NUM		8
+#define TEMP_DAC_SCALE_DEN		5000
+
+#define TEMP_DAC_TEMP_TO_REG(temp) \
+	(((temp) - TEMP_DAC_MIN) * TEMP_DAC_SCALE_NUM / TEMP_DAC_SCALE_DEN)
+#define TEMP_DAC_REG_TO_TEMP(reg) \
+	(TEMP_DAC_MIN + (reg) * TEMP_DAC_SCALE_DEN / TEMP_DAC_SCALE_NUM)
+
+static const long temp_dac_max[STAGE_COUNT] = {
+	119375, 159375, 159375
+};
+
 /* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
 #define DEFAULT_TEMP			37000
 
@@ -87,6 +110,9 @@ struct qpnp_tm_chip {
 
 	struct iio_channel		*adc;
 	const long			(*temp_map)[THRESH_COUNT][STAGE_COUNT];
+
+	bool				has_temp_dac;
+	long				temp_dac_map[STAGE_COUNT];
 };
 
 /* This array maps from GEN2 alarm state to GEN1 alarm stage */
@@ -120,6 +146,13 @@ static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data)
  */
 static long qpnp_tm_decode_temp(struct qpnp_tm_chip *chip, unsigned int stage)
 {
+	if (chip->has_temp_dac) {
+		if (stage == 0 || stage > STAGE_COUNT)
+			return 0;
+
+		return chip->temp_dac_map[stage - 1];
+	}
+
 	if (!chip->temp_map || chip->thresh >= THRESH_COUNT || stage == 0 ||
 	    stage > STAGE_COUNT)
 		return 0;
@@ -221,6 +254,34 @@ static int qpnp_tm_get_temp(struct thermal_zone_device *tz, int *temp)
 	return 0;
 }
 
+static int qpnp_tm_set_temp_dac_thresh(struct qpnp_tm_chip *chip, int trip,
+				       int temp)
+{
+	int ret, temp_cfg;
+	u8 reg;
+
+	if (trip < 0 || trip >= STAGE_COUNT) {
+		dev_err(chip->dev, "invalid TEMP_DAC trip = %d\n", trip);
+		return -EINVAL;
+	} else if (temp < TEMP_DAC_MIN || temp > temp_dac_max[trip]) {
+		dev_err(chip->dev, "invalid TEMP_DAC temp = %d\n", temp);
+		return -EINVAL;
+	}
+
+	reg = TEMP_DAC_TEMP_TO_REG(temp);
+	temp_cfg = TEMP_DAC_REG_TO_TEMP(reg);
+
+	ret = qpnp_tm_write(chip, QPNP_TM_REG_TEMP_DAC_STG1 + trip, reg);
+	if (ret < 0) {
+		dev_err(chip->dev, "TEMP_DAC_STG write failed, ret=%d\n", ret);
+		return ret;
+	}
+
+	chip->temp_dac_map[trip] = temp_cfg;
+
+	return 0;
+}
+
 static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
 					     int temp)
 {
@@ -309,6 +370,24 @@ static const struct thermal_zone_device_ops qpnp_tm_sensor_ops = {
 	.set_trip_temp = qpnp_tm_set_trip_temp,
 };
 
+static int qpnp_tm_set_temp_dac_trip_temp(struct thermal_zone_device *tz,
+					  int trip, int temp)
+{
+	struct qpnp_tm_chip *chip = tz->devdata;
+	int ret;
+
+	mutex_lock(&chip->lock);
+	ret = qpnp_tm_set_temp_dac_thresh(chip, trip, temp);
+	mutex_unlock(&chip->lock);
+
+	return ret;
+}
+
+static const struct thermal_zone_device_ops qpnp_tm_sensor_temp_dac_ops = {
+	.get_temp = qpnp_tm_get_temp,
+	.set_trip_temp = qpnp_tm_set_temp_dac_trip_temp,
+};
+
 static irqreturn_t qpnp_tm_isr(int irq, void *data)
 {
 	struct qpnp_tm_chip *chip = data;
@@ -341,6 +420,60 @@ static int qpnp_tm_get_critical_trip_temp(struct qpnp_tm_chip *chip)
 	return THERMAL_TEMP_INVALID;
 }
 
+/* Configure TEMP_DAC registers based on DT thermal_zone trips */
+static int qpnp_tm_temp_dac_update_trip_temps(struct qpnp_tm_chip *chip)
+{
+	const struct thermal_trip *trips;
+	int ret, ntrips, i;
+
+	ntrips = of_thermal_get_ntrips(chip->tz_dev);
+	/* Keep hardware defaults if no DT trips are defined. */
+	if (ntrips <= 0)
+		return 0;
+
+	trips = of_thermal_get_trip_points(chip->tz_dev);
+	if (!trips)
+		return -EINVAL;
+
+	for (i = 0; i < ntrips; i++) {
+		if (of_thermal_is_trip_valid(chip->tz_dev, i)) {
+			ret = qpnp_tm_set_temp_dac_thresh(chip, i,
+							  trips[i].temperature);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	/* Verify that trips are strictly increasing. */
+	for (i = 1; i < STAGE_COUNT; i++) {
+		if (chip->temp_dac_map[i] <= chip->temp_dac_map[i - 1]) {
+			dev_err(chip->dev, "Threshold %d=%ld <= threshold %d=%ld\n",
+				i, chip->temp_dac_map[i], i - 1,
+				chip->temp_dac_map[i - 1]);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/* Read the hardware default TEMP_DAC stage threshold temperatures */
+static int qpnp_tm_temp_dac_init(struct qpnp_tm_chip *chip)
+{
+	int ret, i;
+	u8 reg = 0;
+
+	for (i = 0; i < STAGE_COUNT; i++) {
+		ret = qpnp_tm_read(chip, QPNP_TM_REG_TEMP_DAC_STG1 + i, &reg);
+		if (ret < 0)
+			return ret;
+
+		chip->temp_dac_map[i] = TEMP_DAC_REG_TO_TEMP(reg);
+	}
+
+	return 0;
+}
+
 /*
  * This function initializes the internal temp value based on only the
  * current thermal stage and threshold. Setup threshold control and
@@ -373,10 +506,16 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip)
 	if (stage)
 		chip->temp = qpnp_tm_decode_temp(chip, stage);
 
-	crit_temp = qpnp_tm_get_critical_trip_temp(chip);
-	ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp);
-	if (ret < 0)
-		goto out;
+	if (chip->has_temp_dac) {
+		ret = qpnp_tm_temp_dac_update_trip_temps(chip);
+		if (ret < 0)
+			goto out;
+	} else {
+		crit_temp = qpnp_tm_get_critical_trip_temp(chip);
+		ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp);
+		if (ret < 0)
+			goto out;
+	}
 
 	/* Enable the thermal alarm PMIC module in always-on mode. */
 	reg = ALARM_CTRL_FORCE_ENABLE;
@@ -393,6 +532,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
 {
 	struct qpnp_tm_chip *chip;
 	struct device_node *node;
+	const struct thermal_zone_device_ops *ops;
 	u8 type, subtype, dig_major, dig_minor;
 	u32 res;
 	int ret, irq;
@@ -464,19 +604,29 @@ static int qpnp_tm_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
+	ops = &qpnp_tm_sensor_ops;
 	chip->subtype = subtype;
-	if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 1)
+	if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 2)
+		chip->has_temp_dac = true;
+	else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 1)
 		chip->temp_map = &temp_map_gen2_v1;
 	else
 		chip->temp_map = &temp_map_gen1;
 
+	if (chip->has_temp_dac) {
+		ops = &qpnp_tm_sensor_temp_dac_ops;
+		ret = qpnp_tm_temp_dac_init(chip);
+		if (ret < 0)
+			return ret;
+	}
+
 	/*
 	 * Register the sensor before initializing the hardware to be able to
 	 * read the trip points. get_temp() returns the default temperature
 	 * before the hardware initialization is completed.
 	 */
 	chip->tz_dev = devm_thermal_of_zone_register(
-		&pdev->dev, 0, chip, &qpnp_tm_sensor_ops);
+		&pdev->dev, 0, chip, ops);
 	if (IS_ERR(chip->tz_dev)) {
 		dev_err(&pdev->dev, "failed to register sensor\n");
 		return PTR_ERR(chip->tz_dev);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RESEND PATCH v3 3/3] thermal: qcom-spmi-temp-alarm: add support for LITE PMIC peripherals
  2023-01-24 23:46 [RESEND PATCH v3 0/3] thermal: qcom-spmi-temp-alarm: add support for new TEMP_ALARM subtypes David Collins
  2023-01-24 23:46 ` [RESEND PATCH v3 1/3] thermal: qcom-spmi-temp-alarm: enable stage 2 shutdown when required David Collins
  2023-01-24 23:46 ` [RESEND PATCH v3 2/3] thermal: qcom-spmi-temp-alarm: add support for GEN2 rev 2 PMIC peripherals David Collins
@ 2023-01-24 23:46 ` David Collins
  2023-03-21 15:34   ` Dmitry Baryshkov
  2023-03-20 23:51 ` [RESEND PATCH v3 0/3] thermal: qcom-spmi-temp-alarm: add support for new TEMP_ALARM subtypes David Collins
  3 siblings, 1 reply; 7+ messages in thread
From: David Collins @ 2023-01-24 23:46 UTC (permalink / raw)
  To: Daniel Lezcano, Rafael J . Wysocki, Amit Kucheria,
	Thara Gopinath, Andy Gross, Bjorn Andersson
  Cc: David Collins, Konrad Dybcio, Zhang Rui, linux-arm-msm, linux-pm,
	linux-kernel

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.

Signed-off-by: David Collins <quic_collinsd@quicinc.com>
---
 drivers/thermal/qcom/qcom-spmi-temp-alarm.c | 217 +++++++++++++++++++-
 1 file changed, 208 insertions(+), 9 deletions(-)

diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
index 1fe8ff7a274e..dbf13dec7acd 100644
--- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
+++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
@@ -4,6 +4,7 @@
  * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
+#include <linux/bitfield.h>
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/err.h>
@@ -24,21 +25,28 @@
 #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
 /* TEMP_DAC_* registers are only present for TEMP_GEN2 v2.0 */
 #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)
 #define STATUS_GEN2_STATE_SHIFT		4
 
+/* IRQ status only needed for TEMP_ALARM_LITE */
+#define IRQ_STATUS_MASK			BIT(0)
+
 #define SHUTDOWN_CTRL1_OVERRIDE_S2	BIT(6)
 #define SHUTDOWN_CTRL1_THRESHOLD_MASK	GENMASK(1, 0)
 
@@ -46,6 +54,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
 
@@ -90,6 +100,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_map_lite_warning[THRESH_COUNT] = {
+	115000, 125000, 135000, 145000
+};
+
+static const long temp_map_lite_shutdown[THRESH_COUNT] = {
+	135000, 145000, 160000, 175000
+};
+
 /* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
 #define DEFAULT_TEMP			37000
 
@@ -146,7 +169,7 @@ static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data)
  */
 static long qpnp_tm_decode_temp(struct qpnp_tm_chip *chip, unsigned int stage)
 {
-	if (chip->has_temp_dac) {
+	if (chip->has_temp_dac || chip->subtype == QPNP_TM_SUBTYPE_LITE) {
 		if (stage == 0 || stage > STAGE_COUNT)
 			return 0;
 
@@ -164,19 +187,26 @@ static long qpnp_tm_decode_temp(struct qpnp_tm_chip *chip, unsigned int stage)
  * qpnp_tm_get_temp_stage() - return over-temperature stage
  * @chip:		Pointer to the qpnp_tm chip
  *
- * Return: stage (GEN1) or state (GEN2) on success, or errno on failure.
+ * Return: stage (GEN1), state (GEN2), or alarm interrupt state (LITE) on
+ *	   success; or errno on failure.
  */
 static int qpnp_tm_get_temp_stage(struct qpnp_tm_chip *chip)
 {
 	int ret;
+	u16 addr = QPNP_TM_REG_STATUS;
 	u8 reg = 0;
 
-	ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg);
+	if (chip->subtype == QPNP_TM_SUBTYPE_LITE)
+		addr = QPNP_TM_REG_IRQ_STATUS;
+
+	ret = qpnp_tm_read(chip, addr, &reg);
 	if (ret < 0)
 		return ret;
 
 	if (chip->subtype == QPNP_TM_SUBTYPE_GEN1)
 		ret = reg & STATUS_GEN1_STAGE_MASK;
+	else if (chip->subtype == QPNP_TM_SUBTYPE_LITE)
+		ret = reg & IRQ_STATUS_MASK;
 	else
 		ret = (reg & STATUS_GEN2_STATE_MASK) >> STATUS_GEN2_STATE_SHIFT;
 
@@ -199,7 +229,8 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
 		return ret;
 	stage = ret;
 
-	if (chip->subtype == QPNP_TM_SUBTYPE_GEN1) {
+	if (chip->subtype == QPNP_TM_SUBTYPE_GEN1
+	    || chip->subtype == QPNP_TM_SUBTYPE_LITE) {
 		stage_new = stage;
 		stage_old = chip->stage;
 	} else {
@@ -282,6 +313,78 @@ static int qpnp_tm_set_temp_dac_thresh(struct qpnp_tm_chip *chip, int trip,
 	return 0;
 }
 
+static int qpnp_tm_set_temp_lite_thresh(struct qpnp_tm_chip *chip, int trip,
+				       int temp)
+{
+	int ret, temp_cfg, i;
+	const long *temp_map;
+	u16 addr;
+	u8 reg, thresh;
+
+	if (trip < 0 || trip >= STAGE_COUNT) {
+		dev_err(chip->dev, "invalid TEMP_LITE trip = %d\n", trip);
+		return -EINVAL;
+	}
+
+	switch (trip) {
+	case 0:
+		temp_map = temp_map_lite_warning;
+		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_map_lite_shutdown;
+		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_dac_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_dac_map[trip] = temp_cfg;
+
+	return 0;
+}
+
 static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
 					     int temp)
 {
@@ -388,6 +491,24 @@ static const struct thermal_zone_device_ops qpnp_tm_sensor_temp_dac_ops = {
 	.set_trip_temp = qpnp_tm_set_temp_dac_trip_temp,
 };
 
+static int qpnp_tm_set_temp_lite_trip_temp(struct thermal_zone_device *tz,
+					   int trip, int temp)
+{
+	struct qpnp_tm_chip *chip = tz->devdata;
+	int ret;
+
+	mutex_lock(&chip->lock);
+	ret = qpnp_tm_set_temp_lite_thresh(chip, trip, temp);
+	mutex_unlock(&chip->lock);
+
+	return ret;
+}
+
+static const struct thermal_zone_device_ops qpnp_tm_sensor_temp_lite_ops = {
+	.get_temp = qpnp_tm_get_temp,
+	.set_trip_temp = qpnp_tm_set_temp_lite_trip_temp,
+};
+
 static irqreturn_t qpnp_tm_isr(int irq, void *data)
 {
 	struct qpnp_tm_chip *chip = data;
@@ -474,6 +595,71 @@ static int qpnp_tm_temp_dac_init(struct qpnp_tm_chip *chip)
 	return 0;
 }
 
+/* Configure TEMP_LITE registers based on DT thermal_zone trips */
+static int qpnp_tm_temp_lite_update_trip_temps(struct qpnp_tm_chip *chip)
+{
+	const struct thermal_trip *trips;
+	int ret, ntrips, i;
+
+	ntrips = of_thermal_get_ntrips(chip->tz_dev);
+	/* Keep hardware defaults if no DT trips are defined. */
+	if (ntrips <= 0)
+		return 0;
+
+	trips = of_thermal_get_trip_points(chip->tz_dev);
+	if (!trips)
+		return -EINVAL;
+
+	for (i = 0; i < ntrips; i++) {
+		if (of_thermal_is_trip_valid(chip->tz_dev, i)) {
+			ret = qpnp_tm_set_temp_lite_thresh(chip, i,
+							  trips[i].temperature);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	/* Verify that trips are strictly increasing. */
+	if (chip->temp_dac_map[2] <= chip->temp_dac_map[0]) {
+		dev_err(chip->dev, "Threshold 2=%ld <= threshold 0=%ld\n",
+			chip->temp_dac_map[2], chip->temp_dac_map[0]);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Read the hardware default TEMP_LITE stage threshold temperatures */
+static int qpnp_tm_temp_lite_init(struct qpnp_tm_chip *chip)
+{
+	int ret, thresh;
+	u8 reg = 0;
+
+	/*
+	 * Store the warning trip temp in temp_dac_map[0] and the shutdown trip
+	 * temp in temp_dac_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_dac_map[0] = temp_map_lite_warning[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_dac_map[2] = temp_map_lite_shutdown[thresh];
+
+	return 0;
+}
+
 /*
  * This function initializes the internal temp value based on only the
  * current thermal stage and threshold. Setup threshold control and
@@ -500,13 +686,18 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip)
 		goto out;
 	chip->stage = ret;
 
-	stage = chip->subtype == QPNP_TM_SUBTYPE_GEN1
-		? chip->stage : alarm_state_map[chip->stage];
+	stage = (chip->subtype == QPNP_TM_SUBTYPE_GEN1
+		 || chip->subtype == QPNP_TM_SUBTYPE_LITE)
+			? chip->stage : alarm_state_map[chip->stage];
 
 	if (stage)
 		chip->temp = qpnp_tm_decode_temp(chip, stage);
 
-	if (chip->has_temp_dac) {
+	if (chip->subtype == QPNP_TM_SUBTYPE_LITE) {
+		ret = qpnp_tm_temp_lite_update_trip_temps(chip);
+		if (ret < 0)
+			goto out;
+	} else if (chip->has_temp_dac) {
 		ret = qpnp_tm_temp_dac_update_trip_temps(chip);
 		if (ret < 0)
 			goto out;
@@ -598,7 +789,8 @@ static int qpnp_tm_probe(struct platform_device *pdev)
 	chip->dig_revision = (dig_major << 8) | dig_minor;
 
 	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;
@@ -610,7 +802,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
 		chip->has_temp_dac = true;
 	else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 1)
 		chip->temp_map = &temp_map_gen2_v1;
-	else
+	else if (subtype == QPNP_TM_SUBTYPE_GEN1)
 		chip->temp_map = &temp_map_gen1;
 
 	if (chip->has_temp_dac) {
@@ -620,6 +812,13 @@ static int qpnp_tm_probe(struct platform_device *pdev)
 			return ret;
 	}
 
+	if (chip->subtype == QPNP_TM_SUBTYPE_LITE) {
+		ops = &qpnp_tm_sensor_temp_lite_ops;
+		ret = qpnp_tm_temp_lite_init(chip);
+		if (ret < 0)
+			return ret;
+	}
+
 	/*
 	 * Register the sensor before initializing the hardware to be able to
 	 * read the trip points. get_temp() returns the default temperature
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [RESEND PATCH v3 0/3] thermal: qcom-spmi-temp-alarm: add support for new TEMP_ALARM subtypes
  2023-01-24 23:46 [RESEND PATCH v3 0/3] thermal: qcom-spmi-temp-alarm: add support for new TEMP_ALARM subtypes David Collins
                   ` (2 preceding siblings ...)
  2023-01-24 23:46 ` [RESEND PATCH v3 3/3] thermal: qcom-spmi-temp-alarm: add support for LITE " David Collins
@ 2023-03-20 23:51 ` David Collins
  3 siblings, 0 replies; 7+ messages in thread
From: David Collins @ 2023-03-20 23:51 UTC (permalink / raw)
  To: Amit Kucheria, Thara Gopinath
  Cc: Rafael J . Wysocki, Daniel Lezcano, Andy Gross, Bjorn Andersson,
	Konrad Dybcio, Zhang Rui, linux-arm-msm, linux-pm, linux-kernel

On 1/24/23 15:46, David Collins wrote:
> Add support in the qcom-spmi-temp-alarm driver for the new PMIC
> TEMP_ALARM peripheral subtypes: GEN2 rev 2 and LITE.  The GEN2 rev 2
> subtype provides greater flexibility in temperature threshold
> specification by using an independent register value to configure
> each of the three thresholds.  The LITE subtype utilizes a simplified
> set of control registers to configure two thresholds: warning and
> shutdown.
> 
> Also add support to avoid a potential issue on certain versions of
> the TEMP_ALARM GEN2 subtype when automatic stage 2 partial shutdown
> is disabled.
> 
> Changes since v2 [1]:
> * Added missing header <linux/bitfield.h> in the third patch
> 
> Changes since v1 [2]:
> * Updated the thermal API usage in the patches to work with the recent commit
>   ca1b9a9eb3fd ("thermal/drivers/qcom: Switch to new of API")
> 
> [1]: https://lore.kernel.org/lkml/cover.1670375556.git.quic_collinsd@quicinc.com/
> [2]: https://lore.kernel.org/lkml/cover.1663282895.git.quic_collinsd@quicinc.com/
> 
> David Collins (3):
>   thermal: qcom-spmi-temp-alarm: enable stage 2 shutdown when required
>   thermal: qcom-spmi-temp-alarm: add support for GEN2 rev 2 PMIC
>     peripherals
>   thermal: qcom-spmi-temp-alarm: add support for LITE PMIC peripherals
> 
>  drivers/thermal/qcom/qcom-spmi-temp-alarm.c | 407 +++++++++++++++++++-
>  1 file changed, 392 insertions(+), 15 deletions(-)

Hello Amit and Thara,

Could you please take a look at this patch series when you have some
time?  It hasn't received any feedback yet after being sent out on
1/24/2023.

According to the MAINTAINERS files, you are the maintainers for all
files in the drivers/thermal/qcom/ directory:

QUALCOMM TSENS THERMAL DRIVER
M:	Amit Kucheria <amitk@kernel.org>
M:	Thara Gopinath <thara.gopinath@gmail.com>
L:	linux-pm@vger.kernel.org
L:	linux-arm-msm@vger.kernel.org
S:	Maintained
F:	Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
F:	drivers/thermal/qcom/

Can you please check if this entry needs to be updated?  The
drivers/thermal/qcom directory now contains the qcom-tsens driver as
well as three other independent QCOM thermal drivers:
qcom-spmi-temp-alarm, qcom-spmi-adc-tm5, and lmh.

Thanks,
David


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RESEND PATCH v3 1/3] thermal: qcom-spmi-temp-alarm: enable stage 2 shutdown when required
  2023-01-24 23:46 ` [RESEND PATCH v3 1/3] thermal: qcom-spmi-temp-alarm: enable stage 2 shutdown when required David Collins
@ 2023-03-21  0:57   ` Dmitry Baryshkov
  0 siblings, 0 replies; 7+ messages in thread
From: Dmitry Baryshkov @ 2023-03-21  0:57 UTC (permalink / raw)
  To: David Collins, Daniel Lezcano, Rafael J . Wysocki, Amit Kucheria,
	Thara Gopinath, Andy Gross, Bjorn Andersson
  Cc: Konrad Dybcio, Zhang Rui, linux-arm-msm, linux-pm, linux-kernel

On 25/01/2023 01:46, David Collins wrote:
> Certain TEMP_ALARM GEN2 PMIC peripherals need over-temperature
> stage 2 automatic PMIC partial shutdown to be enabled in order to
> avoid repeated faults in the event of reaching over-temperature
> stage 3.  Modify the stage 2 shutdown control logic to ensure that
> stage 2 shutdown is enabled on all affected PMICs.  Read the
> digital major and minor revision registers to identify these
> PMICs.
> 
> Signed-off-by: David Collins <quic_collinsd@quicinc.com>
> ---
>   drivers/thermal/qcom/qcom-spmi-temp-alarm.c | 32 +++++++++++++++++++--
>   1 file changed, 30 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
> index ad84978109e6..e2e52703ac4d 100644
> --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
> +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
> @@ -1,6 +1,7 @@
>   // SPDX-License-Identifier: GPL-2.0-only
>   /*
>    * Copyright (c) 2011-2015, 2017, 2020, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
>    */
>   
>   #include <linux/bitops.h>
> @@ -18,6 +19,7 @@
>   #include "../thermal_core.h"
>   #include "../thermal_hwmon.h"
>   
> +#define QPNP_TM_REG_DIG_MINOR		0x00
>   #define QPNP_TM_REG_DIG_MAJOR		0x01
>   #define QPNP_TM_REG_TYPE		0x04
>   #define QPNP_TM_REG_SUBTYPE		0x05
> @@ -73,6 +75,7 @@ struct qpnp_tm_chip {
>   	struct device			*dev;
>   	struct thermal_zone_device	*tz_dev;
>   	unsigned int			subtype;
> +	unsigned int			dig_revision;
>   	long				temp;
>   	unsigned int			thresh;
>   	unsigned int			stage;
> @@ -224,6 +227,7 @@ static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
>   	long stage2_threshold_min = (*chip->temp_map)[THRESH_MIN][1];
>   	long stage2_threshold_max = (*chip->temp_map)[THRESH_MAX][1];
>   	bool disable_s2_shutdown = false;
> +	bool require_s2_shutdown = false;
>   	u8 reg;
>   
>   	WARN_ON(!mutex_is_locked(&chip->lock));
> @@ -256,9 +260,25 @@ static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
>   				 temp, stage2_threshold_max, stage2_threshold_max);
>   	}
>   
> +	if (chip->subtype == QPNP_TM_SUBTYPE_GEN2) {
> +		/*
> +		 * Check if stage 2 automatic partial shutdown must remain
> +		 * enabled to avoid potential repeated faults upon reaching
> +		 * over-temperature stage 3.
> +		 */
> +		switch (chip->dig_revision) {
> +		case 0x0001:
> +		case 0x0002:
> +		case 0x0100:
> +		case 0x0101:
> +			require_s2_shutdown = true;
> +			break;
> +		}
> +	}

Please move this switch to _probe and set chip->require_s2_shutdown instead.

> +
>   skip:
>   	reg |= chip->thresh;
> -	if (disable_s2_shutdown)
> +	if (disable_s2_shutdown && !require_s2_shutdown)
>   		reg |= SHUTDOWN_CTRL1_OVERRIDE_S2;
>   
>   	return qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
> @@ -373,7 +393,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
>   {
>   	struct qpnp_tm_chip *chip;
>   	struct device_node *node;
> -	u8 type, subtype, dig_major;
> +	u8 type, subtype, dig_major, dig_minor;
>   	u32 res;
>   	int ret, irq;
>   
> @@ -429,6 +449,14 @@ static int qpnp_tm_probe(struct platform_device *pdev)
>   		return ret;
>   	}
>   
> +	ret = qpnp_tm_read(chip, QPNP_TM_REG_DIG_MINOR, &dig_minor);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "could not read dig_minor\n");
> +		return ret;
> +	}
> +
> +	chip->dig_revision = (dig_major << 8) | dig_minor;
> +
>   	if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1
>   				     && subtype != QPNP_TM_SUBTYPE_GEN2)) {
>   		dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n",

-- 
With best wishes
Dmitry


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RESEND PATCH v3 3/3] thermal: qcom-spmi-temp-alarm: add support for LITE PMIC peripherals
  2023-01-24 23:46 ` [RESEND PATCH v3 3/3] thermal: qcom-spmi-temp-alarm: add support for LITE " David Collins
@ 2023-03-21 15:34   ` Dmitry Baryshkov
  0 siblings, 0 replies; 7+ messages in thread
From: Dmitry Baryshkov @ 2023-03-21 15:34 UTC (permalink / raw)
  To: David Collins, Daniel Lezcano, Rafael J . Wysocki, Amit Kucheria,
	Thara Gopinath, Andy Gross, Bjorn Andersson
  Cc: Konrad Dybcio, Zhang Rui, linux-arm-msm, linux-pm, linux-kernel

On 25/01/2023 01:46, David Collins wrote:
> 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.

This adds additional DT requirements to the lite temp-alarm trip points. 
Please consider adding a separate DT compatible string and describing 
these details in DT bindings. And the commit message _must_ also 
describe them.

> 
> Signed-off-by: David Collins <quic_collinsd@quicinc.com>
> ---
>   drivers/thermal/qcom/qcom-spmi-temp-alarm.c | 217 +++++++++++++++++++-
>   1 file changed, 208 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
> index 1fe8ff7a274e..dbf13dec7acd 100644
> --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
> +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
> @@ -4,6 +4,7 @@
>    * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
>    */
>   
> +#include <linux/bitfield.h>
>   #include <linux/bitops.h>
>   #include <linux/delay.h>
>   #include <linux/err.h>
> @@ -24,21 +25,28 @@
>   #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
>   /* TEMP_DAC_* registers are only present for TEMP_GEN2 v2.0 */
>   #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)
>   #define STATUS_GEN2_STATE_SHIFT		4
>   
> +/* IRQ status only needed for TEMP_ALARM_LITE */
> +#define IRQ_STATUS_MASK			BIT(0)
> +
>   #define SHUTDOWN_CTRL1_OVERRIDE_S2	BIT(6)
>   #define SHUTDOWN_CTRL1_THRESHOLD_MASK	GENMASK(1, 0)
>   
> @@ -46,6 +54,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
>   
> @@ -90,6 +100,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_map_lite_warning[THRESH_COUNT] = {
> +	115000, 125000, 135000, 145000
> +};
> +
> +static const long temp_map_lite_shutdown[THRESH_COUNT] = {
> +	135000, 145000, 160000, 175000
> +};
> +
>   /* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
>   #define DEFAULT_TEMP			37000
>   
> @@ -146,7 +169,7 @@ static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data)
>    */
>   static long qpnp_tm_decode_temp(struct qpnp_tm_chip *chip, unsigned int stage)
>   {
> -	if (chip->has_temp_dac) {
> +	if (chip->has_temp_dac || chip->subtype == QPNP_TM_SUBTYPE_LITE) {
>   		if (stage == 0 || stage > STAGE_COUNT)
>   			return 0;
>   
> @@ -164,19 +187,26 @@ static long qpnp_tm_decode_temp(struct qpnp_tm_chip *chip, unsigned int stage)
>    * qpnp_tm_get_temp_stage() - return over-temperature stage
>    * @chip:		Pointer to the qpnp_tm chip
>    *
> - * Return: stage (GEN1) or state (GEN2) on success, or errno on failure.
> + * Return: stage (GEN1), state (GEN2), or alarm interrupt state (LITE) on
> + *	   success; or errno on failure.
>    */
>   static int qpnp_tm_get_temp_stage(struct qpnp_tm_chip *chip)
>   {
>   	int ret;
> +	u16 addr = QPNP_TM_REG_STATUS;
>   	u8 reg = 0;
>   
> -	ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg);
> +	if (chip->subtype == QPNP_TM_SUBTYPE_LITE)
> +		addr = QPNP_TM_REG_IRQ_STATUS;
> +
> +	ret = qpnp_tm_read(chip, addr, &reg);
>   	if (ret < 0)
>   		return ret;
>   
>   	if (chip->subtype == QPNP_TM_SUBTYPE_GEN1)
>   		ret = reg & STATUS_GEN1_STAGE_MASK;
> +	else if (chip->subtype == QPNP_TM_SUBTYPE_LITE)
> +		ret = reg & IRQ_STATUS_MASK;
>   	else
>   		ret = (reg & STATUS_GEN2_STATE_MASK) >> STATUS_GEN2_STATE_SHIFT;
>   
> @@ -199,7 +229,8 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
>   		return ret;
>   	stage = ret;
>   
> -	if (chip->subtype == QPNP_TM_SUBTYPE_GEN1) {
> +	if (chip->subtype == QPNP_TM_SUBTYPE_GEN1
> +	    || chip->subtype == QPNP_TM_SUBTYPE_LITE) {
>   		stage_new = stage;
>   		stage_old = chip->stage;
>   	} else {
> @@ -282,6 +313,78 @@ static int qpnp_tm_set_temp_dac_thresh(struct qpnp_tm_chip *chip, int trip,
>   	return 0;
>   }
>   
> +static int qpnp_tm_set_temp_lite_thresh(struct qpnp_tm_chip *chip, int trip,
> +				       int temp)
> +{
> +	int ret, temp_cfg, i;
> +	const long *temp_map;
> +	u16 addr;
> +	u8 reg, thresh;
> +
> +	if (trip < 0 || trip >= STAGE_COUNT) {
> +		dev_err(chip->dev, "invalid TEMP_LITE trip = %d\n", trip);
> +		return -EINVAL;
> +	}
> +
> +	switch (trip) {
> +	case 0:
> +		temp_map = temp_map_lite_warning;
> +		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_map_lite_shutdown;
> +		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_dac_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_dac_map[trip] = temp_cfg;
> +
> +	return 0;
> +}
> +
>   static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
>   					     int temp)
>   {
> @@ -388,6 +491,24 @@ static const struct thermal_zone_device_ops qpnp_tm_sensor_temp_dac_ops = {
>   	.set_trip_temp = qpnp_tm_set_temp_dac_trip_temp,
>   };
>   
> +static int qpnp_tm_set_temp_lite_trip_temp(struct thermal_zone_device *tz,
> +					   int trip, int temp)
> +{
> +	struct qpnp_tm_chip *chip = tz->devdata;
> +	int ret;
> +
> +	mutex_lock(&chip->lock);
> +	ret = qpnp_tm_set_temp_lite_thresh(chip, trip, temp);
> +	mutex_unlock(&chip->lock);
> +
> +	return ret;
> +}
> +
> +static const struct thermal_zone_device_ops qpnp_tm_sensor_temp_lite_ops = {
> +	.get_temp = qpnp_tm_get_temp,
> +	.set_trip_temp = qpnp_tm_set_temp_lite_trip_temp,
> +};
> +
>   static irqreturn_t qpnp_tm_isr(int irq, void *data)
>   {
>   	struct qpnp_tm_chip *chip = data;
> @@ -474,6 +595,71 @@ static int qpnp_tm_temp_dac_init(struct qpnp_tm_chip *chip)
>   	return 0;
>   }
>   
> +/* Configure TEMP_LITE registers based on DT thermal_zone trips */
> +static int qpnp_tm_temp_lite_update_trip_temps(struct qpnp_tm_chip *chip)
> +{
> +	const struct thermal_trip *trips;
> +	int ret, ntrips, i;
> +
> +	ntrips = of_thermal_get_ntrips(chip->tz_dev);
> +	/* Keep hardware defaults if no DT trips are defined. */
> +	if (ntrips <= 0)
> +		return 0;
> +
> +	trips = of_thermal_get_trip_points(chip->tz_dev);
> +	if (!trips)
> +		return -EINVAL;
> +
> +	for (i = 0; i < ntrips; i++) {
> +		if (of_thermal_is_trip_valid(chip->tz_dev, i)) {
> +			ret = qpnp_tm_set_temp_lite_thresh(chip, i,
> +							  trips[i].temperature);
> +			if (ret < 0)
> +				return ret;
> +		}
> +	}
> +
> +	/* Verify that trips are strictly increasing. */
> +	if (chip->temp_dac_map[2] <= chip->temp_dac_map[0]) {
> +		dev_err(chip->dev, "Threshold 2=%ld <= threshold 0=%ld\n",
> +			chip->temp_dac_map[2], chip->temp_dac_map[0]);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +/* Read the hardware default TEMP_LITE stage threshold temperatures */
> +static int qpnp_tm_temp_lite_init(struct qpnp_tm_chip *chip)
> +{
> +	int ret, thresh;
> +	u8 reg = 0;
> +
> +	/*
> +	 * Store the warning trip temp in temp_dac_map[0] and the shutdown trip
> +	 * temp in temp_dac_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_dac_map[0] = temp_map_lite_warning[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_dac_map[2] = temp_map_lite_shutdown[thresh];
> +
> +	return 0;
> +}
> +
>   /*
>    * This function initializes the internal temp value based on only the
>    * current thermal stage and threshold. Setup threshold control and
> @@ -500,13 +686,18 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip)
>   		goto out;
>   	chip->stage = ret;
>   
> -	stage = chip->subtype == QPNP_TM_SUBTYPE_GEN1
> -		? chip->stage : alarm_state_map[chip->stage];
> +	stage = (chip->subtype == QPNP_TM_SUBTYPE_GEN1
> +		 || chip->subtype == QPNP_TM_SUBTYPE_LITE)
> +			? chip->stage : alarm_state_map[chip->stage];
>   
>   	if (stage)
>   		chip->temp = qpnp_tm_decode_temp(chip, stage);
>   
> -	if (chip->has_temp_dac) {
> +	if (chip->subtype == QPNP_TM_SUBTYPE_LITE) {
> +		ret = qpnp_tm_temp_lite_update_trip_temps(chip);
> +		if (ret < 0)
> +			goto out;
> +	} else if (chip->has_temp_dac) {
>   		ret = qpnp_tm_temp_dac_update_trip_temps(chip);
>   		if (ret < 0)
>   			goto out;
> @@ -598,7 +789,8 @@ static int qpnp_tm_probe(struct platform_device *pdev)
>   	chip->dig_revision = (dig_major << 8) | dig_minor;
>   
>   	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;
> @@ -610,7 +802,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
>   		chip->has_temp_dac = true;
>   	else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 1)
>   		chip->temp_map = &temp_map_gen2_v1;
> -	else
> +	else if (subtype == QPNP_TM_SUBTYPE_GEN1)
>   		chip->temp_map = &temp_map_gen1;
>   
>   	if (chip->has_temp_dac) {
> @@ -620,6 +812,13 @@ static int qpnp_tm_probe(struct platform_device *pdev)
>   			return ret;
>   	}
>   
> +	if (chip->subtype == QPNP_TM_SUBTYPE_LITE) {
> +		ops = &qpnp_tm_sensor_temp_lite_ops;
> +		ret = qpnp_tm_temp_lite_init(chip);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
>   	/*
>   	 * Register the sensor before initializing the hardware to be able to
>   	 * read the trip points. get_temp() returns the default temperature

-- 
With best wishes
Dmitry


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2023-03-21 15:34 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-24 23:46 [RESEND PATCH v3 0/3] thermal: qcom-spmi-temp-alarm: add support for new TEMP_ALARM subtypes David Collins
2023-01-24 23:46 ` [RESEND PATCH v3 1/3] thermal: qcom-spmi-temp-alarm: enable stage 2 shutdown when required David Collins
2023-03-21  0:57   ` Dmitry Baryshkov
2023-01-24 23:46 ` [RESEND PATCH v3 2/3] thermal: qcom-spmi-temp-alarm: add support for GEN2 rev 2 PMIC peripherals David Collins
2023-01-24 23:46 ` [RESEND PATCH v3 3/3] thermal: qcom-spmi-temp-alarm: add support for LITE " David Collins
2023-03-21 15:34   ` Dmitry Baryshkov
2023-03-20 23:51 ` [RESEND PATCH v3 0/3] thermal: qcom-spmi-temp-alarm: add support for new TEMP_ALARM subtypes David Collins

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.