[PATCH 3/3] thermal: samsung: acpm-tmu: add Exynos850 support

Alexey Klimov <[email protected]>
Newsgroups org.kernel.vger.linux-samsung-soc,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pm
Message-ID <[email protected]>
Add support for the Thermal Management Unit block found on the
Exynos850 SoC.

Unlike the GS101, the Exynos850 ACPM firmware implements a smaller
set of IPC commands (primarily basic TMU initialization, suspend/resume,
and temperature reading). While the firmware supports basic TMU
initialization, suspend/resume, and temperature polling, it lacks the
ability to fully configure the hardware. The remaining functionality
is implemented via direct MMIO access:
- TMU initialization sequence,
- setting falling and rising thresholds and associated IRQs,
- helpers to convert temperature to hardware values,
- handling of IRQs,
- thermal zone control (TMU core enable/disable).

Additionally, this also reorders the sensor group assignment in
acpm_tmu_probe() to let it happen before TMU init SoC callback.

Signed-off-by: Alexey Klimov <[email protected]>
---
 drivers/thermal/samsung/acpm-tmu.c | 356 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 355 insertions(+), 1 deletion(-)

diff --git a/drivers/thermal/samsung/acpm-tmu.c b/drivers/thermal/samsung/acpm-tmu.c
index b3cea9890b4b..2bf880515e31 100644
--- a/drivers/thermal/samsung/acpm-tmu.c
+++ b/drivers/thermal/samsung/acpm-tmu.c
@@ -5,6 +5,7 @@
  * Copyright 2026 Linaro Ltd.
  */
 
+#include <linux/bitfield.h>
 #include <linux/cleanup.h>
 #include <linux/clk.h>
 #include <linux/device/devres.h>
@@ -23,9 +24,79 @@
 
 #include "../thermal_hwmon.h"
 
+#define TYPE_TWO_POINT_TRIMMING		1
+
 #define EXYNOS_TMU_SENSOR(i)		BIT(i)
 #define EXYNOS_TMU_SENSORS_MAX_COUNT	16
 
+#define EXYNOS850_INTPEND_RISE_MASK	GENMASK(8, 0)
+#define EXYNOS850_INTPEND_FALL_MASK	GENMASK(23, 16)
+
+#define EXYNOS850_GPU_SENSOR_MASK	(EXYNOS_TMU_SENSOR(3))
+#define EXYNOS850_CPUCL1_SENSOR_MASK	(EXYNOS_TMU_SENSOR(2))
+#define EXYNOS850_CPUCL0_SENSOR_MASK	(EXYNOS_TMU_SENSOR(1))
+
+#define EXYNOS_TMU_REG_TRIMINFO		(0x0)
+#define EXYNOS850_TMU_TRIMINFO_SHIFT	4
+#define EXYNOS850_TMU_TRIMINFO_OFFSET(n)	\
+	(EXYNOS_TMU_REG_TRIMINFO + (n) * EXYNOS850_TMU_TRIMINFO_SHIFT)
+
+#define EXYNOS_FIRST_POINT_TRIM		25
+#define EXYNOS_SECOND_POINT_TRIM	85
+
+#define EXYNOS_TRIMINFO_CALIB_SEL_MASK	BIT(23)
+#define EXYNOS_TRIMINFO_25_MASK		GENMASK(8, 0)
+#define EXYNOS_TRIMINFO_85_MASK		GENMASK(17, 9)
+#define EXYNOS850_TMU_BGRI_TRIM_SHIFT	20
+#define EXYNOS850_TMU_BGRI_TRIM_MASK	0xf
+#define EXYNOS850_TMU_VREF_TRIM_SHIFT	12
+#define EXYNOS850_TMU_VREF_TRIM_MASK	0xf
+#define EXYNOS850_TMU_VBEI_TRIM_SHIFT	8
+#define EXYNOS850_TMU_VBEI_TRIM_MASK	0xf
+
+#define EXYNOS850_TMU_CONTROL		(0x20)
+#define EXYNOS850_TMU_CONTROL1		(0x24)
+#define EXYNOS850_TMU_COUNTER_VALUE0	(0x30)
+#define EXYNOS850_TMU_COUNTER_VALUE1	(0x34)
+#define EXYNOS850_TMU_AVG_CONTROL	(0x38)
+#define EXYNOS850_TMU_TRIM0		(0x3c)
+
+#define EXYNOS_TMU_REG_INTPEND0		(0x118)
+#define EXYNOS_TMU_REG_INTPEND5		(0x318)
+#define EXYNOS_TMU_REG_INTPEND8		(0x658)
+
+#define EXYNOS_TMU_REG_INTEN0		(0x110)
+#define EXYNOS_TMU_REG_INTEN5		(0x310)
+#define EXYNOS_TMU_REG_INTEN8		(0x650)
+#define EXYNOS850_RISE_LEVEL1		BIT(1)
+#define EXYNOS850_FALL_LEVEL1		BIT(17)
+
+#define EXYNOS_TMU_REG_THD_TEMP0	(0x50)
+#define EXYNOS_TMU_REG_THD_TEMP1	(0x170)
+#define EXYNOS_TMU_REG_THD_TEMP8	(0x450)
+
+#define EXYNOS850_REG_RISE1_0_OFFSET	(0xc)
+#define EXYNOS850_REG_FALL1_0_OFFSET	(EXYNOS850_REG_RISE1_0_OFFSET + 0x10)
+#define EXYNOS850_THD_LEVEL1_MASK	GENMASK(24, 16)
+
+#define EXYNOS850_TMU_REG_THD(p)	((p == 0) ?					\
+					EXYNOS_TMU_REG_THD_TEMP0 :			\
+					((p < 8) ?					\
+					(EXYNOS_TMU_REG_THD_TEMP1 + (p - 1) * 0x20) :	\
+					(EXYNOS_TMU_REG_THD_TEMP8 + (p - 8) * 0x20)))
+
+#define EXYNOS850_TMU_REG_INTEN(p)	((p < 5) ?					\
+					(EXYNOS_TMU_REG_INTEN0 + p * 0x10) :		\
+					((p < 8) ?					\
+					(EXYNOS_TMU_REG_INTEN5 + (p - 5) * 0x10) :	\
+					(EXYNOS_TMU_REG_INTEN8 + (p - 8) * 0x10)))
+
+#define EXYNOS850_REG_INTPEND(p)	((p < 5) ?					\
+					(EXYNOS_TMU_REG_INTPEND0 + p * 0x10) :		\
+					((p < 8) ?					\
+					(EXYNOS_TMU_REG_INTPEND5 + (p - 5) * 0x10) :	\
+					(EXYNOS_TMU_REG_INTPEND8 + (p - 8) * 0x10)))
+
 #define GS101_CPUCL2_SENSOR_MASK (EXYNOS_TMU_SENSOR(0) |	\
 				  EXYNOS_TMU_SENSOR(6) |	\
 				  EXYNOS_TMU_SENSOR(7) |	\
@@ -69,6 +140,9 @@ struct acpm_tmu_sensor {
 	struct acpm_tmu_priv *priv;
 	struct mutex lock; /* protects sensor state */
 	bool enabled;
+
+	u32 efuse_value;
+	u16 temp_error1, temp_error2;
 };
 
 struct acpm_tmu_priv {
@@ -81,6 +155,7 @@ struct acpm_tmu_priv {
 	unsigned int mbox_chan_id;
 	unsigned int num_sensors;
 	int irq;
+	u32 cal_type;
 	struct acpm_tmu_sensor sensors[] __counted_by(num_sensors);
 };
 
@@ -92,6 +167,7 @@ struct acpm_tmu_driver_data {
 	unsigned int mbox_chan_id;
 
 	/* SoC-specific TMU routines and values */
+	u32 efuse_value;
 	int (*tz_control)(struct acpm_tmu_sensor *sensor, bool on);
 	int (*tmu_check_and_clear_irqs)(struct acpm_tmu_sensor *sensor,
 					bool *pending_irq);
@@ -139,6 +215,40 @@ static const struct regmap_config gs101_regmap_config = {
 	.max_register = GS101_REG_INTPEND(15),
 };
 
+static const struct acpm_tmu_sensor_group exynos850_sensor_groups[] = {
+	ACPM_TMU_SENSOR_GROUP(EXYNOS850_CPUCL0_SENSOR_MASK, 0),
+	ACPM_TMU_SENSOR_GROUP(EXYNOS850_CPUCL1_SENSOR_MASK, 1),
+	ACPM_TMU_SENSOR_GROUP(EXYNOS850_GPU_SENSOR_MASK, 2),
+};
+
+static const struct reg_field exynos850_reg_fields[REG_INTPEND_COUNT] = {
+	[P0_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(0), 0, 23),
+	[P1_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(1), 0, 23),
+	[P2_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(2), 0, 23),
+	[P3_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(3), 0, 23),
+	[P4_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(4), 0, 23),
+	[P5_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(5), 0, 23),
+	[P6_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(6), 0, 23),
+	[P7_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(7), 0, 23),
+	[P8_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(8), 0, 23),
+	[P9_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(9), 0, 23),
+	[P10_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(10), 0, 23),
+	[P11_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(11), 0, 23),
+	[P12_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(12), 0, 23),
+	[P13_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(13), 0, 23),
+	[P14_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(14), 0, 23),
+	[P15_INTPEND] =		REG_FIELD(EXYNOS850_REG_INTPEND(15), 0, 23),
+};
+
+static const struct regmap_config exynos850_regmap_config = {
+	.name = "e850_tmu_regs",
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.use_relaxed_mmio = true,
+	.max_register = EXYNOS850_REG_INTPEND(15),
+};
+
 static int gs101_tz_control(struct acpm_tmu_sensor *sensor, bool on)
 {
 	struct acpm_tmu_priv *priv = sensor->priv;
@@ -155,6 +265,129 @@ static int gs101_tmu_init(struct acpm_tmu_priv *priv)
 	return handle->ops->tmu.init(handle, priv->mbox_chan_id);
 }
 
+static int exynos850_tz_control(struct acpm_tmu_sensor *sensor, bool on)
+{
+	struct acpm_tmu_priv *priv = sensor->priv;
+
+	regmap_update_bits(priv->regmap, EXYNOS850_TMU_CONTROL,
+			   BIT(0), on ? BIT(0) : 0);
+	return 0;
+}
+
+static int exynos850_tmu_init(struct acpm_tmu_priv *priv)
+{
+	struct acpm_handle *handle = priv->handle;
+	struct regmap *regmap = priv->regmap;
+	unsigned int trim_info, trim;
+	unsigned int t_buf_vref_sel, t_buf_slope_sel, avg_mode;
+	unsigned int t_bgri_trim, t_vref_trim, t_vbei_trim;
+	u32 trim0_mask, trim0_val;
+	int ret, i;
+
+	ret = handle->ops->tmu.init(handle, priv->mbox_chan_id);
+	if (ret)
+		return ret;
+
+	/*
+	 * Bootloader may partly initialise TMU or may not initialise it at all,
+	 * regardless of that we do the full TMU initialisation here and
+	 * collect calibration info.
+	 */
+	regmap_read(regmap, EXYNOS850_TMU_TRIMINFO_OFFSET(0), &trim_info);
+	priv->cal_type = FIELD_GET(EXYNOS_TRIMINFO_CALIB_SEL_MASK, trim_info);
+
+	for (i = 0; i < priv->num_sensors; i++) {
+		struct acpm_tmu_sensor *sensor = &priv->sensors[i];
+		unsigned long mask = sensor->group->mask;
+		int t;
+		u16 temp_error1, temp_error2;
+
+		for_each_set_bit(t, &mask, EXYNOS_TMU_SENSORS_MAX_COUNT) {
+			regmap_read(regmap, EXYNOS850_TMU_TRIMINFO_OFFSET(t), &trim_info);
+
+			temp_error1 = FIELD_GET(EXYNOS_TRIMINFO_25_MASK, trim_info);
+			temp_error2 = FIELD_GET(EXYNOS_TRIMINFO_85_MASK, trim_info);
+
+			if (temp_error1)
+				sensor->temp_error1 = temp_error1;
+			else
+				sensor->temp_error1 = FIELD_GET(EXYNOS_TRIMINFO_25_MASK,
+								priv->data->efuse_value);
+
+			if (priv->cal_type == TYPE_TWO_POINT_TRIMMING) {
+				if (temp_error1)
+					sensor->temp_error2 = temp_error2;
+				else
+					sensor->temp_error2 = FIELD_GET(EXYNOS_TRIMINFO_85_MASK,
+									priv->data->efuse_value);
+			}
+		}
+	}
+
+	/* Read TRIMINFO 0, 1, 2 settings */
+	regmap_read(regmap, EXYNOS850_TMU_TRIMINFO_OFFSET(0), &trim);
+	t_buf_vref_sel = FIELD_GET(GENMASK(22, 18), trim);
+
+	regmap_read(regmap, EXYNOS850_TMU_TRIMINFO_OFFSET(1), &trim);
+	t_buf_slope_sel = FIELD_GET(GENMASK(21, 18), trim);
+
+	regmap_read(regmap, EXYNOS850_TMU_TRIMINFO_OFFSET(2), &trim);
+	avg_mode = FIELD_GET(GENMASK(20, 18), trim);
+
+	/*
+	 * Set BUF_VREF_SEL and BUF_SLOPE_SEL.
+	 * Clear bit 12 -- disable thermal tripping for now.
+	 * Clear bit 0 -- TMU core off
+	 */
+	regmap_update_bits(regmap, EXYNOS850_TMU_CONTROL,
+			   GENMASK(28, 24) | BIT(12) | GENMASK(11, 8) | BIT(0),
+			   FIELD_PREP(GENMASK(28, 24), t_buf_vref_sel) |
+			   FIELD_PREP(GENMASK(11, 8), t_buf_slope_sel));
+
+	/*
+	 * Set NUM_PROBE equal to number of sensors.
+	 * Disable LPI by clearing bit 10.
+	 */
+	regmap_update_bits(regmap, EXYNOS850_TMU_CONTROL1,
+			   GENMASK(19, 16) | BIT(10),
+			   FIELD_PREP(GENMASK(19, 16), priv->num_sensors));
+
+	/* Set AVG_MODE (If avg_mode > 0, set bit 4 and insert avg_mode into bits [2:0]) */
+	regmap_update_bits(regmap, EXYNOS850_TMU_AVG_CONTROL,
+			   BIT(4) | GENMASK(2, 0),
+			   avg_mode ? (BIT(4) | FIELD_PREP(GENMASK(2, 0), avg_mode)) : 0);
+
+	/* Set COUNTER_VALUE 0 & 1 */
+	regmap_update_bits(regmap, EXYNOS850_TMU_COUNTER_VALUE0,
+			   GENMASK(15, 0), FIELD_PREP(GENMASK(15, 0), 0x028A));
+
+	regmap_update_bits(regmap, EXYNOS850_TMU_COUNTER_VALUE1,
+			   GENMASK(31, 16), FIELD_PREP(GENMASK(31, 16), 0x028A));
+
+	/* Read TRIMINFO 3, 4, 5 for BGR_I/VREF/VBE_I */
+	regmap_read(regmap, EXYNOS850_TMU_TRIMINFO_OFFSET(3), &trim);
+	t_bgri_trim = FIELD_GET(GENMASK(21, 18), trim);
+
+	regmap_read(regmap, EXYNOS850_TMU_TRIMINFO_OFFSET(4), &trim);
+	t_vref_trim = FIELD_GET(GENMASK(21, 18), trim);
+
+	regmap_read(regmap, EXYNOS850_TMU_TRIMINFO_OFFSET(5), &trim);
+	t_vbei_trim = FIELD_GET(GENMASK(21, 18), trim);
+
+	/* Set TRIM0 */
+	trim0_mask = (EXYNOS850_TMU_BGRI_TRIM_MASK << EXYNOS850_TMU_BGRI_TRIM_SHIFT) |
+		     (EXYNOS850_TMU_VREF_TRIM_MASK << EXYNOS850_TMU_VREF_TRIM_SHIFT) |
+		     (EXYNOS850_TMU_VBEI_TRIM_MASK << EXYNOS850_TMU_VBEI_TRIM_SHIFT);
+
+	trim0_val = (t_bgri_trim << EXYNOS850_TMU_BGRI_TRIM_SHIFT) |
+		    (t_vref_trim << EXYNOS850_TMU_VREF_TRIM_SHIFT) |
+		    (t_vbei_trim << EXYNOS850_TMU_VBEI_TRIM_SHIFT);
+
+	regmap_update_bits(regmap, EXYNOS850_TMU_TRIM0, trim0_mask, trim0_val);
+
+	return 0;
+}
+
 static int acpm_tmu_op_tz_control(struct acpm_tmu_sensor *sensor, bool on)
 {
 	struct acpm_tmu_priv *priv = sensor->priv;
@@ -268,6 +501,74 @@ static int acpm_tmu_get_temp(struct thermal_zone_device *tz, int *temp)
 	return 0;
 }
 
+static int temp_to_code(struct acpm_tmu_sensor *sensor, u8 temp)
+{
+	struct acpm_tmu_priv *priv = sensor->priv;
+
+	if (priv->cal_type == TYPE_TWO_POINT_TRIMMING)
+		return (temp - EXYNOS_FIRST_POINT_TRIM) *
+		       (sensor->temp_error2 - sensor->temp_error1) /
+		       (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) +
+		       sensor->temp_error1;
+
+	return temp + sensor->temp_error1 - EXYNOS_FIRST_POINT_TRIM;
+}
+
+static int exynos850_tmu_update_thresholds(struct acpm_tmu_sensor *sensor,
+				      u8 thresholds[2], u8 inten)
+{
+	struct acpm_tmu_priv *priv = sensor->priv;
+	unsigned long mask = sensor->group->mask;
+	bool was_enabled;
+	int ret, restore_ret, i, trip_code, trip_code_low;
+	u32 addr;
+
+	guard(mutex)(&sensor->lock);
+
+	was_enabled = sensor->enabled;
+
+	if (was_enabled) {
+		ret = acpm_tmu_op_tz_control(sensor, false);
+		if (ret)
+			return ret;
+	}
+
+	trip_code = temp_to_code(sensor, thresholds[1]);
+	trip_code_low = temp_to_code(sensor, thresholds[0]);
+
+	for_each_set_bit(i, &mask, EXYNOS_TMU_SENSORS_MAX_COUNT) {
+		u32 irq_en = 0;
+
+		if (inten & BIT(0)) {
+			addr = EXYNOS850_TMU_REG_THD(i) + EXYNOS850_REG_FALL1_0_OFFSET;
+			regmap_update_bits(priv->regmap, addr, EXYNOS850_THD_LEVEL1_MASK,
+					   FIELD_PREP(EXYNOS850_THD_LEVEL1_MASK, trip_code_low));
+			irq_en = EXYNOS850_FALL_LEVEL1;
+		}
+
+		if (inten & BIT(1)) {
+			addr = EXYNOS850_TMU_REG_THD(i) + EXYNOS850_REG_RISE1_0_OFFSET;
+			regmap_update_bits(priv->regmap, addr, EXYNOS850_THD_LEVEL1_MASK,
+					   FIELD_PREP(EXYNOS850_THD_LEVEL1_MASK, trip_code));
+			irq_en |= EXYNOS850_RISE_LEVEL1;
+		}
+
+		regmap_update_bits(priv->regmap, EXYNOS850_TMU_REG_INTEN(i),
+				   (EXYNOS850_FALL_LEVEL1 | EXYNOS850_RISE_LEVEL1), irq_en);
+	}
+
+	if (was_enabled) {
+		restore_ret = acpm_tmu_op_tz_control(sensor, true);
+		if (restore_ret)
+			dev_err(priv->dev, "Failed to restore sensor state: %d\n",
+				restore_ret);
+		if (!ret)
+			ret = restore_ret;
+	}
+
+	return 0;
+}
+
 static int gs101_tmu_update_thresholds(struct acpm_tmu_sensor *sensor,
 				       u8 thresholds[2], u8 inten)
 {
@@ -376,6 +677,36 @@ static int gs101_handle_irqs(struct acpm_tmu_sensor *sensor, bool *pending_irq)
 	return ret;
 }
 
+static int exynos850_handle_irqs(struct acpm_tmu_sensor *sensor,
+				 bool *pending_irq)
+{
+	struct acpm_tmu_priv *priv = sensor->priv;
+	unsigned long mask = sensor->group->mask;
+	int i, ret;
+	u32 val, clear_val;
+
+	guard(mutex)(&sensor->lock);
+
+	for_each_set_bit(i, &mask, EXYNOS_TMU_SENSORS_MAX_COUNT) {
+		ret = regmap_field_read(priv->regmap_fields[i], &val);
+		if (ret)
+			return ret;
+
+		if (val) {
+			clear_val = val &
+				(EXYNOS850_INTPEND_RISE_MASK | EXYNOS850_INTPEND_FALL_MASK);
+			ret = regmap_field_force_write(priv->regmap_fields[i], clear_val);
+			if (ret)
+				dev_err(priv->dev,
+					 "sensor %d: failed to clear IRQ (%d)\n", i, ret);
+			*pending_irq = true;
+			break;
+		}
+	}
+
+	return 0;
+}
+
 static irqreturn_t acpm_tmu_thread_fn(int irq, void *id)
 {
 	struct acpm_tmu_priv *priv = id;
@@ -423,11 +754,28 @@ static const struct acpm_tmu_driver_data acpm_tmu_gs101 = {
 	.tmu_update_thresholds = gs101_tmu_update_thresholds,
 };
 
+static const struct acpm_tmu_driver_data acpm_tmu_exynos850 = {
+	.reg_fields = exynos850_reg_fields,
+	.regmap_config = &exynos850_regmap_config,
+	.sensor_groups = exynos850_sensor_groups,
+	.num_sensor_groups = ARRAY_SIZE(exynos850_sensor_groups),
+	.mbox_chan_id = 9,
+	.efuse_value = 55,
+	.tz_control = exynos850_tz_control,
+	.tmu_check_and_clear_irqs = exynos850_handle_irqs,
+	.tmu_init = exynos850_tmu_init,
+	.tmu_update_thresholds = exynos850_tmu_update_thresholds,
+};
+
 static const struct of_device_id acpm_tmu_match[] = {
 	{
 		.compatible = "google,gs101-tmu-top",
 		.data = &acpm_tmu_gs101
 	},
+	{
+		.compatible = "samsung,exynos850-tmu-top",
+		.data = &acpm_tmu_exynos850,
+	},
 	{ /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, acpm_tmu_match);
@@ -501,6 +849,13 @@ static int acpm_tmu_probe(struct platform_device *pdev)
 	if (ret < 0)
 		return dev_err_probe(dev, ret, "Failed to resume device\n");
 
+	/* Sensor groups should be assigned before ->tmu_init() for some SoCs */
+	for (i = 0; i < priv->num_sensors; i++) {
+		struct acpm_tmu_sensor *sensor = &priv->sensors[i];
+
+		sensor->group = &data->sensor_groups[i];
+	}
+
 	if (priv->data->tmu_init) {
 		ret = priv->data->tmu_init(priv);
 		if (ret) {
@@ -513,7 +868,6 @@ static int acpm_tmu_probe(struct platform_device *pdev)
 		struct acpm_tmu_sensor *sensor = &priv->sensors[i];
 
 		mutex_init(&sensor->lock);
-		sensor->group = &data->sensor_groups[i];
 		sensor->priv = priv;
 
 		sensor->tzd = devm_thermal_of_zone_register(dev, i, sensor,

-- 
2.51.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.