LINUX-SAMSUNG / thermal: exynos: Add the support for Exynos5433 TMU
authorChanwoo Choi <cw00.choi@samsung.com>
Thu, 12 Feb 2015 06:05:44 +0000 (15:05 +0900)
committerSeung-Woo Kim <sw0312.kim@samsung.com>
Wed, 14 Dec 2016 04:40:21 +0000 (13:40 +0900)
This patch adds the support for Exynos5433's TMU (Thermal Management Unit).
Exynos5433 has a little different register bit fields as following description:
- Support the eight trip points for rising/falling interrupt by using two registers
- Read the calibration type (1-point or 2-point) and sensor id from TRIMINFO register
- Use a little different register address

Cc: Zhang Rui <rui.zhang@intel.com>
Cc: Eduardo Valentin <edubezval@gmail.com>
Cc: Lukasz Majewski <l.majewski@samsung.com>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
drivers/thermal/samsung/exynos_tmu.c
drivers/thermal/samsung/exynos_tmu.h

index 67098a8..13c3ace 100644 (file)
 #define EXYNOS4412_MUX_ADDR_VALUE          6
 #define EXYNOS4412_MUX_ADDR_SHIFT          20
 
+/* Exynos5433 specific registers */
+#define EXYNOS5433_TMU_REG_CONTROL1            0x024
+#define EXYNOS5433_TMU_SAMPLING_INTERVAL       0x02c
+#define EXYNOS5433_TMU_COUNTER_VALUE0          0x030
+#define EXYNOS5433_TMU_COUNTER_VALUE1          0x034
+#define EXYNOS5433_TMU_REG_CURRENT_TEMP1       0x044
+#define EXYNOS5433_THD_TEMP_RISE3_0            0x050
+#define EXYNOS5433_THD_TEMP_RISE7_4            0x054
+#define EXYNOS5433_THD_TEMP_FALL3_0            0x060
+#define EXYNOS5433_THD_TEMP_FALL7_4            0x064
+#define EXYNOS5433_TMU_REG_INTEN               0x0c0
+#define EXYNOS5433_TMU_REG_INTPEND             0x0c8
+#define EXYNOS5433_TMU_EMUL_CON                        0x110
+#define EXYNOS5433_TMU_PD_DET_EN               0x130
+
+#define EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT    16
+#define EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT    23
+#define EXYNOS5433_TRIMINFO_SENSOR_ID_MASK     \
+                       (0xf << EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT)
+#define EXYNOS5433_TRIMINFO_CALIB_SEL_MASK     BIT(23)
+
+#define EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING 0
+#define EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING 1
+
+#define EXYNOS5433_PD_DET_EN                   1
+
 /*exynos5440 specific registers*/
 #define EXYNOS5440_TMU_S0_7_TRIM               0x000
 #define EXYNOS5440_TMU_S0_7_CTRL               0x020
@@ -484,6 +510,101 @@ out:
        return ret;
 }
 
+static int exynos5433_tmu_initialize(struct platform_device *pdev)
+{
+       struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+       struct exynos_tmu_platform_data *pdata = data->pdata;
+       struct thermal_zone_device *tz = data->tzd;
+       unsigned int status, trim_info;
+       unsigned int rising_threshold = 0, falling_threshold = 0;
+       unsigned long temp, temp_hist;
+       int ret = 0, threshold_code, i, sensor_id, cal_type;
+
+       status = readb(data->base + EXYNOS_TMU_REG_STATUS);
+       if (!status) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
+       sanitize_temp_error(data, trim_info);
+
+       /* Read the temperature sensor id */
+       sensor_id = (trim_info & EXYNOS5433_TRIMINFO_SENSOR_ID_MASK)
+                               >> EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT;
+       dev_info(&pdev->dev, "Temperature sensor ID: 0x%x\n", sensor_id);
+
+       /* Read the calibration mode */
+       writel(trim_info, data->base + EXYNOS_TMU_REG_TRIMINFO);
+       cal_type = (trim_info & EXYNOS5433_TRIMINFO_CALIB_SEL_MASK)
+                               >> EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT;
+
+       switch (cal_type) {
+       case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING:
+               pdata->cal_type = TYPE_ONE_POINT_TRIMMING;
+               break;
+       case EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING:
+               pdata->cal_type = TYPE_TWO_POINT_TRIMMING;
+               break;
+       default:
+               pdata->cal_type = TYPE_ONE_POINT_TRIMMING;
+               break;
+       };
+
+       dev_info(&pdev->dev, "Calibration type is %d-point calibration\n",
+                       cal_type ?  2 : 1);
+
+       /* Write temperature code for rising and falling threshold */
+       for (i = 0; i < of_thermal_get_ntrips(tz); i++) {
+               int rising_reg_offset, falling_reg_offset;
+               int j = 0;
+
+               switch (i) {
+               case 0:
+               case 1:
+               case 2:
+               case 3:
+                       rising_reg_offset = EXYNOS5433_THD_TEMP_RISE3_0;
+                       falling_reg_offset = EXYNOS5433_THD_TEMP_FALL3_0;
+                       j = i;
+                       break;
+               case 4:
+               case 5:
+               case 6:
+               case 7:
+                       rising_reg_offset = EXYNOS5433_THD_TEMP_RISE7_4;
+                       falling_reg_offset = EXYNOS5433_THD_TEMP_FALL7_4;
+                       j = i - 4;
+                       break;
+               default:
+                       continue;
+               }
+
+               /* Write temperature code for rising threshold */
+               tz->ops->get_trip_temp(tz, i, &temp);
+               temp /= MCELSIUS;
+               threshold_code = temp_to_code(data, temp);
+
+               rising_threshold = readl(data->base + rising_reg_offset);
+               rising_threshold |= (threshold_code << j * 8);
+               writel(rising_threshold, data->base + rising_reg_offset);
+
+               /* Write temperature code for falling threshold */
+               tz->ops->get_trip_hyst(tz, i, &temp_hist);
+               temp_hist = temp - (temp_hist / MCELSIUS);
+               threshold_code = temp_to_code(data, temp_hist);
+
+               falling_threshold = readl(data->base + falling_reg_offset);
+               falling_threshold &= ~(0xff << j * 8);
+               falling_threshold |= (threshold_code << j * 8);
+               writel(falling_threshold, data->base + falling_reg_offset);
+       }
+
+       data->tmu_clear_irqs(data);
+out:
+       return ret;
+}
+
 static int exynos5440_tmu_initialize(struct platform_device *pdev)
 {
        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
@@ -643,6 +764,48 @@ static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
        writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
 }
 
+static void exynos5433_tmu_control(struct platform_device *pdev, bool on)
+{
+       struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+       struct thermal_zone_device *tz = data->tzd;
+       unsigned int con, interrupt_en, pd_det_en;
+
+       con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
+
+       if (on) {
+               con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
+               interrupt_en =
+                       (of_thermal_is_trip_valid(tz, 7)
+                       << EXYNOS7_TMU_INTEN_RISE7_SHIFT) |
+                       (of_thermal_is_trip_valid(tz, 6)
+                       << EXYNOS7_TMU_INTEN_RISE6_SHIFT) |
+                       (of_thermal_is_trip_valid(tz, 5)
+                       << EXYNOS7_TMU_INTEN_RISE5_SHIFT) |
+                       (of_thermal_is_trip_valid(tz, 4)
+                       << EXYNOS7_TMU_INTEN_RISE4_SHIFT) |
+                       (of_thermal_is_trip_valid(tz, 3)
+                       << EXYNOS7_TMU_INTEN_RISE3_SHIFT) |
+                       (of_thermal_is_trip_valid(tz, 2)
+                       << EXYNOS7_TMU_INTEN_RISE2_SHIFT) |
+                       (of_thermal_is_trip_valid(tz, 1)
+                       << EXYNOS7_TMU_INTEN_RISE1_SHIFT) |
+                       (of_thermal_is_trip_valid(tz, 0)
+                       << EXYNOS7_TMU_INTEN_RISE0_SHIFT);
+
+               interrupt_en |=
+                       interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
+       } else {
+               con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
+               interrupt_en = 0; /* Disable all interrupts */
+       }
+
+       pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0;
+
+       writel(pd_det_en, data->base + EXYNOS5433_TMU_PD_DET_EN);
+       writel(interrupt_en, data->base + EXYNOS5433_TMU_REG_INTEN);
+       writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
+}
+
 static void exynos5440_tmu_control(struct platform_device *pdev, bool on)
 {
        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
@@ -770,6 +933,8 @@ static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data,
 
        if (data->soc == SOC_ARCH_EXYNOS5260)
                emul_con = EXYNOS5260_EMUL_CON;
+       if (data->soc == SOC_ARCH_EXYNOS5433)
+               emul_con = EXYNOS5433_TMU_EMUL_CON;
        else if (data->soc == SOC_ARCH_EXYNOS7)
                emul_con = EXYNOS7_TMU_REG_EMUL_CON;
        else
@@ -882,6 +1047,9 @@ static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data)
        } else if (data->soc == SOC_ARCH_EXYNOS7) {
                tmu_intstat = EXYNOS7_TMU_REG_INTPEND;
                tmu_intclear = EXYNOS7_TMU_REG_INTPEND;
+       } else if (data->soc == SOC_ARCH_EXYNOS5433) {
+               tmu_intstat = EXYNOS5433_TMU_REG_INTPEND;
+               tmu_intclear = EXYNOS5433_TMU_REG_INTPEND;
        } else {
                tmu_intstat = EXYNOS_TMU_REG_INTSTAT;
                tmu_intclear = EXYNOS_TMU_REG_INTCLEAR;
@@ -926,6 +1094,7 @@ static const struct of_device_id exynos_tmu_match[] = {
        { .compatible = "samsung,exynos5260-tmu", },
        { .compatible = "samsung,exynos5420-tmu", },
        { .compatible = "samsung,exynos5420-tmu-ext-triminfo", },
+       { .compatible = "samsung,exynos5433-tmu", },
        { .compatible = "samsung,exynos5440-tmu", },
        { .compatible = "samsung,exynos7-tmu", },
        { /* sentinel */ },
@@ -949,6 +1118,8 @@ static int exynos_of_get_soc_type(struct device_node *np)
        else if (of_device_is_compatible(np,
                                         "samsung,exynos5420-tmu-ext-triminfo"))
                return SOC_ARCH_EXYNOS5420_TRIMINFO;
+       else if (of_device_is_compatible(np, "samsung,exynos5433-tmu"))
+               return SOC_ARCH_EXYNOS5433;
        else if (of_device_is_compatible(np, "samsung,exynos5440-tmu"))
                return SOC_ARCH_EXYNOS5440;
        else if (of_device_is_compatible(np, "samsung,exynos7-tmu"))
@@ -1069,6 +1240,13 @@ static int exynos_map_dt_data(struct platform_device *pdev)
                data->tmu_set_emulation = exynos4412_tmu_set_emulation;
                data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
                break;
+       case SOC_ARCH_EXYNOS5433:
+               data->tmu_initialize = exynos5433_tmu_initialize;
+               data->tmu_control = exynos5433_tmu_control;
+               data->tmu_read = exynos4412_tmu_read;
+               data->tmu_set_emulation = exynos4412_tmu_set_emulation;
+               data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
+               break;
        case SOC_ARCH_EXYNOS5440:
                data->tmu_initialize = exynos5440_tmu_initialize;
                data->tmu_control = exynos5440_tmu_control;
@@ -1172,7 +1350,9 @@ static int exynos_tmu_probe(struct platform_device *pdev)
                goto err_clk_sec;
        }
 
-       if (data->soc == SOC_ARCH_EXYNOS7) {
+       switch (data->soc) {
+       case SOC_ARCH_EXYNOS5433:
+       case SOC_ARCH_EXYNOS7:
                data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk");
                if (IS_ERR(data->sclk)) {
                        dev_err(&pdev->dev, "Failed to get sclk\n");
@@ -1184,7 +1364,10 @@ static int exynos_tmu_probe(struct platform_device *pdev)
                                goto err_clk;
                        }
                }
-       }
+               break;
+       default:
+               break;
+       };
 
        ret = exynos_tmu_initialize(pdev);
        if (ret) {
index 4d71ec6..440c714 100644 (file)
@@ -33,6 +33,7 @@ enum soc_type {
        SOC_ARCH_EXYNOS5260,
        SOC_ARCH_EXYNOS5420,
        SOC_ARCH_EXYNOS5420_TRIMINFO,
+       SOC_ARCH_EXYNOS5433,
        SOC_ARCH_EXYNOS5440,
        SOC_ARCH_EXYNOS7,
 };