From ac38c4ee9056aff0aecfd4429f62674853524ca4 Mon Sep 17 00:00:00 2001 From: Soomin Kim Date: Mon, 11 Jul 2016 11:20:33 +0900 Subject: [PATCH] [8895] tmu: samsung: Exynos8895 initial bring up Change-Id: I21be6076556a75bfb47864ee5a3a2dabdbcc6d14 Signed-off-by: Soomin Kim --- drivers/thermal/samsung/exynos_tmu.c | 160 +++++++++++++++++++++++++++ drivers/thermal/samsung/exynos_tmu.h | 1 + 2 files changed, 161 insertions(+) diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 41e12862ac45..de2b782fc9d4 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -421,6 +421,140 @@ static void exynos8890_tmu_control(struct platform_device *pdev, bool on) writel(con, data->base + EXYNOS_TMU_REG_CONTROL); } +static int exynos8895_tmu_initialize(struct platform_device *pdev) +{ + struct exynos_tmu_data *data = platform_get_drvdata(pdev); + struct thermal_zone_device *tz = data->tzd; + struct exynos_tmu_platform_data *pdata = data->pdata; + unsigned int rising_threshold = 0, falling_threshold = 0; + int temp, temp_hist; + unsigned int trim_info; + unsigned int reg_off, bit_off; + int threshold_code, i; + + /* Check tmu core ready status */ + trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); + + /* Check thermal calibration type */ + pdata->cal_type = (trim_info >> EXYNOS_TMU_CALIB_SEL_SHIFT) + & EXYNOS_TMU_CALIB_SEL_MASK; + + /* Check temp_error1 and error2 value */ + data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK; + data->temp_error2 = (trim_info >> EXYNOS_TMU_TRIMINFO_85_P0_SHIFT) + & EXYNOS_TMU_TEMP_MASK; + + if (!data->temp_error1) + data->temp_error1 = pdata->efuse_value & EXYNOS_TMU_TEMP_MASK; + if (!data->temp_error2) + data->temp_error2 = (pdata->efuse_value >> + EXYNOS_TMU_TRIMINFO_85_P0_SHIFT) + & EXYNOS_TMU_TEMP_MASK; + /* Write temperature code for rising and falling threshold */ + for (i = (of_thermal_get_ntrips(tz) - 1); i >= 0; i--) { + /* + * On exynos7 there are 4 rising and 4 falling threshold + * registers (0x50-0x5c and 0x60-0x6c respectively). Each + * register holds the value of two threshold levels (at bit + * offsets 0 and 16). Based on the fact that there are atmost + * eight possible trigger levels, calculate the register and + * bit offsets where the threshold levels are to be written. + * + * e.g. EXYNOS_THD_TEMP_RISE7_6 (0x50) + * [24:16] - Threshold level 7 + * [8:0] - Threshold level 6 + * e.g. EXYNOS_THD_TEMP_RISE5_4 (0x54) + * [24:16] - Threshold level 5 + * [8:0] - Threshold level 4 + * + * and similarly for falling thresholds. + * + * Based on the above, calculate the register and bit offsets + * for rising/falling threshold levels and populate them. + */ + reg_off = ((7 - i) / 2) * 4; + bit_off = ((8 - i) % 2); + + tz->ops->get_trip_temp(tz, i, &temp); + temp /= MCELSIUS; + + tz->ops->get_trip_hyst(tz, i, &temp_hist); + temp_hist = temp - (temp_hist / MCELSIUS); + + /* Set 9-bit temperature code for rising threshold levels */ + threshold_code = temp_to_code(data, temp); + rising_threshold = readl(data->base + + EXYNOS_THD_TEMP_RISE7_6 + reg_off); + rising_threshold &= ~(EXYNOS_TMU_TEMP_MASK << (16 * bit_off)); + rising_threshold |= threshold_code << (16 * bit_off); + writel(rising_threshold, + data->base + EXYNOS_THD_TEMP_RISE7_6 + reg_off); + + /* Set 9-bit temperature code for falling threshold levels */ + threshold_code = temp_to_code(data, temp_hist); + falling_threshold &= ~(EXYNOS_TMU_TEMP_MASK << (16 * bit_off)); + falling_threshold |= threshold_code << (16 * bit_off); + writel(falling_threshold, + data->base + EXYNOS_THD_TEMP_FALL7_6 + reg_off); + } + + data->tmu_clear_irqs(data); + + return 0; +} + +static void exynos8895_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, trim_info, trim_info1; + unsigned int t_buf_vref_sel, t_buf_slope_sel; + + trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); + trim_info1 = readl(data->base + EXYNOS_TMU_REG_TRIMINFO1); + + /* Save fuse buf_vref_sel, calib_sel value to TRIMINFO and 1 register */ + t_buf_vref_sel = (trim_info >> EXYNOS_TMU_T_BUF_VREF_SEL_SHIFT) + & (EXYNOS_TMU_T_BUF_VREF_SEL_MASK); + t_buf_slope_sel = (trim_info1 >> EXYNOS_TMU_T_BUF_SLOPE_SEL_SHIFT) + & (EXYNOS_TMU_T_BUF_SLOPE_SEL_MASK); + + con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); + + if (on) { + con |= (t_buf_vref_sel << EXYNOS_TMU_REF_VOLTAGE_SHIFT); + con |= (t_buf_slope_sel << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); + con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); + con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); + interrupt_en = + (of_thermal_is_trip_valid(tz, 7) + << EXYNOS_TMU_INTEN_RISE7_SHIFT) | + (of_thermal_is_trip_valid(tz, 6) + << EXYNOS_TMU_INTEN_RISE6_SHIFT) | + (of_thermal_is_trip_valid(tz, 5) + << EXYNOS_TMU_INTEN_RISE5_SHIFT) | + (of_thermal_is_trip_valid(tz, 4) + << EXYNOS_TMU_INTEN_RISE4_SHIFT) | + (of_thermal_is_trip_valid(tz, 3) + << EXYNOS_TMU_INTEN_RISE3_SHIFT) | + (of_thermal_is_trip_valid(tz, 2) + << EXYNOS_TMU_INTEN_RISE2_SHIFT) | + (of_thermal_is_trip_valid(tz, 1) + << EXYNOS_TMU_INTEN_RISE1_SHIFT) | + (of_thermal_is_trip_valid(tz, 0) + << EXYNOS_TMU_INTEN_RISE0_SHIFT); + + interrupt_en |= + interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; + } else { + con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); + con &= ~(1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); + interrupt_en = 0; /* Disable all interrupts */ + } + + writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); + writel(con, data->base + EXYNOS_TMU_REG_CONTROL); +} static int exynos_get_temp(void *p, int *temp) { struct exynos_tmu_data *data = p; @@ -526,6 +660,12 @@ static int exynos8890_tmu_read(struct exynos_tmu_data *data) EXYNOS_TMU_TEMP_MASK; } +static int exynos8895_tmu_read(struct exynos_tmu_data *data) +{ + return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & + EXYNOS_TMU_TEMP_MASK; +} + static void exynos_tmu_work(struct work_struct *work) { struct exynos_tmu_data *data = container_of(work, @@ -550,6 +690,15 @@ static void exynos8890_tmu_clear_irqs(struct exynos_tmu_data *data) writel(val_irq, data->base + EXYNOS_TMU_REG_INTPEND); } +static void exynos8895_tmu_clear_irqs(struct exynos_tmu_data *data) +{ + unsigned int val_irq; + + + val_irq = readl(data->base + EXYNOS_TMU_REG_INTPEND); + writel(val_irq, data->base + EXYNOS_TMU_REG_INTPEND); +} + static irqreturn_t exynos_tmu_irq(int irq, void *id) { struct exynos_tmu_data *data = id; @@ -594,6 +743,7 @@ static struct notifier_block exynos_tmu_pm_notifier = { static const struct of_device_id exynos_tmu_match[] = { { .compatible = "samsung,exynos8890-tmu", }, + { .compatible = "samsung,exynos8895-tmu", }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, exynos_tmu_match); @@ -602,6 +752,8 @@ static int exynos_of_get_soc_type(struct device_node *np) { if (of_device_is_compatible(np, "samsung,exynos8890-tmu")) return SOC_ARCH_EXYNOS8890; + if (of_device_is_compatible(np, "samsung,exynos8895-tmu")) + return SOC_ARCH_EXYNOS8895; return -EINVAL; } @@ -700,6 +852,14 @@ static int exynos_map_dt_data(struct platform_device *pdev) data->tmu_set_emulation = exynos8890_tmu_set_emulation; data->tmu_clear_irqs = exynos8890_tmu_clear_irqs; break; + case SOC_ARCH_EXYNOS8895: + data->tmu_initialize = exynos8895_tmu_initialize; + data->tmu_control = exynos8895_tmu_control; + data->tmu_read = exynos8895_tmu_read; + data->tmu_set_emulation = exynos8890_tmu_set_emulation; + data->tmu_clear_irqs = exynos8895_tmu_clear_irqs; + break; + default: dev_err(&pdev->dev, "Platform not supported\n"); return -EINVAL; diff --git a/drivers/thermal/samsung/exynos_tmu.h b/drivers/thermal/samsung/exynos_tmu.h index c412b4e133b4..31d6ec0a6be0 100644 --- a/drivers/thermal/samsung/exynos_tmu.h +++ b/drivers/thermal/samsung/exynos_tmu.h @@ -27,6 +27,7 @@ enum soc_type { SOC_ARCH_EXYNOS8890 = 1, + SOC_ARCH_EXYNOS8895 = 2, }; /** -- 2.20.1