|
@@ -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);
|
|
|
};
|
|
|
|
|
|
/*
|
|
@@ -180,118 +182,13 @@ static u32 get_th_reg(struct exynos_tmu_data *data, u32 threshold, bool falling)
|
|
|
static int exynos_tmu_initialize(struct platform_device *pdev)
|
|
|
{
|
|
|
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;
|
|
|
+ int ret;
|
|
|
|
|
|
mutex_lock(&data->lock);
|
|
|
clk_enable(data->clk);
|
|
|
if (!IS_ERR(data->clk_sec))
|
|
|
clk_enable(data->clk_sec);
|
|
|
-
|
|
|
- if (data->soc != SOC_ARCH_EXYNOS5440) {
|
|
|
- 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);
|
|
|
- }
|
|
|
-
|
|
|
- /* 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 + 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);
|
|
|
- }
|
|
|
- } else {
|
|
|
- /* 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);
|
|
|
-
|
|
|
- if (data->soc == SOC_ARCH_EXYNOS4210) {
|
|
|
- /* 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 +
|
|
|
- reg->threshold_th0 + i * sizeof(reg->threshold_th0));
|
|
|
-
|
|
|
- exynos_tmu_clear_irqs(data);
|
|
|
- } else {
|
|
|
- /* Write temperature code for rising and falling threshold */
|
|
|
- rising_threshold = readl(data->base + reg->threshold_th0);
|
|
|
- rising_threshold = get_th_reg(data, rising_threshold, false);
|
|
|
- if (data->soc != SOC_ARCH_EXYNOS5440)
|
|
|
- falling_threshold = get_th_reg(data, 0, true);
|
|
|
-
|
|
|
- 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 (data->soc != SOC_ARCH_EXYNOS5440) {
|
|
|
- /* 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);
|
|
|
- } else {
|
|
|
- /* 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 + reg->tmu_ctrl);
|
|
|
- con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
|
|
|
- writel(con, data->base + reg->tmu_ctrl);
|
|
|
- }
|
|
|
- }
|
|
|
- /*Clear the PMIN in the common TMU register*/
|
|
|
- if (data->soc == SOC_ARCH_EXYNOS5440 && !data->id)
|
|
|
- writel(0, data->base_second + EXYNOS5440_TMU_PMIN);
|
|
|
-out:
|
|
|
+ ret = data->tmu_initialize(pdev);
|
|
|
clk_disable(data->clk);
|
|
|
mutex_unlock(&data->lock);
|
|
|
if (!IS_ERR(data->clk_sec))
|
|
@@ -347,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;
|
|
@@ -639,16 +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 ||
|
|
|
- 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;
|