All of lore.kernel.org
 help / color / mirror / Atom feed
From: Sang Min Kim <hypmean.kim@samsung.com>
To: "bzolnier@gmail.com" <bzolnier@gmail.com>,
	"krzysztof.kozlowski@canonical.com" 
	<krzysztof.kozlowski@canonical.com>,
	"rafael@kernel.org" <rafael@kernel.org>,
	"daniel.lezcano@linaro.org" <daniel.lezcano@linaro.org>,
	"amitk@kernel.org" <amitk@kernel.org>,
	"rui.zhang@intel.com" <rui.zhang@intel.com>,
	ALIM AKHTAR <alim.akhtar@samsung.com>,
	"lgirdwood@gmail.com" <lgirdwood@gmail.com>,
	"broonie@kernel.org" <broonie@kernel.org>
Cc: "linux-pm@vger.kernel.org" <linux-pm@vger.kernel.org>,
	"linux-samsung-soc@vger.kernel.org" 
	<linux-samsung-soc@vger.kernel.org>,
	"linux-arm-kernel@lists.infradead.org" 
	<linux-arm-kernel@lists.infradead.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
Subject: [PATCH 2/2] thermal: exynos: Add support for ARTPEC-8
Date: Tue, 22 Mar 2022 17:22:42 +0900	[thread overview]
Message-ID: <20220322082242epcms2p5b5e9e24a947e1d6c4b77fb9c95baf992@epcms2p5> (raw)
In-Reply-To: <20220322075638.110056-1-hypmean.kim@samsung.com>

Add support thermal management for Axis ARTPEC-8 SoC.
ARTPEC-8 is the SoC platform of Axis Communications.
In the existing thermal management function of exynos, functions that support
remote sensors have been added.
 
Signed-off-by: sangmin kim <hypmean.kim@samsung.com>
---
 drivers/thermal/samsung/exynos_tmu.c | 666 ++++++++++++++++++++++++++++++++---
 1 file changed, 616 insertions(+), 50 deletions(-)
 
diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c
index f4ab4c5..9837f42 100644
--- a/drivers/thermal/samsung/exynos_tmu.c
+++ b/drivers/thermal/samsung/exynos_tmu.c
@@ -14,6 +14,7 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
+#include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/of_address.h>
@@ -124,6 +125,77 @@
 
 #define MCELSIUS        1000
 
+/* Artpec8 specific registers */
+#define ARTPEC8_TMU_REG_TRIMINFO        0x0
+#define ARTPEC8_TMU_REG_TRIMINFO1        0x4
+#define ARTPEC8_TMU_REG_TRIMINFO2        0x8
+#define ARTPEC8_TMU_REG_TRIMINFO3        0xC
+#define ARTPEC8_TMU_REG_TRIMINFO4        0x10
+#define ARTPEC8_TMU_REG_TRIMINFO5        0x14
+#define ARTPEC8_TMU_REG_CONTROL                0x20
+#define ARTPEC8_TMU_REG_CONTROL1        0x24
+#define ARTPEC8_TMU_REG_STATUS                0x28
+
+#define ARTPEC8_TMU_REG_AVG_CONTROL        0x38
+#define ARTPEC8_TMU_REG_TMU_TRIM0        0x3C
+
+#define ARTPEC8_TMU_REG_EMUL_CON        0x160
+#define NUM_PROBE_OFFSET                16
+
+#define ARTPEC8_FIRST_POINT_TRIM        25
+#define ARTPEC8_SECOND_POINT_TRIM        105
+
+#define ARTPEC8_EMUL_EN                        1
+#define ARTPEC8_TIME_OFFSET                16
+#define ARTPEC8_EMUL_NEXT_TIME                (0x4e20 << ARTPEC8_TIME_OFFSET)
+
+#define ARTPEC8_TMU_TEMP_MASK                0x1ff
+#define ARTPEC8_CALIB_SEL_SHIFT                23
+
+#define ARTPEC8_EMUL_DATA_SHIFT                7
+
+#define ARTPEC8_T_BUF_VREF_SEL_SHIFT        18
+#define ARTPEC8_T_BUF_SLOPE_SEL_SHIFT        18
+#define ARTPEC8_INTEN_TRIPPING_SHIFT        7
+#define ARTPEC8_INTEN_CLOCKDOWN_SHIFT        8
+#define ARTPEC8_TRIMINFO_105_SHIFT        9
+#define ARTPEC8_INTEN_FALL0_SHIFT        16
+#define ARTPEC8_TMU_REF_VOLTAGE_SHIFT        24
+#define ARTPEC8_TMU_REF_VOLTAGE_MASK        0x1f
+#define ARTPEC8_TMU_BUF_SLOPE_SEL_SHIFT        8
+#define ARTPEC8_TMU_BUF_SLOPE_SEL_MASK        0xf
+
+#define ARTPEC8_TMU_CONTROL_CORE_EN        1
+#define ARTPEC8_TMU_CONTROL_AUTO_MODE        2
+#define ARTPEC8_TMU_CONTROL_TRIP_EN        (1 << 12)
+#define ARTPEC8_LPI_MODE_EN                (1 << 10)
+
+#define ARTPEC8_TRIM0_BGR_I_SHIFT        20
+#define ARTPEC8_TRIM0_VREF_SHIFT        12
+#define ARTPEC8_TRIM0_VBE_I_SHIFT        8
+
+#define INTPEND_RISE_MASK                0xff
+#define INTPEND_FALL_MASK                0xff0000
+#define ARTPEC8_TRIM0_MASK                0xf
+#define ARTPEC8_TRIM2_MASK                0x7
+
+#define ARTPEC8_TRIMINFO_TRIM0_SHIFT        18
+
+#define LOW_TEMP_WEIGHT                        9203
+#define HIGH_TEMP_WEIGHT                9745
+#define TEMP_WEIGHT                        10000
+
+struct sensor_offset {
+        u32 trim_offset;
+        u32 temp_offset;
+        u32 temp_reg_shift;
+        u32 rise_offset;
+        u32 fall_offset;
+        u32 past_offset;
+        u32 inten;
+        u32 intpend;
+};
+
 enum soc_type {
         SOC_ARCH_EXYNOS3250 = 1,
         SOC_ARCH_EXYNOS4210,
@@ -134,6 +206,63 @@ enum soc_type {
         SOC_ARCH_EXYNOS5420_TRIMINFO,
         SOC_ARCH_EXYNOS5433,
         SOC_ARCH_EXYNOS7,
+        SOC_ARCH_ARTPEC8,
+};
+
+#define SENSOR(_tr, _te, _sh, _ri, _fa, _pa, _en, _pend)        \
+        {                                        \
+                .trim_offset        = _tr,                \
+                .temp_offset        = _te,                \
+                .temp_reg_shift        = _sh,                \
+                .rise_offset        = _ri,                \
+                .fall_offset        = _fa,                \
+                .past_offset        = _pa,                \
+                .inten                = _en,                \
+                .intpend        = _pend,                \
+        }
+
+static const struct sensor_offset artpec8_sensors[] = {
+        SENSOR(0x0,        0x40,        0,  0x50,        0x60,        0x70,        0x110,        0x118),
+        SENSOR(0x4,        0x40,        9,  0x170,        0x180,        0x90,        0x120,        0x128),
+        SENSOR(0x8,        0x44,        0,  0x190,        0x1a0,        0xb0,        0x130,        0x138),
+        SENSOR(0xc,        0x44,        9,  0x1b0,        0x1c0,        0xd0,        0x140,        0x148),
+        SENSOR(0x10,        0x44,        18, 0x1d0,        0x1e0,        0xf0,        0x150,        0x158),
+        SENSOR(0x14,        0x48,        0,  0x1f0,        0x200,        0x250,        0x310,        0x318),
+};
+
+/**
+ * struct artpec8_sensor: A structure to hold the private data of the sensor
+ * @tmudev: The tmu device which this sensor is connected.
+ * @tzd: Thermal zonde device pointer to register this sensor.
+ * @id: Identifier of the one instance of the thermal sensor.
+ * @ntrip: Number of threshols for this sensor.
+ * @triminfo_25: OTP information to trim temperature sensor error for 25C
+ * @triminfo_105: OTP information to trim temperature sensor error for 105C
+ * @trim_offset: Offset of triminfo register.
+ * @temp_offset: Offset of current temperature. The temperature values of
+ *                2 to 3 remote sensors are stored in this register.
+ * @temp_reg_shift: start location of each tempt in temp_off
+ * @rise_offset: Offset of rising threshold level 6 and 7.
+ * @fall_offset: Offset of falling thershold level 6 and 7.
+ * @past_offset: Offset of Past temperature 0,1.
+ * @inten: Offset of interrupt enable sfr.
+ * @intpend: Offset of interrupt pending sfr.
+ */
+struct artpec8_sensor {
+        struct exynos_tmu_data *tmudev;
+        struct thermal_zone_device *tzd;
+        int id;
+        unsigned int ntrip;
+        u16 triminfo_25;
+        u16 triminfo_105;
+        u32 trim_offset;
+        u32 temp_offset;
+        u32 temp_reg_shift;
+        u32 rise_offset;
+        u32 fall_offset;
+        u32 past_offset;
+        u32 inten;
+        u32 intpend;
 };
 
 /**
@@ -193,6 +322,7 @@ struct exynos_tmu_data {
         struct thermal_zone_device *tzd;
         unsigned int ntrip;
         bool enabled;
+        u32 nr_remote;
 
         void (*tmu_set_trip_temp)(struct exynos_tmu_data *data, int trip,
                                  u8 temp);
@@ -203,6 +333,8 @@ struct exynos_tmu_data {
         int (*tmu_read)(struct exynos_tmu_data *data);
         void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp);
         void (*tmu_clear_irqs)(struct exynos_tmu_data *data);
+
+        struct artpec8_sensor sensor[0];
 };
 
 /*
@@ -220,6 +352,28 @@ static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
                 data->temp_error1;
 }
 
+static u16 artpec8_temp_to_code(struct artpec8_sensor *sensor, int temp)
+{
+        int code;
+        int weight;
+
+        if (sensor->tmudev->cal_type == TYPE_ONE_POINT_TRIMMING)
+                return temp + sensor->triminfo_25 - ARTPEC8_FIRST_POINT_TRIM;
+
+        if (temp > ARTPEC8_FIRST_POINT_TRIM)
+                weight = HIGH_TEMP_WEIGHT;
+        else
+                weight = LOW_TEMP_WEIGHT;
+
+        code = DIV_ROUND_CLOSEST((temp - ARTPEC8_FIRST_POINT_TRIM) *
+                (sensor->triminfo_105 - sensor->triminfo_25) * TEMP_WEIGHT,
+                (ARTPEC8_SECOND_POINT_TRIM - ARTPEC8_FIRST_POINT_TRIM) *
+                weight);
+        code += sensor->triminfo_25;
+
+        return (u16)code;
+}
+
 /*
  * Calculate a temperature value from a temperature code.
  * The unit of the temperature is degree Celsius.
@@ -235,6 +389,27 @@ static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code)
                 EXYNOS_FIRST_POINT_TRIM;
 }
 
+static int artpec8_code_to_temp(struct artpec8_sensor *sensor, u16 code)
+{
+        int temp;
+        int weight;
+
+        if (sensor->tmudev->cal_type == TYPE_ONE_POINT_TRIMMING)
+                return code - sensor->triminfo_25 + ARTPEC8_FIRST_POINT_TRIM;
+
+        if (code > sensor->triminfo_25)
+                weight = HIGH_TEMP_WEIGHT;
+        else
+                weight = LOW_TEMP_WEIGHT;
+
+        temp = DIV_ROUND_CLOSEST((code - sensor->triminfo_25) *
+                (ARTPEC8_SECOND_POINT_TRIM - ARTPEC8_FIRST_POINT_TRIM) * weight,
+                (sensor->triminfo_105 - sensor->triminfo_25) * TEMP_WEIGHT);
+        temp += ARTPEC8_FIRST_POINT_TRIM;
+
+        return temp;
+}
+
 static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info)
 {
         u16 tmu_temp_mask =
@@ -338,7 +513,8 @@ static u32 get_con_reg(struct exynos_tmu_data *data, u32 con)
         con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT);
         con |= data->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT;
 
-        con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
+        con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK <<
+                        EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
         con |= (data->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
 
         con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT);
@@ -558,6 +734,120 @@ static void exynos7_tmu_initialize(struct platform_device *pdev)
         sanitize_temp_error(data, trim_info);
 }
 
+static void artpec8_tmu_set_trip_temp(struct exynos_tmu_data *data,
+                int trip, int temp, int remote)
+{
+        unsigned int reg_off, bit_off;
+        u32 th;
+        struct artpec8_sensor *sensor;
+        unsigned int temp_rise;
+
+        sensor = &data->sensor[remote];
+        temp_rise = sensor->rise_offset;
+
+        reg_off = ((7 - trip) / 2) * 4;
+        bit_off = ((8 - trip) % 2);
+
+        th = readl(data->base + temp_rise + reg_off);
+        th &= ~(ARTPEC8_TMU_TEMP_MASK << (16 * bit_off));
+        th |= artpec8_temp_to_code(sensor, temp) << (16 * bit_off);
+        writel(th, data->base + temp_rise + reg_off);
+}
+
+static void artpec8_tmu_set_trip_hyst(struct exynos_tmu_data *data,
+                int trip, int temp, int hyst, int remote)
+{
+        unsigned int reg_off, bit_off;
+        u32 th;
+        struct artpec8_sensor *sensor;
+        unsigned int temp_fall;
+
+        sensor = &data->sensor[remote];
+        temp_fall = sensor->fall_offset;
+
+        reg_off = ((7 - trip) / 2) * 4;
+        bit_off = ((8 - trip) % 2);
+
+        th = readl(data->base + temp_fall + reg_off);
+        th &= ~(ARTPEC8_TMU_TEMP_MASK << (16 * bit_off));
+        th |= artpec8_temp_to_code(sensor, temp - hyst) << (16 * bit_off);
+        writel(th, data->base + temp_fall + reg_off);
+}
+
+static void artpec8_tmu_clear_irqs(struct exynos_tmu_data *data, int i)
+{
+        u32 intp = readl(data->base + data->sensor[i].intpend);
+
+        writel(intp, data->base + data->sensor[i].intpend);
+}
+
+static int artpec8_sensor_initialize(struct exynos_tmu_data *data,
+                int sensor_idx)
+{
+        struct thermal_zone_device *tzd;
+        struct artpec8_sensor *sensor;
+        int ret = 0, trip, temp, hyst;
+
+        sensor = &data->sensor[sensor_idx];
+        if (!sensor)
+                return -EINVAL;
+
+        tzd = sensor->tzd;
+        for (trip = 0; trip < sensor->ntrip; trip++) {
+                ret = tzd->ops->get_trip_temp(tzd, trip, &temp);
+                if (ret)
+                        break;
+
+                temp /= MCELSIUS;
+                artpec8_tmu_set_trip_temp(data, trip, temp, sensor_idx);
+
+                ret = tzd->ops->get_trip_hyst(tzd, trip, &hyst);
+                if (ret)
+                        break;
+
+                hyst /= MCELSIUS;
+                artpec8_tmu_set_trip_hyst(data, trip, temp, hyst, sensor_idx);
+        }
+        artpec8_tmu_clear_irqs(data, sensor_idx);
+
+        return ret;
+}
+
+static void artpec8_tmu_initialize(struct platform_device *pdev)
+{
+        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+        int ret = 0, sensor_idx;
+
+        mutex_lock(&data->lock);
+
+        for (sensor_idx = 0; sensor_idx < data->nr_remote; sensor_idx++) {
+                if (!readb(data->base + ARTPEC8_TMU_REG_STATUS))
+                        break;
+
+                ret = artpec8_sensor_initialize(data, sensor_idx);
+                if (ret)
+                        break;
+        }
+
+        mutex_unlock(&data->lock);
+}
+
+static void artpec8_enable_interrupt(struct exynos_tmu_data *data,
+                int sensor_idx)
+{
+        int i;
+        unsigned int interrupt_en = 0;
+        struct thermal_zone_device *tz = data->sensor[sensor_idx].tzd;
+
+        for (i = 0; i < data->sensor->ntrip; i++) {
+                if (!of_thermal_is_trip_valid(tz, i))
+                        continue;
+                interrupt_en |= (1 << i);
+        }
+        writel(interrupt_en, data->base + data->sensor[sensor_idx].inten);
+}
+
+
 static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
 {
         struct exynos_tmu_data *data = platform_get_drvdata(pdev);
@@ -650,6 +940,62 @@ static void exynos7_tmu_control(struct platform_device *pdev, bool on)
         writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
 }
 
+static void artpec8_tmu_control(struct platform_device *pdev, bool on)
+{
+        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+        unsigned int con, con1, i;
+        unsigned int vref;
+        unsigned int slope;
+        unsigned int trim0, trim0_bgr, trim0_vref, trim0_vbe, avg_mode;
+
+        vref = (readl(data->base + ARTPEC8_TMU_REG_TRIMINFO) >>
+                ARTPEC8_T_BUF_VREF_SEL_SHIFT) &
+                ARTPEC8_TMU_REF_VOLTAGE_MASK;
+        slope = (readl(data->base + ARTPEC8_TMU_REG_TRIMINFO1) >>
+                ARTPEC8_T_BUF_SLOPE_SEL_SHIFT) &
+                ARTPEC8_TMU_BUF_SLOPE_SEL_MASK;
+        con = (vref << ARTPEC8_TMU_REF_VOLTAGE_SHIFT) |
+                (slope << ARTPEC8_TMU_BUF_SLOPE_SEL_SHIFT);
+
+        if (on) {
+                for (i = 0; i < data->nr_remote; i++)
+                        artpec8_enable_interrupt(data, i);
+                con |= (ARTPEC8_TMU_CONTROL_CORE_EN |
+                        ARTPEC8_TMU_CONTROL_AUTO_MODE);
+        } else {
+                con &= ~(ARTPEC8_TMU_CONTROL_CORE_EN);
+        }
+
+        trim0_bgr = readl(data->base + ARTPEC8_TMU_REG_TRIMINFO3) >>
+                ARTPEC8_TRIMINFO_TRIM0_SHIFT;
+        trim0_bgr &= ARTPEC8_TRIM0_MASK;
+        trim0_vref = readl(data->base + ARTPEC8_TMU_REG_TRIMINFO4) >>
+                ARTPEC8_TRIMINFO_TRIM0_SHIFT;
+        trim0_vref &= ARTPEC8_TRIM0_MASK;
+        trim0_vbe = readl(data->base + ARTPEC8_TMU_REG_TRIMINFO5) >>
+                ARTPEC8_TRIMINFO_TRIM0_SHIFT;
+        trim0_vbe &= ARTPEC8_TRIM0_MASK;
+        trim0 = trim0_bgr << ARTPEC8_TRIM0_BGR_I_SHIFT |
+                trim0_vref << ARTPEC8_TRIM0_VREF_SHIFT |
+                trim0_vbe << ARTPEC8_TRIM0_VBE_I_SHIFT;
+
+        con1 = (data->nr_remote << NUM_PROBE_OFFSET) | ARTPEC8_LPI_MODE_EN;
+
+        while (!readb(data->base + ARTPEC8_TMU_REG_STATUS))
+                pr_debug("TMU busy waiting\n");
+
+
+        avg_mode = readl(data->base + ARTPEC8_TMU_REG_AVG_CONTROL);
+        avg_mode &= ~ARTPEC8_TRIM2_MASK;
+        avg_mode |= (readl(data->base + ARTPEC8_TMU_REG_TRIMINFO2) >>
+                ARTPEC8_TRIMINFO_TRIM0_SHIFT) & ARTPEC8_TRIM2_MASK;
+
+        writel(avg_mode, data->base + ARTPEC8_TMU_REG_AVG_CONTROL);
+        writel(trim0, data->base + ARTPEC8_TMU_REG_TMU_TRIM0);
+        writel(con1, data->base + ARTPEC8_TMU_REG_CONTROL1);
+        writel(con, data->base + ARTPEC8_TMU_REG_CONTROL);
+}
+
 static int exynos_get_temp(void *p, int *temp)
 {
         struct exynos_tmu_data *data = p;
@@ -679,6 +1025,37 @@ static int exynos_get_temp(void *p, int *temp)
         return ret;
 }
 
+static int artpec8_get_temp(void *p, int *temp)
+{
+        struct artpec8_sensor *sensor = p;
+        struct exynos_tmu_data *data;
+        bool enabled, valid;
+        u16 value;
+        int ret = 0;
+
+        if (!sensor)
+                return -EINVAL;
+
+        data = sensor->tmudev;
+        if (!data)
+                return -EINVAL;
+
+        enabled = readl(data->base + ARTPEC8_TMU_REG_CONTROL) & 0x1;
+        valid = readl(data->base + ARTPEC8_TMU_REG_STATUS) & 0xf0;
+        if (!enabled || !valid)
+                return -EAGAIN;
+
+        mutex_lock(&data->lock);
+
+        value = (readl(data->base + sensor->temp_offset) >>
+                sensor->temp_reg_shift) & ARTPEC8_TMU_TEMP_MASK;
+        *temp = artpec8_code_to_temp(sensor, value) * MCELSIUS;
+
+        mutex_unlock(&data->lock);
+
+        return ret;
+}
+
 #ifdef CONFIG_THERMAL_EMULATION
 static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val,
                             int temp)
@@ -748,10 +1125,32 @@ static int exynos_tmu_set_emulation(void *drv_data, int temp)
 out:
         return ret;
 }
+
+static int artpec8_tmu_set_emulation(void *sensor_data, int temp)
+{
+        struct artpec8_sensor *sensor = sensor_data;
+        struct exynos_tmu_data *data = sensor->tmudev;
+        u32 temp_code, econ;
+
+        temp /= MCELSIUS;
+
+        mutex_lock(&data->lock);
+
+        temp_code = artpec8_temp_to_code(sensor, temp);
+        econ = ARTPEC8_EMUL_NEXT_TIME | temp_code << ARTPEC8_EMUL_DATA_SHIFT |
+                ARTPEC8_EMUL_EN;
+        writel(econ, data->base + ARTPEC8_TMU_REG_EMUL_CON);
+
+        mutex_unlock(&data->lock);
+
+        return 0;
+}
 #else
 #define exynos4412_tmu_set_emulation NULL
 static int exynos_tmu_set_emulation(void *drv_data, int temp)
         { return -EINVAL; }
+static int artpec8_tmu_set_emulation(void *sensor_data, int temp)
+        { return -EINVAL; }
 #endif /* CONFIG_THERMAL_EMULATION */
 
 static int exynos4210_tmu_read(struct exynos_tmu_data *data)
@@ -791,6 +1190,45 @@ static void exynos_tmu_work(struct work_struct *work)
         enable_irq(data->irq);
 }
 
+static void artpec8_tmu_work(struct work_struct *work)
+{
+        int i;
+        u32 inten, intpend, rise, fall;
+        struct artpec8_sensor *sensor;
+        struct exynos_tmu_data *data = container_of(work,
+                        struct exynos_tmu_data, irq_work);
+
+        for (i = 0; i < data->nr_remote; i++)
+                thermal_zone_device_update(data->sensor[i].tzd,
+                                THERMAL_EVENT_UNSPECIFIED);
+
+        mutex_lock(&data->lock);
+        for (i = 0; i < data->nr_remote; i++) {
+                sensor = &data->sensor[i];
+                intpend = readl(data->base + sensor->intpend);
+
+                if (intpend) {
+                        fall = intpend & INTPEND_FALL_MASK;
+                        rise = intpend & INTPEND_RISE_MASK;
+
+                        if (fall) {
+                                inten = readl(data->base + sensor->inten) & (~fall);
+                                inten |= fall >> ARTPEC8_INTEN_FALL0_SHIFT;
+                                writel(inten, data->base + sensor->inten);
+                        }
+
+                        if (rise) {
+                                inten = readl(data->base + sensor->inten) & (~rise);
+                                inten |= (rise << ARTPEC8_INTEN_FALL0_SHIFT) |
+                                        min_t(u32, rise << 1, BIT(sensor->ntrip - 1)) | (rise >> 1);
+                                writel(inten, data->base + sensor->inten);
+                        }
+                        writel(intpend, data->base + sensor->intpend);
+                }
+        }
+        mutex_unlock(&data->lock);
+}
+
 static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data)
 {
         unsigned int val_irq;
@@ -860,6 +1298,9 @@ static const struct of_device_id exynos_tmu_match[] = {
         }, {
                 .compatible = "samsung,exynos7-tmu",
                 .data = (const void *)SOC_ARCH_EXYNOS7,
+        }, {
+                .compatible = "axis,artpec8-tmu",
+                .data = (const void *)SOC_ARCH_ARTPEC8,
         },
         { },
 };
@@ -895,6 +1336,7 @@ static int exynos_map_dt_data(struct platform_device *pdev)
         }
 
         data->soc = (enum soc_type)of_device_get_match_data(&pdev->dev);
+        data->cal_type = TYPE_ONE_POINT_TRIMMING;
 
         switch (data->soc) {
         case SOC_ARCH_EXYNOS4210:
@@ -968,13 +1410,19 @@ static int exynos_map_dt_data(struct platform_device *pdev)
                 data->min_efuse_value = 15;
                 data->max_efuse_value = 100;
                 break;
+        case SOC_ARCH_ARTPEC8:
+                data->tmu_initialize = artpec8_tmu_initialize;
+                data->tmu_control = artpec8_tmu_control;
+                data->cal_type = readl(data->base + ARTPEC8_TMU_REG_TRIMINFO) >>
+                        ARTPEC8_CALIB_SEL_SHIFT;
+                of_property_read_u32(pdev->dev.of_node, "remote_sensors",
+                                &data->nr_remote);
+                break;
         default:
                 dev_err(&pdev->dev, "Platform not supported\n");
                 return -EINVAL;
         }
 
-        data->cal_type = TYPE_ONE_POINT_TRIMMING;
-
         /*
          * Check if the TMU shares some registers and then try to map the
          * memory of common registers.
@@ -1002,42 +1450,138 @@ static const struct thermal_zone_of_device_ops exynos_sensor_ops = {
         .set_emul_temp = exynos_tmu_set_emulation,
 };
 
+static const struct thermal_zone_of_device_ops artpec8_ops = {
+        .get_temp = artpec8_get_temp,
+        .set_emul_temp = artpec8_tmu_set_emulation,
+};
+
+static int artpec8_map_sensor_data(struct exynos_tmu_data *data,
+                int sensor_idx, struct artpec8_sensor *sensor)
+{
+        int id = sensor_idx;
+
+        sensor->id = id;
+        sensor->tmudev = data;
+        sensor->trim_offset = artpec8_sensors[id].trim_offset;
+        sensor->temp_offset = artpec8_sensors[id].temp_offset;
+        sensor->temp_reg_shift = artpec8_sensors[id].temp_reg_shift;
+        sensor->rise_offset = artpec8_sensors[id].rise_offset;
+        sensor->fall_offset = artpec8_sensors[id].fall_offset;
+        sensor->past_offset = artpec8_sensors[id].past_offset;
+        sensor->inten = artpec8_sensors[id].inten;
+        sensor->intpend = artpec8_sensors[id].intpend;
+        sensor->triminfo_25 = readl(data->base + sensor->trim_offset) &
+                ARTPEC8_TMU_TEMP_MASK;
+        sensor->triminfo_105 = (readl(data->base + sensor->trim_offset) >>
+                ARTPEC8_TRIMINFO_105_SHIFT) & ARTPEC8_TMU_TEMP_MASK;
+
+        return 0;
+}
+
+static int artpec8_register_tzd(struct platform_device *pdev)
+{
+        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+        struct artpec8_sensor *sensor;
+        struct device *dev = &pdev->dev;
+        int sensor_idx, ret = 0;
+        struct thermal_zone_device *tzd;
+        const struct thermal_trip *trips;
+
+        for (sensor_idx = 0; sensor_idx < data->nr_remote; sensor_idx++) {
+                sensor = &data->sensor[sensor_idx];
+
+                ret = artpec8_map_sensor_data(data, sensor_idx, sensor);
+                if (ret)
+                        break;
+
+                tzd = devm_thermal_zone_of_sensor_register(dev,
+                                sensor_idx, sensor, &artpec8_ops);
+                if (IS_ERR(tzd))
+                        continue;
+
+                sensor->tzd = tzd;
+                trips = of_thermal_get_trip_points(tzd);
+                if (!trips) {
+                        dev_warn(dev,
+                                "Cannot get trip points from device tree!\n");
+                        ret = -ENODEV;
+                        break;
+                }
+                sensor->ntrip = of_thermal_get_ntrips(tzd);
+        }
+
+        return ret;
+}
+
 static int exynos_tmu_probe(struct platform_device *pdev)
 {
         struct exynos_tmu_data *data;
         int ret;
+        int sensor_idx;
+        int nr_remote = 0;
+        struct device *dev;
+        const struct of_device_id *dev_id;
 
-        data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
-                                        GFP_KERNEL);
+        if (pdev->dev.of_node)
+                dev = &pdev->dev;
+        else
+                dev = pdev->dev.parent;
+
+        dev_id = of_match_node(exynos_tmu_match, dev->of_node);
+        if (dev_id) {
+                data = (struct exynos_tmu_data *)dev_id->data;
+        } else {
+                dev_warn(dev, "dev id error\n");
+                return -EINVAL;
+        }
+
+        ret = of_property_read_u32(dev->of_node, "remote_sensors", &nr_remote);
+        if (ret < 0)
+                data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
+                                    GFP_KERNEL);
+        else
+                data = devm_kzalloc(dev, sizeof(struct exynos_tmu_data) +
+                        (sizeof(struct artpec8_sensor) * nr_remote),
+                        GFP_KERNEL);
         if (!data)
                 return -ENOMEM;
 
         platform_set_drvdata(pdev, data);
         mutex_init(&data->lock);
 
+        if (data->soc != SOC_ARCH_ARTPEC8) {
         /*
          * Try enabling the regulator if found
          * TODO: Add regulator as an SOC feature, so that regulator enable
          * is a compulsory call.
          */
-        data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu");
-        if (!IS_ERR(data->regulator)) {
-                ret = regulator_enable(data->regulator);
-                if (ret) {
-                        dev_err(&pdev->dev, "failed to enable vtmu\n");
-                        return ret;
+                data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu");
+                if (!IS_ERR(data->regulator)) {
+                        ret = regulator_enable(data->regulator);
+                        if (ret) {
+                                dev_err(&pdev->dev, "failed to enable vtmu\n");
+                                return ret;
+                        }
+                } else {
+                        if (PTR_ERR(data->regulator) == -EPROBE_DEFER)
+                                return -EPROBE_DEFER;
+                        dev_info(&pdev->dev, "Regulator node (vtmu) not found\n");
                 }
-        } else {
-                if (PTR_ERR(data->regulator) == -EPROBE_DEFER)
-                        return -EPROBE_DEFER;
-                dev_info(&pdev->dev, "Regulator node (vtmu) not found\n");
         }
 
         ret = exynos_map_dt_data(pdev);
         if (ret)
                 goto err_sensor;
 
-        INIT_WORK(&data->irq_work, exynos_tmu_work);
+        if (data->soc == SOC_ARCH_ARTPEC8) {
+                ret = artpec8_register_tzd(pdev);
+                if (ret)
+                        return -EINVAL;
+
+                INIT_WORK(&data->irq_work, artpec8_tmu_work);
+        } else {
+                INIT_WORK(&data->irq_work, exynos_tmu_work);
+        }
 
         data->clk = devm_clk_get(&pdev->dev, "tmu_apbif");
         if (IS_ERR(data->clk)) {
@@ -1046,18 +1590,21 @@ static int exynos_tmu_probe(struct platform_device *pdev)
                 goto err_sensor;
         }
 
-        data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif");
-        if (IS_ERR(data->clk_sec)) {
-                if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) {
-                        dev_err(&pdev->dev, "Failed to get triminfo clock\n");
-                        ret = PTR_ERR(data->clk_sec);
-                        goto err_sensor;
-                }
-        } else {
-                ret = clk_prepare(data->clk_sec);
-                if (ret) {
-                        dev_err(&pdev->dev, "Failed to get clock\n");
-                        goto err_sensor;
+        if (data->soc != SOC_ARCH_ARTPEC8) {
+                data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif");
+                if (IS_ERR(data->clk_sec)) {
+                        if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) {
+                                dev_err(&pdev->dev,
+                                        "Failed to get triminfo clock\n");
+                                ret = PTR_ERR(data->clk_sec);
+                                goto err_sensor;
+                        }
+                } else {
+                        ret = clk_prepare(data->clk_sec);
+                        if (ret) {
+                                dev_err(&pdev->dev, "Failed to get clock\n");
+                                goto err_sensor;
+                        }
                 }
         }
 
@@ -1070,6 +1617,7 @@ static int exynos_tmu_probe(struct platform_device *pdev)
         switch (data->soc) {
         case SOC_ARCH_EXYNOS5433:
         case SOC_ARCH_EXYNOS7:
+        case SOC_ARCH_ARTPEC8:
                 data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk");
                 if (IS_ERR(data->sclk)) {
                         dev_err(&pdev->dev, "Failed to get sclk\n");
@@ -1087,24 +1635,26 @@ static int exynos_tmu_probe(struct platform_device *pdev)
                 break;
         }
 
+        if (data->soc != SOC_ARCH_ARTPEC8) {
         /*
          * data->tzd must be registered before calling exynos_tmu_initialize(),
          * requesting irq and calling exynos_tmu_control().
          */
-        data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data,
-                                                    &exynos_sensor_ops);
-        if (IS_ERR(data->tzd)) {
-                ret = PTR_ERR(data->tzd);
-                if (ret != -EPROBE_DEFER)
-                        dev_err(&pdev->dev, "Failed to register sensor: %d\n",
-                                ret);
-                goto err_sclk;
-        }
+                data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data,
+                                                            &exynos_sensor_ops);
+                if (IS_ERR(data->tzd)) {
+                        ret = PTR_ERR(data->tzd);
+                        if (ret != -EPROBE_DEFER)
+                                dev_err(&pdev->dev,
+                                        "Failed to register sensor: %d\n", ret);
+                        goto err_sclk;
+                }
 
-        ret = exynos_tmu_initialize(pdev);
-        if (ret) {
-                dev_err(&pdev->dev, "Failed to initialize TMU\n");
-                goto err_thermal;
+                ret = exynos_tmu_initialize(pdev);
+                if (ret) {
+                        dev_err(&pdev->dev, "Failed to initialize TMU\n");
+                        goto err_thermal;
+                }
         }
 
         ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq,
@@ -1118,7 +1668,13 @@ static int exynos_tmu_probe(struct platform_device *pdev)
         return 0;
 
 err_thermal:
-        thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd);
+        if (data->soc == SOC_ARCH_ARTPEC8) {
+                for (sensor_idx = 0; sensor_idx < data->nr_remote; sensor_idx++)
+                        thermal_zone_of_sensor_unregister(
+                                dev, data->sensor[sensor_idx].tzd);
+        } else {
+                thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd);
+        }
 err_sclk:
         clk_disable_unprepare(data->sclk);
 err_clk:
@@ -1137,17 +1693,27 @@ static int exynos_tmu_remove(struct platform_device *pdev)
 {
         struct exynos_tmu_data *data = platform_get_drvdata(pdev);
         struct thermal_zone_device *tzd = data->tzd;
+        struct device *dev = &pdev->dev;
+        int i;
 
-        thermal_zone_of_sensor_unregister(&pdev->dev, tzd);
-        exynos_tmu_control(pdev, false);
+        if (data->soc == SOC_ARCH_ARTPEC8) {
+                for (i = 0; i < data->nr_remote; i++)
+                        thermal_zone_of_sensor_unregister(dev, data->sensor[i].tzd);
+                exynos_tmu_control(pdev, false);
 
-        clk_disable_unprepare(data->sclk);
-        clk_unprepare(data->clk);
-        if (!IS_ERR(data->clk_sec))
-                clk_unprepare(data->clk_sec);
+                clk_unprepare(data->clk);
+        } else {
+                thermal_zone_of_sensor_unregister(&pdev->dev, tzd);
+                exynos_tmu_control(pdev, false);
 
-        if (!IS_ERR(data->regulator))
-                regulator_disable(data->regulator);
+                clk_disable_unprepare(data->sclk);
+                clk_unprepare(data->clk);
+                if (!IS_ERR(data->clk_sec))
+                        clk_unprepare(data->clk_sec);
+
+                if (!IS_ERR(data->regulator))
+                        regulator_disable(data->regulator);
+        }
 
         return 0;
 }
-- 
2.9.5
 
 

WARNING: multiple messages have this Message-ID (diff)
From: Sang Min Kim <hypmean.kim@samsung.com>
To: "bzolnier@gmail.com" <bzolnier@gmail.com>,
	"krzysztof.kozlowski@canonical.com"
	<krzysztof.kozlowski@canonical.com>,
	"rafael@kernel.org" <rafael@kernel.org>,
	"daniel.lezcano@linaro.org" <daniel.lezcano@linaro.org>,
	"amitk@kernel.org" <amitk@kernel.org>,
	"rui.zhang@intel.com" <rui.zhang@intel.com>,
	ALIM AKHTAR <alim.akhtar@samsung.com>,
	"lgirdwood@gmail.com" <lgirdwood@gmail.com>,
	"broonie@kernel.org" <broonie@kernel.org>
Cc: "linux-pm@vger.kernel.org" <linux-pm@vger.kernel.org>,
	"linux-samsung-soc@vger.kernel.org"
	<linux-samsung-soc@vger.kernel.org>,
	"linux-arm-kernel@lists.infradead.org"
	<linux-arm-kernel@lists.infradead.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
Subject: [PATCH 2/2] thermal: exynos: Add support for ARTPEC-8
Date: Tue, 22 Mar 2022 17:22:42 +0900	[thread overview]
Message-ID: <20220322082242epcms2p5b5e9e24a947e1d6c4b77fb9c95baf992@epcms2p5> (raw)
In-Reply-To: <20220322075638.110056-1-hypmean.kim@samsung.com>

Add support thermal management for Axis ARTPEC-8 SoC.
ARTPEC-8 is the SoC platform of Axis Communications.
In the existing thermal management function of exynos, functions that support
remote sensors have been added.
 
Signed-off-by: sangmin kim <hypmean.kim@samsung.com>
---
 drivers/thermal/samsung/exynos_tmu.c | 666 ++++++++++++++++++++++++++++++++---
 1 file changed, 616 insertions(+), 50 deletions(-)
 
diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c
index f4ab4c5..9837f42 100644
--- a/drivers/thermal/samsung/exynos_tmu.c
+++ b/drivers/thermal/samsung/exynos_tmu.c
@@ -14,6 +14,7 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
+#include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/of_address.h>
@@ -124,6 +125,77 @@
 
 #define MCELSIUS        1000
 
+/* Artpec8 specific registers */
+#define ARTPEC8_TMU_REG_TRIMINFO        0x0
+#define ARTPEC8_TMU_REG_TRIMINFO1        0x4
+#define ARTPEC8_TMU_REG_TRIMINFO2        0x8
+#define ARTPEC8_TMU_REG_TRIMINFO3        0xC
+#define ARTPEC8_TMU_REG_TRIMINFO4        0x10
+#define ARTPEC8_TMU_REG_TRIMINFO5        0x14
+#define ARTPEC8_TMU_REG_CONTROL                0x20
+#define ARTPEC8_TMU_REG_CONTROL1        0x24
+#define ARTPEC8_TMU_REG_STATUS                0x28
+
+#define ARTPEC8_TMU_REG_AVG_CONTROL        0x38
+#define ARTPEC8_TMU_REG_TMU_TRIM0        0x3C
+
+#define ARTPEC8_TMU_REG_EMUL_CON        0x160
+#define NUM_PROBE_OFFSET                16
+
+#define ARTPEC8_FIRST_POINT_TRIM        25
+#define ARTPEC8_SECOND_POINT_TRIM        105
+
+#define ARTPEC8_EMUL_EN                        1
+#define ARTPEC8_TIME_OFFSET                16
+#define ARTPEC8_EMUL_NEXT_TIME                (0x4e20 << ARTPEC8_TIME_OFFSET)
+
+#define ARTPEC8_TMU_TEMP_MASK                0x1ff
+#define ARTPEC8_CALIB_SEL_SHIFT                23
+
+#define ARTPEC8_EMUL_DATA_SHIFT                7
+
+#define ARTPEC8_T_BUF_VREF_SEL_SHIFT        18
+#define ARTPEC8_T_BUF_SLOPE_SEL_SHIFT        18
+#define ARTPEC8_INTEN_TRIPPING_SHIFT        7
+#define ARTPEC8_INTEN_CLOCKDOWN_SHIFT        8
+#define ARTPEC8_TRIMINFO_105_SHIFT        9
+#define ARTPEC8_INTEN_FALL0_SHIFT        16
+#define ARTPEC8_TMU_REF_VOLTAGE_SHIFT        24
+#define ARTPEC8_TMU_REF_VOLTAGE_MASK        0x1f
+#define ARTPEC8_TMU_BUF_SLOPE_SEL_SHIFT        8
+#define ARTPEC8_TMU_BUF_SLOPE_SEL_MASK        0xf
+
+#define ARTPEC8_TMU_CONTROL_CORE_EN        1
+#define ARTPEC8_TMU_CONTROL_AUTO_MODE        2
+#define ARTPEC8_TMU_CONTROL_TRIP_EN        (1 << 12)
+#define ARTPEC8_LPI_MODE_EN                (1 << 10)
+
+#define ARTPEC8_TRIM0_BGR_I_SHIFT        20
+#define ARTPEC8_TRIM0_VREF_SHIFT        12
+#define ARTPEC8_TRIM0_VBE_I_SHIFT        8
+
+#define INTPEND_RISE_MASK                0xff
+#define INTPEND_FALL_MASK                0xff0000
+#define ARTPEC8_TRIM0_MASK                0xf
+#define ARTPEC8_TRIM2_MASK                0x7
+
+#define ARTPEC8_TRIMINFO_TRIM0_SHIFT        18
+
+#define LOW_TEMP_WEIGHT                        9203
+#define HIGH_TEMP_WEIGHT                9745
+#define TEMP_WEIGHT                        10000
+
+struct sensor_offset {
+        u32 trim_offset;
+        u32 temp_offset;
+        u32 temp_reg_shift;
+        u32 rise_offset;
+        u32 fall_offset;
+        u32 past_offset;
+        u32 inten;
+        u32 intpend;
+};
+
 enum soc_type {
         SOC_ARCH_EXYNOS3250 = 1,
         SOC_ARCH_EXYNOS4210,
@@ -134,6 +206,63 @@ enum soc_type {
         SOC_ARCH_EXYNOS5420_TRIMINFO,
         SOC_ARCH_EXYNOS5433,
         SOC_ARCH_EXYNOS7,
+        SOC_ARCH_ARTPEC8,
+};
+
+#define SENSOR(_tr, _te, _sh, _ri, _fa, _pa, _en, _pend)        \
+        {                                        \
+                .trim_offset        = _tr,                \
+                .temp_offset        = _te,                \
+                .temp_reg_shift        = _sh,                \
+                .rise_offset        = _ri,                \
+                .fall_offset        = _fa,                \
+                .past_offset        = _pa,                \
+                .inten                = _en,                \
+                .intpend        = _pend,                \
+        }
+
+static const struct sensor_offset artpec8_sensors[] = {
+        SENSOR(0x0,        0x40,        0,  0x50,        0x60,        0x70,        0x110,        0x118),
+        SENSOR(0x4,        0x40,        9,  0x170,        0x180,        0x90,        0x120,        0x128),
+        SENSOR(0x8,        0x44,        0,  0x190,        0x1a0,        0xb0,        0x130,        0x138),
+        SENSOR(0xc,        0x44,        9,  0x1b0,        0x1c0,        0xd0,        0x140,        0x148),
+        SENSOR(0x10,        0x44,        18, 0x1d0,        0x1e0,        0xf0,        0x150,        0x158),
+        SENSOR(0x14,        0x48,        0,  0x1f0,        0x200,        0x250,        0x310,        0x318),
+};
+
+/**
+ * struct artpec8_sensor: A structure to hold the private data of the sensor
+ * @tmudev: The tmu device which this sensor is connected.
+ * @tzd: Thermal zonde device pointer to register this sensor.
+ * @id: Identifier of the one instance of the thermal sensor.
+ * @ntrip: Number of threshols for this sensor.
+ * @triminfo_25: OTP information to trim temperature sensor error for 25C
+ * @triminfo_105: OTP information to trim temperature sensor error for 105C
+ * @trim_offset: Offset of triminfo register.
+ * @temp_offset: Offset of current temperature. The temperature values of
+ *                2 to 3 remote sensors are stored in this register.
+ * @temp_reg_shift: start location of each tempt in temp_off
+ * @rise_offset: Offset of rising threshold level 6 and 7.
+ * @fall_offset: Offset of falling thershold level 6 and 7.
+ * @past_offset: Offset of Past temperature 0,1.
+ * @inten: Offset of interrupt enable sfr.
+ * @intpend: Offset of interrupt pending sfr.
+ */
+struct artpec8_sensor {
+        struct exynos_tmu_data *tmudev;
+        struct thermal_zone_device *tzd;
+        int id;
+        unsigned int ntrip;
+        u16 triminfo_25;
+        u16 triminfo_105;
+        u32 trim_offset;
+        u32 temp_offset;
+        u32 temp_reg_shift;
+        u32 rise_offset;
+        u32 fall_offset;
+        u32 past_offset;
+        u32 inten;
+        u32 intpend;
 };
 
 /**
@@ -193,6 +322,7 @@ struct exynos_tmu_data {
         struct thermal_zone_device *tzd;
         unsigned int ntrip;
         bool enabled;
+        u32 nr_remote;
 
         void (*tmu_set_trip_temp)(struct exynos_tmu_data *data, int trip,
                                  u8 temp);
@@ -203,6 +333,8 @@ struct exynos_tmu_data {
         int (*tmu_read)(struct exynos_tmu_data *data);
         void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp);
         void (*tmu_clear_irqs)(struct exynos_tmu_data *data);
+
+        struct artpec8_sensor sensor[0];
 };
 
 /*
@@ -220,6 +352,28 @@ static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
                 data->temp_error1;
 }
 
+static u16 artpec8_temp_to_code(struct artpec8_sensor *sensor, int temp)
+{
+        int code;
+        int weight;
+
+        if (sensor->tmudev->cal_type == TYPE_ONE_POINT_TRIMMING)
+                return temp + sensor->triminfo_25 - ARTPEC8_FIRST_POINT_TRIM;
+
+        if (temp > ARTPEC8_FIRST_POINT_TRIM)
+                weight = HIGH_TEMP_WEIGHT;
+        else
+                weight = LOW_TEMP_WEIGHT;
+
+        code = DIV_ROUND_CLOSEST((temp - ARTPEC8_FIRST_POINT_TRIM) *
+                (sensor->triminfo_105 - sensor->triminfo_25) * TEMP_WEIGHT,
+                (ARTPEC8_SECOND_POINT_TRIM - ARTPEC8_FIRST_POINT_TRIM) *
+                weight);
+        code += sensor->triminfo_25;
+
+        return (u16)code;
+}
+
 /*
  * Calculate a temperature value from a temperature code.
  * The unit of the temperature is degree Celsius.
@@ -235,6 +389,27 @@ static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code)
                 EXYNOS_FIRST_POINT_TRIM;
 }
 
+static int artpec8_code_to_temp(struct artpec8_sensor *sensor, u16 code)
+{
+        int temp;
+        int weight;
+
+        if (sensor->tmudev->cal_type == TYPE_ONE_POINT_TRIMMING)
+                return code - sensor->triminfo_25 + ARTPEC8_FIRST_POINT_TRIM;
+
+        if (code > sensor->triminfo_25)
+                weight = HIGH_TEMP_WEIGHT;
+        else
+                weight = LOW_TEMP_WEIGHT;
+
+        temp = DIV_ROUND_CLOSEST((code - sensor->triminfo_25) *
+                (ARTPEC8_SECOND_POINT_TRIM - ARTPEC8_FIRST_POINT_TRIM) * weight,
+                (sensor->triminfo_105 - sensor->triminfo_25) * TEMP_WEIGHT);
+        temp += ARTPEC8_FIRST_POINT_TRIM;
+
+        return temp;
+}
+
 static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info)
 {
         u16 tmu_temp_mask =
@@ -338,7 +513,8 @@ static u32 get_con_reg(struct exynos_tmu_data *data, u32 con)
         con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT);
         con |= data->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT;
 
-        con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
+        con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK <<
+                        EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
         con |= (data->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
 
         con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT);
@@ -558,6 +734,120 @@ static void exynos7_tmu_initialize(struct platform_device *pdev)
         sanitize_temp_error(data, trim_info);
 }
 
+static void artpec8_tmu_set_trip_temp(struct exynos_tmu_data *data,
+                int trip, int temp, int remote)
+{
+        unsigned int reg_off, bit_off;
+        u32 th;
+        struct artpec8_sensor *sensor;
+        unsigned int temp_rise;
+
+        sensor = &data->sensor[remote];
+        temp_rise = sensor->rise_offset;
+
+        reg_off = ((7 - trip) / 2) * 4;
+        bit_off = ((8 - trip) % 2);
+
+        th = readl(data->base + temp_rise + reg_off);
+        th &= ~(ARTPEC8_TMU_TEMP_MASK << (16 * bit_off));
+        th |= artpec8_temp_to_code(sensor, temp) << (16 * bit_off);
+        writel(th, data->base + temp_rise + reg_off);
+}
+
+static void artpec8_tmu_set_trip_hyst(struct exynos_tmu_data *data,
+                int trip, int temp, int hyst, int remote)
+{
+        unsigned int reg_off, bit_off;
+        u32 th;
+        struct artpec8_sensor *sensor;
+        unsigned int temp_fall;
+
+        sensor = &data->sensor[remote];
+        temp_fall = sensor->fall_offset;
+
+        reg_off = ((7 - trip) / 2) * 4;
+        bit_off = ((8 - trip) % 2);
+
+        th = readl(data->base + temp_fall + reg_off);
+        th &= ~(ARTPEC8_TMU_TEMP_MASK << (16 * bit_off));
+        th |= artpec8_temp_to_code(sensor, temp - hyst) << (16 * bit_off);
+        writel(th, data->base + temp_fall + reg_off);
+}
+
+static void artpec8_tmu_clear_irqs(struct exynos_tmu_data *data, int i)
+{
+        u32 intp = readl(data->base + data->sensor[i].intpend);
+
+        writel(intp, data->base + data->sensor[i].intpend);
+}
+
+static int artpec8_sensor_initialize(struct exynos_tmu_data *data,
+                int sensor_idx)
+{
+        struct thermal_zone_device *tzd;
+        struct artpec8_sensor *sensor;
+        int ret = 0, trip, temp, hyst;
+
+        sensor = &data->sensor[sensor_idx];
+        if (!sensor)
+                return -EINVAL;
+
+        tzd = sensor->tzd;
+        for (trip = 0; trip < sensor->ntrip; trip++) {
+                ret = tzd->ops->get_trip_temp(tzd, trip, &temp);
+                if (ret)
+                        break;
+
+                temp /= MCELSIUS;
+                artpec8_tmu_set_trip_temp(data, trip, temp, sensor_idx);
+
+                ret = tzd->ops->get_trip_hyst(tzd, trip, &hyst);
+                if (ret)
+                        break;
+
+                hyst /= MCELSIUS;
+                artpec8_tmu_set_trip_hyst(data, trip, temp, hyst, sensor_idx);
+        }
+        artpec8_tmu_clear_irqs(data, sensor_idx);
+
+        return ret;
+}
+
+static void artpec8_tmu_initialize(struct platform_device *pdev)
+{
+        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+        int ret = 0, sensor_idx;
+
+        mutex_lock(&data->lock);
+
+        for (sensor_idx = 0; sensor_idx < data->nr_remote; sensor_idx++) {
+                if (!readb(data->base + ARTPEC8_TMU_REG_STATUS))
+                        break;
+
+                ret = artpec8_sensor_initialize(data, sensor_idx);
+                if (ret)
+                        break;
+        }
+
+        mutex_unlock(&data->lock);
+}
+
+static void artpec8_enable_interrupt(struct exynos_tmu_data *data,
+                int sensor_idx)
+{
+        int i;
+        unsigned int interrupt_en = 0;
+        struct thermal_zone_device *tz = data->sensor[sensor_idx].tzd;
+
+        for (i = 0; i < data->sensor->ntrip; i++) {
+                if (!of_thermal_is_trip_valid(tz, i))
+                        continue;
+                interrupt_en |= (1 << i);
+        }
+        writel(interrupt_en, data->base + data->sensor[sensor_idx].inten);
+}
+
+
 static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
 {
         struct exynos_tmu_data *data = platform_get_drvdata(pdev);
@@ -650,6 +940,62 @@ static void exynos7_tmu_control(struct platform_device *pdev, bool on)
         writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
 }
 
+static void artpec8_tmu_control(struct platform_device *pdev, bool on)
+{
+        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+        unsigned int con, con1, i;
+        unsigned int vref;
+        unsigned int slope;
+        unsigned int trim0, trim0_bgr, trim0_vref, trim0_vbe, avg_mode;
+
+        vref = (readl(data->base + ARTPEC8_TMU_REG_TRIMINFO) >>
+                ARTPEC8_T_BUF_VREF_SEL_SHIFT) &
+                ARTPEC8_TMU_REF_VOLTAGE_MASK;
+        slope = (readl(data->base + ARTPEC8_TMU_REG_TRIMINFO1) >>
+                ARTPEC8_T_BUF_SLOPE_SEL_SHIFT) &
+                ARTPEC8_TMU_BUF_SLOPE_SEL_MASK;
+        con = (vref << ARTPEC8_TMU_REF_VOLTAGE_SHIFT) |
+                (slope << ARTPEC8_TMU_BUF_SLOPE_SEL_SHIFT);
+
+        if (on) {
+                for (i = 0; i < data->nr_remote; i++)
+                        artpec8_enable_interrupt(data, i);
+                con |= (ARTPEC8_TMU_CONTROL_CORE_EN |
+                        ARTPEC8_TMU_CONTROL_AUTO_MODE);
+        } else {
+                con &= ~(ARTPEC8_TMU_CONTROL_CORE_EN);
+        }
+
+        trim0_bgr = readl(data->base + ARTPEC8_TMU_REG_TRIMINFO3) >>
+                ARTPEC8_TRIMINFO_TRIM0_SHIFT;
+        trim0_bgr &= ARTPEC8_TRIM0_MASK;
+        trim0_vref = readl(data->base + ARTPEC8_TMU_REG_TRIMINFO4) >>
+                ARTPEC8_TRIMINFO_TRIM0_SHIFT;
+        trim0_vref &= ARTPEC8_TRIM0_MASK;
+        trim0_vbe = readl(data->base + ARTPEC8_TMU_REG_TRIMINFO5) >>
+                ARTPEC8_TRIMINFO_TRIM0_SHIFT;
+        trim0_vbe &= ARTPEC8_TRIM0_MASK;
+        trim0 = trim0_bgr << ARTPEC8_TRIM0_BGR_I_SHIFT |
+                trim0_vref << ARTPEC8_TRIM0_VREF_SHIFT |
+                trim0_vbe << ARTPEC8_TRIM0_VBE_I_SHIFT;
+
+        con1 = (data->nr_remote << NUM_PROBE_OFFSET) | ARTPEC8_LPI_MODE_EN;
+
+        while (!readb(data->base + ARTPEC8_TMU_REG_STATUS))
+                pr_debug("TMU busy waiting\n");
+
+
+        avg_mode = readl(data->base + ARTPEC8_TMU_REG_AVG_CONTROL);
+        avg_mode &= ~ARTPEC8_TRIM2_MASK;
+        avg_mode |= (readl(data->base + ARTPEC8_TMU_REG_TRIMINFO2) >>
+                ARTPEC8_TRIMINFO_TRIM0_SHIFT) & ARTPEC8_TRIM2_MASK;
+
+        writel(avg_mode, data->base + ARTPEC8_TMU_REG_AVG_CONTROL);
+        writel(trim0, data->base + ARTPEC8_TMU_REG_TMU_TRIM0);
+        writel(con1, data->base + ARTPEC8_TMU_REG_CONTROL1);
+        writel(con, data->base + ARTPEC8_TMU_REG_CONTROL);
+}
+
 static int exynos_get_temp(void *p, int *temp)
 {
         struct exynos_tmu_data *data = p;
@@ -679,6 +1025,37 @@ static int exynos_get_temp(void *p, int *temp)
         return ret;
 }
 
+static int artpec8_get_temp(void *p, int *temp)
+{
+        struct artpec8_sensor *sensor = p;
+        struct exynos_tmu_data *data;
+        bool enabled, valid;
+        u16 value;
+        int ret = 0;
+
+        if (!sensor)
+                return -EINVAL;
+
+        data = sensor->tmudev;
+        if (!data)
+                return -EINVAL;
+
+        enabled = readl(data->base + ARTPEC8_TMU_REG_CONTROL) & 0x1;
+        valid = readl(data->base + ARTPEC8_TMU_REG_STATUS) & 0xf0;
+        if (!enabled || !valid)
+                return -EAGAIN;
+
+        mutex_lock(&data->lock);
+
+        value = (readl(data->base + sensor->temp_offset) >>
+                sensor->temp_reg_shift) & ARTPEC8_TMU_TEMP_MASK;
+        *temp = artpec8_code_to_temp(sensor, value) * MCELSIUS;
+
+        mutex_unlock(&data->lock);
+
+        return ret;
+}
+
 #ifdef CONFIG_THERMAL_EMULATION
 static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val,
                             int temp)
@@ -748,10 +1125,32 @@ static int exynos_tmu_set_emulation(void *drv_data, int temp)
 out:
         return ret;
 }
+
+static int artpec8_tmu_set_emulation(void *sensor_data, int temp)
+{
+        struct artpec8_sensor *sensor = sensor_data;
+        struct exynos_tmu_data *data = sensor->tmudev;
+        u32 temp_code, econ;
+
+        temp /= MCELSIUS;
+
+        mutex_lock(&data->lock);
+
+        temp_code = artpec8_temp_to_code(sensor, temp);
+        econ = ARTPEC8_EMUL_NEXT_TIME | temp_code << ARTPEC8_EMUL_DATA_SHIFT |
+                ARTPEC8_EMUL_EN;
+        writel(econ, data->base + ARTPEC8_TMU_REG_EMUL_CON);
+
+        mutex_unlock(&data->lock);
+
+        return 0;
+}
 #else
 #define exynos4412_tmu_set_emulation NULL
 static int exynos_tmu_set_emulation(void *drv_data, int temp)
         { return -EINVAL; }
+static int artpec8_tmu_set_emulation(void *sensor_data, int temp)
+        { return -EINVAL; }
 #endif /* CONFIG_THERMAL_EMULATION */
 
 static int exynos4210_tmu_read(struct exynos_tmu_data *data)
@@ -791,6 +1190,45 @@ static void exynos_tmu_work(struct work_struct *work)
         enable_irq(data->irq);
 }
 
+static void artpec8_tmu_work(struct work_struct *work)
+{
+        int i;
+        u32 inten, intpend, rise, fall;
+        struct artpec8_sensor *sensor;
+        struct exynos_tmu_data *data = container_of(work,
+                        struct exynos_tmu_data, irq_work);
+
+        for (i = 0; i < data->nr_remote; i++)
+                thermal_zone_device_update(data->sensor[i].tzd,
+                                THERMAL_EVENT_UNSPECIFIED);
+
+        mutex_lock(&data->lock);
+        for (i = 0; i < data->nr_remote; i++) {
+                sensor = &data->sensor[i];
+                intpend = readl(data->base + sensor->intpend);
+
+                if (intpend) {
+                        fall = intpend & INTPEND_FALL_MASK;
+                        rise = intpend & INTPEND_RISE_MASK;
+
+                        if (fall) {
+                                inten = readl(data->base + sensor->inten) & (~fall);
+                                inten |= fall >> ARTPEC8_INTEN_FALL0_SHIFT;
+                                writel(inten, data->base + sensor->inten);
+                        }
+
+                        if (rise) {
+                                inten = readl(data->base + sensor->inten) & (~rise);
+                                inten |= (rise << ARTPEC8_INTEN_FALL0_SHIFT) |
+                                        min_t(u32, rise << 1, BIT(sensor->ntrip - 1)) | (rise >> 1);
+                                writel(inten, data->base + sensor->inten);
+                        }
+                        writel(intpend, data->base + sensor->intpend);
+                }
+        }
+        mutex_unlock(&data->lock);
+}
+
 static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data)
 {
         unsigned int val_irq;
@@ -860,6 +1298,9 @@ static const struct of_device_id exynos_tmu_match[] = {
         }, {
                 .compatible = "samsung,exynos7-tmu",
                 .data = (const void *)SOC_ARCH_EXYNOS7,
+        }, {
+                .compatible = "axis,artpec8-tmu",
+                .data = (const void *)SOC_ARCH_ARTPEC8,
         },
         { },
 };
@@ -895,6 +1336,7 @@ static int exynos_map_dt_data(struct platform_device *pdev)
         }
 
         data->soc = (enum soc_type)of_device_get_match_data(&pdev->dev);
+        data->cal_type = TYPE_ONE_POINT_TRIMMING;
 
         switch (data->soc) {
         case SOC_ARCH_EXYNOS4210:
@@ -968,13 +1410,19 @@ static int exynos_map_dt_data(struct platform_device *pdev)
                 data->min_efuse_value = 15;
                 data->max_efuse_value = 100;
                 break;
+        case SOC_ARCH_ARTPEC8:
+                data->tmu_initialize = artpec8_tmu_initialize;
+                data->tmu_control = artpec8_tmu_control;
+                data->cal_type = readl(data->base + ARTPEC8_TMU_REG_TRIMINFO) >>
+                        ARTPEC8_CALIB_SEL_SHIFT;
+                of_property_read_u32(pdev->dev.of_node, "remote_sensors",
+                                &data->nr_remote);
+                break;
         default:
                 dev_err(&pdev->dev, "Platform not supported\n");
                 return -EINVAL;
         }
 
-        data->cal_type = TYPE_ONE_POINT_TRIMMING;
-
         /*
          * Check if the TMU shares some registers and then try to map the
          * memory of common registers.
@@ -1002,42 +1450,138 @@ static const struct thermal_zone_of_device_ops exynos_sensor_ops = {
         .set_emul_temp = exynos_tmu_set_emulation,
 };
 
+static const struct thermal_zone_of_device_ops artpec8_ops = {
+        .get_temp = artpec8_get_temp,
+        .set_emul_temp = artpec8_tmu_set_emulation,
+};
+
+static int artpec8_map_sensor_data(struct exynos_tmu_data *data,
+                int sensor_idx, struct artpec8_sensor *sensor)
+{
+        int id = sensor_idx;
+
+        sensor->id = id;
+        sensor->tmudev = data;
+        sensor->trim_offset = artpec8_sensors[id].trim_offset;
+        sensor->temp_offset = artpec8_sensors[id].temp_offset;
+        sensor->temp_reg_shift = artpec8_sensors[id].temp_reg_shift;
+        sensor->rise_offset = artpec8_sensors[id].rise_offset;
+        sensor->fall_offset = artpec8_sensors[id].fall_offset;
+        sensor->past_offset = artpec8_sensors[id].past_offset;
+        sensor->inten = artpec8_sensors[id].inten;
+        sensor->intpend = artpec8_sensors[id].intpend;
+        sensor->triminfo_25 = readl(data->base + sensor->trim_offset) &
+                ARTPEC8_TMU_TEMP_MASK;
+        sensor->triminfo_105 = (readl(data->base + sensor->trim_offset) >>
+                ARTPEC8_TRIMINFO_105_SHIFT) & ARTPEC8_TMU_TEMP_MASK;
+
+        return 0;
+}
+
+static int artpec8_register_tzd(struct platform_device *pdev)
+{
+        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+        struct artpec8_sensor *sensor;
+        struct device *dev = &pdev->dev;
+        int sensor_idx, ret = 0;
+        struct thermal_zone_device *tzd;
+        const struct thermal_trip *trips;
+
+        for (sensor_idx = 0; sensor_idx < data->nr_remote; sensor_idx++) {
+                sensor = &data->sensor[sensor_idx];
+
+                ret = artpec8_map_sensor_data(data, sensor_idx, sensor);
+                if (ret)
+                        break;
+
+                tzd = devm_thermal_zone_of_sensor_register(dev,
+                                sensor_idx, sensor, &artpec8_ops);
+                if (IS_ERR(tzd))
+                        continue;
+
+                sensor->tzd = tzd;
+                trips = of_thermal_get_trip_points(tzd);
+                if (!trips) {
+                        dev_warn(dev,
+                                "Cannot get trip points from device tree!\n");
+                        ret = -ENODEV;
+                        break;
+                }
+                sensor->ntrip = of_thermal_get_ntrips(tzd);
+        }
+
+        return ret;
+}
+
 static int exynos_tmu_probe(struct platform_device *pdev)
 {
         struct exynos_tmu_data *data;
         int ret;
+        int sensor_idx;
+        int nr_remote = 0;
+        struct device *dev;
+        const struct of_device_id *dev_id;
 
-        data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
-                                        GFP_KERNEL);
+        if (pdev->dev.of_node)
+                dev = &pdev->dev;
+        else
+                dev = pdev->dev.parent;
+
+        dev_id = of_match_node(exynos_tmu_match, dev->of_node);
+        if (dev_id) {
+                data = (struct exynos_tmu_data *)dev_id->data;
+        } else {
+                dev_warn(dev, "dev id error\n");
+                return -EINVAL;
+        }
+
+        ret = of_property_read_u32(dev->of_node, "remote_sensors", &nr_remote);
+        if (ret < 0)
+                data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
+                                    GFP_KERNEL);
+        else
+                data = devm_kzalloc(dev, sizeof(struct exynos_tmu_data) +
+                        (sizeof(struct artpec8_sensor) * nr_remote),
+                        GFP_KERNEL);
         if (!data)
                 return -ENOMEM;
 
         platform_set_drvdata(pdev, data);
         mutex_init(&data->lock);
 
+        if (data->soc != SOC_ARCH_ARTPEC8) {
         /*
          * Try enabling the regulator if found
          * TODO: Add regulator as an SOC feature, so that regulator enable
          * is a compulsory call.
          */
-        data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu");
-        if (!IS_ERR(data->regulator)) {
-                ret = regulator_enable(data->regulator);
-                if (ret) {
-                        dev_err(&pdev->dev, "failed to enable vtmu\n");
-                        return ret;
+                data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu");
+                if (!IS_ERR(data->regulator)) {
+                        ret = regulator_enable(data->regulator);
+                        if (ret) {
+                                dev_err(&pdev->dev, "failed to enable vtmu\n");
+                                return ret;
+                        }
+                } else {
+                        if (PTR_ERR(data->regulator) == -EPROBE_DEFER)
+                                return -EPROBE_DEFER;
+                        dev_info(&pdev->dev, "Regulator node (vtmu) not found\n");
                 }
-        } else {
-                if (PTR_ERR(data->regulator) == -EPROBE_DEFER)
-                        return -EPROBE_DEFER;
-                dev_info(&pdev->dev, "Regulator node (vtmu) not found\n");
         }
 
         ret = exynos_map_dt_data(pdev);
         if (ret)
                 goto err_sensor;
 
-        INIT_WORK(&data->irq_work, exynos_tmu_work);
+        if (data->soc == SOC_ARCH_ARTPEC8) {
+                ret = artpec8_register_tzd(pdev);
+                if (ret)
+                        return -EINVAL;
+
+                INIT_WORK(&data->irq_work, artpec8_tmu_work);
+        } else {
+                INIT_WORK(&data->irq_work, exynos_tmu_work);
+        }
 
         data->clk = devm_clk_get(&pdev->dev, "tmu_apbif");
         if (IS_ERR(data->clk)) {
@@ -1046,18 +1590,21 @@ static int exynos_tmu_probe(struct platform_device *pdev)
                 goto err_sensor;
         }
 
-        data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif");
-        if (IS_ERR(data->clk_sec)) {
-                if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) {
-                        dev_err(&pdev->dev, "Failed to get triminfo clock\n");
-                        ret = PTR_ERR(data->clk_sec);
-                        goto err_sensor;
-                }
-        } else {
-                ret = clk_prepare(data->clk_sec);
-                if (ret) {
-                        dev_err(&pdev->dev, "Failed to get clock\n");
-                        goto err_sensor;
+        if (data->soc != SOC_ARCH_ARTPEC8) {
+                data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif");
+                if (IS_ERR(data->clk_sec)) {
+                        if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) {
+                                dev_err(&pdev->dev,
+                                        "Failed to get triminfo clock\n");
+                                ret = PTR_ERR(data->clk_sec);
+                                goto err_sensor;
+                        }
+                } else {
+                        ret = clk_prepare(data->clk_sec);
+                        if (ret) {
+                                dev_err(&pdev->dev, "Failed to get clock\n");
+                                goto err_sensor;
+                        }
                 }
         }
 
@@ -1070,6 +1617,7 @@ static int exynos_tmu_probe(struct platform_device *pdev)
         switch (data->soc) {
         case SOC_ARCH_EXYNOS5433:
         case SOC_ARCH_EXYNOS7:
+        case SOC_ARCH_ARTPEC8:
                 data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk");
                 if (IS_ERR(data->sclk)) {
                         dev_err(&pdev->dev, "Failed to get sclk\n");
@@ -1087,24 +1635,26 @@ static int exynos_tmu_probe(struct platform_device *pdev)
                 break;
         }
 
+        if (data->soc != SOC_ARCH_ARTPEC8) {
         /*
          * data->tzd must be registered before calling exynos_tmu_initialize(),
          * requesting irq and calling exynos_tmu_control().
          */
-        data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data,
-                                                    &exynos_sensor_ops);
-        if (IS_ERR(data->tzd)) {
-                ret = PTR_ERR(data->tzd);
-                if (ret != -EPROBE_DEFER)
-                        dev_err(&pdev->dev, "Failed to register sensor: %d\n",
-                                ret);
-                goto err_sclk;
-        }
+                data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data,
+                                                            &exynos_sensor_ops);
+                if (IS_ERR(data->tzd)) {
+                        ret = PTR_ERR(data->tzd);
+                        if (ret != -EPROBE_DEFER)
+                                dev_err(&pdev->dev,
+                                        "Failed to register sensor: %d\n", ret);
+                        goto err_sclk;
+                }
 
-        ret = exynos_tmu_initialize(pdev);
-        if (ret) {
-                dev_err(&pdev->dev, "Failed to initialize TMU\n");
-                goto err_thermal;
+                ret = exynos_tmu_initialize(pdev);
+                if (ret) {
+                        dev_err(&pdev->dev, "Failed to initialize TMU\n");
+                        goto err_thermal;
+                }
         }
 
         ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq,
@@ -1118,7 +1668,13 @@ static int exynos_tmu_probe(struct platform_device *pdev)
         return 0;
 
 err_thermal:
-        thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd);
+        if (data->soc == SOC_ARCH_ARTPEC8) {
+                for (sensor_idx = 0; sensor_idx < data->nr_remote; sensor_idx++)
+                        thermal_zone_of_sensor_unregister(
+                                dev, data->sensor[sensor_idx].tzd);
+        } else {
+                thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd);
+        }
 err_sclk:
         clk_disable_unprepare(data->sclk);
 err_clk:
@@ -1137,17 +1693,27 @@ static int exynos_tmu_remove(struct platform_device *pdev)
 {
         struct exynos_tmu_data *data = platform_get_drvdata(pdev);
         struct thermal_zone_device *tzd = data->tzd;
+        struct device *dev = &pdev->dev;
+        int i;
 
-        thermal_zone_of_sensor_unregister(&pdev->dev, tzd);
-        exynos_tmu_control(pdev, false);
+        if (data->soc == SOC_ARCH_ARTPEC8) {
+                for (i = 0; i < data->nr_remote; i++)
+                        thermal_zone_of_sensor_unregister(dev, data->sensor[i].tzd);
+                exynos_tmu_control(pdev, false);
 
-        clk_disable_unprepare(data->sclk);
-        clk_unprepare(data->clk);
-        if (!IS_ERR(data->clk_sec))
-                clk_unprepare(data->clk_sec);
+                clk_unprepare(data->clk);
+        } else {
+                thermal_zone_of_sensor_unregister(&pdev->dev, tzd);
+                exynos_tmu_control(pdev, false);
 
-        if (!IS_ERR(data->regulator))
-                regulator_disable(data->regulator);
+                clk_disable_unprepare(data->sclk);
+                clk_unprepare(data->clk);
+                if (!IS_ERR(data->clk_sec))
+                        clk_unprepare(data->clk_sec);
+
+                if (!IS_ERR(data->regulator))
+                        regulator_disable(data->regulator);
+        }
 
         return 0;
 }
-- 
2.9.5
 
 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

       reply	other threads:[~2022-03-22  8:23 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20220322075638.110056-1-hypmean.kim@samsung.com>
     [not found] ` <CGME20220322075618epcas1p3c63582c62099312db6d40d71cfbee15b@epcms2p5>
2022-03-22  8:22   ` Sang Min Kim [this message]
2022-03-22  8:22     ` [PATCH 2/2] thermal: exynos: Add support for ARTPEC-8 Sang Min Kim
2022-03-22 17:48     ` Krzysztof Kozlowski
2022-03-22 17:48       ` Krzysztof Kozlowski
2022-03-22 17:49     ` Krzysztof Kozlowski
2022-03-22 17:49       ` Krzysztof Kozlowski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220322082242epcms2p5b5e9e24a947e1d6c4b77fb9c95baf992@epcms2p5 \
    --to=hypmean.kim@samsung.com \
    --cc=alim.akhtar@samsung.com \
    --cc=amitk@kernel.org \
    --cc=broonie@kernel.org \
    --cc=bzolnier@gmail.com \
    --cc=daniel.lezcano@linaro.org \
    --cc=krzysztof.kozlowski@canonical.com \
    --cc=lgirdwood@gmail.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=linux-samsung-soc@vger.kernel.org \
    --cc=rafael@kernel.org \
    --cc=rui.zhang@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.