]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/thermal/samsung/exynos_tmu.c
thermal: exynos: add ->tmu_initialize method
[mirror_ubuntu-artful-kernel.git] / drivers / thermal / samsung / exynos_tmu.c
index 49c09243fd3829628bf8a53af3407c958d66cb11..736ef7885878b68d7a036359894f72ce1f7e8c87 100644 (file)
@@ -52,6 +52,7 @@
  * @temp_error2: fused value of the second point trim.
  * @regulator: pointer to the TMU regulator structure.
  * @reg_conf: pointer to structure to register with core thermal.
+ * @tmu_initialize: SoC specific TMU initialization method
  */
 struct exynos_tmu_data {
        int id;
@@ -66,6 +67,7 @@ struct exynos_tmu_data {
        u8 temp_error1, temp_error2;
        struct regulator *regulator;
        struct thermal_sensor_conf *reg_conf;
+       int (*tmu_initialize)(struct platform_device *pdev);
 };
 
 /*
@@ -139,66 +141,10 @@ static void exynos_tmu_clear_irqs(struct exynos_tmu_data *data)
        writel(val_irq, data->base + reg->tmu_intclear);
 }
 
-static int exynos_tmu_initialize(struct platform_device *pdev)
+static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info)
 {
-       struct exynos_tmu_data *data = platform_get_drvdata(pdev);
        struct exynos_tmu_platform_data *pdata = data->pdata;
-       const struct exynos_tmu_registers *reg = pdata->registers;
-       unsigned int status, trim_info = 0, con, ctrl;
-       unsigned int rising_threshold = 0, falling_threshold = 0;
-       int ret = 0, threshold_code, i;
-
-       mutex_lock(&data->lock);
-       clk_enable(data->clk);
-       if (!IS_ERR(data->clk_sec))
-               clk_enable(data->clk_sec);
-
-       if (TMU_SUPPORTS(pdata, READY_STATUS)) {
-               status = readb(data->base + reg->tmu_status);
-               if (!status) {
-                       ret = -EBUSY;
-                       goto out;
-               }
-       }
 
-       if (TMU_SUPPORTS(pdata, TRIM_RELOAD)) {
-               for (i = 0; i < reg->triminfo_ctrl_count; i++) {
-                       if (pdata->triminfo_reload[i]) {
-                               ctrl = readl(data->base +
-                                               reg->triminfo_ctrl[i]);
-                               ctrl |= pdata->triminfo_reload[i];
-                               writel(ctrl, data->base +
-                                               reg->triminfo_ctrl[i]);
-                       }
-               }
-       }
-
-       /* Save trimming info in order to perform calibration */
-       if (data->soc == SOC_ARCH_EXYNOS5440) {
-               /*
-                * For exynos5440 soc triminfo value is swapped between TMU0 and
-                * TMU2, so the below logic is needed.
-                */
-               switch (data->id) {
-               case 0:
-                       trim_info = readl(data->base +
-                       EXYNOS5440_EFUSE_SWAP_OFFSET + reg->triminfo_data);
-                       break;
-               case 1:
-                       trim_info = readl(data->base + reg->triminfo_data);
-                       break;
-               case 2:
-                       trim_info = readl(data->base -
-                       EXYNOS5440_EFUSE_SWAP_OFFSET + reg->triminfo_data);
-               }
-       } else {
-               /* On exynos5420 the triminfo register is in the shared space */
-               if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO)
-                       trim_info = readl(data->base_second +
-                                                       reg->triminfo_data);
-               else
-                       trim_info = readl(data->base + reg->triminfo_data);
-       }
        data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK;
        data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) &
                                EXYNOS_TMU_TEMP_MASK);
@@ -212,69 +158,37 @@ static int exynos_tmu_initialize(struct platform_device *pdev)
                data->temp_error2 =
                        (pdata->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) &
                        EXYNOS_TMU_TEMP_MASK;
+}
 
-       rising_threshold = readl(data->base + reg->threshold_th0);
+static u32 get_th_reg(struct exynos_tmu_data *data, u32 threshold, bool falling)
+{
+       struct exynos_tmu_platform_data *pdata = data->pdata;
+       int i;
 
-       if (data->soc == SOC_ARCH_EXYNOS4210) {
-               /* Write temperature code for threshold */
-               threshold_code = temp_to_code(data, pdata->threshold);
-               writeb(threshold_code,
-                       data->base + reg->threshold_temp);
-               for (i = 0; i < pdata->non_hw_trigger_levels; i++)
-                       writeb(pdata->trigger_levels[i], data->base +
-                       reg->threshold_th0 + i * sizeof(reg->threshold_th0));
+       for (i = 0; i < pdata->non_hw_trigger_levels; i++) {
+               u8 temp = pdata->trigger_levels[i];
 
-               exynos_tmu_clear_irqs(data);
-       } else {
-               /* Write temperature code for rising and falling threshold */
-               for (i = 0; i < pdata->non_hw_trigger_levels; i++) {
-                       threshold_code = temp_to_code(data,
-                                               pdata->trigger_levels[i]);
-                       rising_threshold &= ~(0xff << 8 * i);
-                       rising_threshold |= threshold_code << 8 * i;
-                       if (pdata->threshold_falling) {
-                               threshold_code = temp_to_code(data,
-                                               pdata->trigger_levels[i] -
-                                               pdata->threshold_falling);
-                               falling_threshold |= threshold_code << 8 * i;
-                       }
-               }
+               if (falling)
+                       temp -= pdata->threshold_falling;
+               else
+                       threshold &= ~(0xff << 8 * i);
 
-               writel(rising_threshold,
-                               data->base + reg->threshold_th0);
-               writel(falling_threshold,
-                               data->base + reg->threshold_th1);
-
-               exynos_tmu_clear_irqs(data);
-
-               /* if last threshold limit is also present */
-               i = pdata->max_trigger_level - 1;
-               if (pdata->trigger_levels[i] &&
-                               (pdata->trigger_type[i] == HW_TRIP)) {
-                       threshold_code = temp_to_code(data,
-                                               pdata->trigger_levels[i]);
-                       if (i == EXYNOS_MAX_TRIGGER_PER_REG - 1) {
-                               /* 1-4 level to be assigned in th0 reg */
-                               rising_threshold &= ~(0xff << 8 * i);
-                               rising_threshold |= threshold_code << 8 * i;
-                               writel(rising_threshold,
-                                       data->base + reg->threshold_th0);
-                       } else if (i == EXYNOS_MAX_TRIGGER_PER_REG) {
-                               /* 5th level to be assigned in th2 reg */
-                               rising_threshold =
-                               threshold_code << reg->threshold_th3_l0_shift;
-                               writel(rising_threshold,
-                                       data->base + reg->threshold_th2);
-                       }
-                       con = readl(data->base + reg->tmu_ctrl);
-                       con |= (1 << reg->therm_trip_en_shift);
-                       writel(con, data->base + reg->tmu_ctrl);
-               }
+               threshold |= temp_to_code(data, temp) << 8 * i;
        }
-       /*Clear the PMIN in the common TMU register*/
-       if (reg->tmu_pmin && !data->id)
-               writel(0, data->base_second + reg->tmu_pmin);
-out:
+
+       return threshold;
+}
+
+static int exynos_tmu_initialize(struct platform_device *pdev)
+{
+       struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+       int ret;
+
+       mutex_lock(&data->lock);
+       clk_enable(data->clk);
+       if (!IS_ERR(data->clk_sec))
+               clk_enable(data->clk_sec);
+       ret = data->tmu_initialize(pdev);
        clk_disable(data->clk);
        mutex_unlock(&data->lock);
        if (!IS_ERR(data->clk_sec))
@@ -296,7 +210,7 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
        con = readl(data->base + reg->tmu_ctrl);
 
        if (pdata->test_mux)
-               con |= (pdata->test_mux << reg->test_mux_addr_shift);
+               con |= (pdata->test_mux << EXYNOS4412_MUX_ADDR_SHIFT);
 
        con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT);
        con |= pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT;
@@ -305,9 +219,8 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
        con |= (pdata->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
 
        if (pdata->noise_cancel_mode) {
-               con &= ~(reg->therm_trip_mode_mask <<
-                                       reg->therm_trip_mode_shift);
-               con |= (pdata->noise_cancel_mode << reg->therm_trip_mode_shift);
+               con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT);
+               con |= (pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT);
        }
 
        if (on) {
@@ -331,6 +244,143 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
        mutex_unlock(&data->lock);
 }
 
+static int exynos4210_tmu_initialize(struct platform_device *pdev)
+{
+       struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+       struct exynos_tmu_platform_data *pdata = data->pdata;
+       unsigned int status;
+       int ret = 0, threshold_code, i;
+
+       status = readb(data->base + EXYNOS_TMU_REG_STATUS);
+       if (!status) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO));
+
+       /* Write temperature code for threshold */
+       threshold_code = temp_to_code(data, pdata->threshold);
+       writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
+
+       for (i = 0; i < pdata->non_hw_trigger_levels; i++)
+               writeb(pdata->trigger_levels[i], data->base +
+                      EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4);
+
+       exynos_tmu_clear_irqs(data);
+out:
+       return ret;
+}
+
+static int exynos4412_tmu_initialize(struct platform_device *pdev)
+{
+       struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+       struct exynos_tmu_platform_data *pdata = data->pdata;
+       unsigned int status, trim_info, con, ctrl, rising_threshold;
+       int ret = 0, threshold_code, i;
+
+       status = readb(data->base + EXYNOS_TMU_REG_STATUS);
+       if (!status) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       if (data->soc == SOC_ARCH_EXYNOS3250 ||
+           data->soc == SOC_ARCH_EXYNOS4412 ||
+           data->soc == SOC_ARCH_EXYNOS5250) {
+               if (data->soc == SOC_ARCH_EXYNOS3250) {
+                       ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON1);
+                       ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE;
+                       writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON1);
+               }
+               ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2);
+               ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE;
+               writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2);
+       }
+
+       /* On exynos5420 the triminfo register is in the shared space */
+       if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO)
+               trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO);
+       else
+               trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
+
+       sanitize_temp_error(data, trim_info);
+
+       /* Write temperature code for rising and falling threshold */
+       rising_threshold = readl(data->base + EXYNOS_THD_TEMP_RISE);
+       rising_threshold = get_th_reg(data, rising_threshold, false);
+       writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE);
+       writel(get_th_reg(data, 0, true), data->base + EXYNOS_THD_TEMP_FALL);
+
+       exynos_tmu_clear_irqs(data);
+
+       /* if last threshold limit is also present */
+       i = pdata->max_trigger_level - 1;
+       if (pdata->trigger_levels[i] && pdata->trigger_type[i] == HW_TRIP) {
+               threshold_code = temp_to_code(data, pdata->trigger_levels[i]);
+               /* 1-4 level to be assigned in th0 reg */
+               rising_threshold &= ~(0xff << 8 * i);
+               rising_threshold |= threshold_code << 8 * i;
+               writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE);
+               con = readl(data->base + EXYNOS_TMU_REG_CONTROL);
+               con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
+               writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
+       }
+out:
+       return ret;
+}
+
+static int exynos5440_tmu_initialize(struct platform_device *pdev)
+{
+       struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+       struct exynos_tmu_platform_data *pdata = data->pdata;
+       unsigned int trim_info = 0, con, rising_threshold;
+       int ret = 0, threshold_code, i;
+
+       /*
+        * For exynos5440 soc triminfo value is swapped between TMU0 and
+        * TMU2, so the below logic is needed.
+        */
+       switch (data->id) {
+       case 0:
+               trim_info = readl(data->base + EXYNOS5440_EFUSE_SWAP_OFFSET +
+                                EXYNOS5440_TMU_S0_7_TRIM);
+               break;
+       case 1:
+               trim_info = readl(data->base + EXYNOS5440_TMU_S0_7_TRIM);
+               break;
+       case 2:
+               trim_info = readl(data->base - EXYNOS5440_EFUSE_SWAP_OFFSET +
+                                 EXYNOS5440_TMU_S0_7_TRIM);
+       }
+       sanitize_temp_error(data, trim_info);
+
+       /* Write temperature code for rising and falling threshold */
+       rising_threshold = readl(data->base + EXYNOS5440_TMU_S0_7_TH0);
+       rising_threshold = get_th_reg(data, rising_threshold, false);
+       writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH0);
+       writel(0, data->base + EXYNOS5440_TMU_S0_7_TH1);
+
+       exynos_tmu_clear_irqs(data);
+
+       /* if last threshold limit is also present */
+       i = pdata->max_trigger_level - 1;
+       if (pdata->trigger_levels[i] && pdata->trigger_type[i] == HW_TRIP) {
+               threshold_code = temp_to_code(data, pdata->trigger_levels[i]);
+               /* 5th level to be assigned in th2 reg */
+               rising_threshold =
+                       threshold_code << EXYNOS5440_TMU_TH_RISE4_SHIFT;
+               writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH2);
+               con = readl(data->base + EXYNOS5440_TMU_S0_7_CTRL);
+               con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
+               writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL);
+       }
+       /* Clear the PMIN in the common TMU register */
+       if (!data->id)
+               writel(0, data->base_second + EXYNOS5440_TMU_PMIN);
+       return ret;
+}
+
 static int exynos_tmu_read(struct exynos_tmu_data *data)
 {
        struct exynos_tmu_platform_data *pdata = data->pdata;
@@ -382,11 +432,11 @@ static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
                temp /= MCELSIUS;
 
                if (TMU_SUPPORTS(pdata, EMUL_TIME)) {
-                       val &= ~(EXYNOS_EMUL_TIME_MASK << reg->emul_time_shift);
-                       val |= (EXYNOS_EMUL_TIME << reg->emul_time_shift);
+                       val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT);
+                       val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT);
                }
-               val &= ~(EXYNOS_EMUL_DATA_MASK << reg->emul_temp_shift);
-               val |= (temp_to_code(data, temp) << reg->emul_temp_shift) |
+               val &= ~(EXYNOS_EMUL_DATA_MASK << EXYNOS_EMUL_DATA_SHIFT);
+               val |= (temp_to_code(data, temp) << EXYNOS_EMUL_DATA_SHIFT) |
                        EXYNOS_EMUL_ENABLE;
        } else {
                val &= ~EXYNOS_EMUL_ENABLE;
@@ -409,15 +459,13 @@ static void exynos_tmu_work(struct work_struct *work)
 {
        struct exynos_tmu_data *data = container_of(work,
                        struct exynos_tmu_data, irq_work);
-       struct exynos_tmu_platform_data *pdata = data->pdata;
-       const struct exynos_tmu_registers *reg = pdata->registers;
        unsigned int val_type;
 
        if (!IS_ERR(data->clk_sec))
                clk_enable(data->clk_sec);
        /* Find which sensor generated this interrupt */
-       if (reg->tmu_irqstatus) {
-               val_type = readl(data->base_second + reg->tmu_irqstatus);
+       if (data->soc == SOC_ARCH_EXYNOS5440) {
+               val_type = readl(data->base_second + EXYNOS5440_TMU_IRQ_STATUS);
                if (!((val_type >> data->id) & 0x1))
                        goto out;
        }
@@ -625,15 +673,24 @@ static int exynos_tmu_probe(struct platform_device *pdev)
                goto err_clk_sec;
        }
 
-       if (pdata->type == SOC_ARCH_EXYNOS3250 ||
-           pdata->type == SOC_ARCH_EXYNOS4210 ||
-           pdata->type == SOC_ARCH_EXYNOS4412 ||
-           pdata->type == SOC_ARCH_EXYNOS5250 ||
-           pdata->type == SOC_ARCH_EXYNOS5260 ||
-           pdata->type == SOC_ARCH_EXYNOS5420_TRIMINFO ||
-           pdata->type == SOC_ARCH_EXYNOS5440)
-               data->soc = pdata->type;
-       else {
+       data->soc = pdata->type;
+
+       switch (data->soc) {
+       case SOC_ARCH_EXYNOS4210:
+               data->tmu_initialize = exynos4210_tmu_initialize;
+               break;
+       case SOC_ARCH_EXYNOS3250:
+       case SOC_ARCH_EXYNOS4412:
+       case SOC_ARCH_EXYNOS5250:
+       case SOC_ARCH_EXYNOS5260:
+       case SOC_ARCH_EXYNOS5420:
+       case SOC_ARCH_EXYNOS5420_TRIMINFO:
+               data->tmu_initialize = exynos4412_tmu_initialize;
+               break;
+       case SOC_ARCH_EXYNOS5440:
+               data->tmu_initialize = exynos5440_tmu_initialize;
+               break;
+       default:
                ret = -EINVAL;
                dev_err(&pdev->dev, "Platform not supported\n");
                goto err_clk;