Message ID | 1422337703-11289-2-git-send-email-a.kesavan@samsung.com (mailing list archive) |
---|---|
State | Accepted |
Delegated to: | Eduardo Valentin |
Headers | show |
Hi Abhilash, > Add registers, bit fields and compatible strings for Exynos7 TMU > (Thermal Management Unit). Following are a few of the differences > in the Exynos7 TMU from earlier SoCs: > - 8 trigger levels > - Different bit offsets and more registers for the rising > and falling thresholds. > - New power down detection bit in the TMU_CONTROL register > which does not update the CURRENT_TEMP0 when tmu power down > is detected. > - Change in bit offset for the NEXT_DATA field of EMUL_CON > register. EMUL_CON register address has also changed. > - INTSTAT and INTCLEAR registers present in earlier SoCs > have been combined into one INTPEND register. The register > address for INTCLEAR and INTPEND is also different. > - Since there are 8 rising/falling interrupts as against > at most 4 in earlier SoCs the INTEN bit offsets are different. > - Multiple probe support which is handled by a TMU_CONTROL1 > register (No support for this in the current patch). > > This patch adds special clock support required only for Exynos7. It > also updates the "code_to_temp" prototype as Exynos7 has 9 bit > code-temp mapping. > > Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com> > --- > This patch set has been tested on linux next-20150123 with Eduardo's > thermal-next branch merged along with the arch-side exynos7 related > dts changes applied. > > Changes since v3: > Addressed comments from Lukasz Majewski: > - Added more comments in the code setting the thresholds. > - Split the documentation out into another patch. > Changes since v2: > - Rebased on top of Lukasz' latest exynos tmu series (v4). > - Added new exynos7 soc_type. > Changes since v1: > - Rebased on top of Lukasz' latest exynos tmu series (v2). > - Added sclk support to patch adding Exynos7 tmu support. > Previously, it was a separate patch. > - Used the SOC type to decide if sclk is present. > > drivers/thermal/samsung/exynos_tmu.c | 204 > ++++++++++++++++++++++++++++++++-- > drivers/thermal/samsung/exynos_tmu.h | 1 + 2 files changed, 197 > insertions(+), 8 deletions(-) > > diff --git a/drivers/thermal/samsung/exynos_tmu.c > b/drivers/thermal/samsung/exynos_tmu.c index 852e622..660ff69 100644 > --- a/drivers/thermal/samsung/exynos_tmu.c > +++ b/drivers/thermal/samsung/exynos_tmu.c > @@ -119,6 +119,26 @@ > #define EXYNOS5440_TMU_TH_RISE4_SHIFT 24 > #define EXYNOS5440_EFUSE_SWAP_OFFSET 8 > > +/* Exynos7 specific registers */ > +#define EXYNOS7_THD_TEMP_RISE7_6 0x50 > +#define EXYNOS7_THD_TEMP_FALL7_6 0x60 > +#define EXYNOS7_TMU_REG_INTEN 0x110 > +#define EXYNOS7_TMU_REG_INTPEND 0x118 > +#define EXYNOS7_TMU_REG_EMUL_CON 0x160 > + > +#define EXYNOS7_TMU_TEMP_MASK 0x1ff > +#define EXYNOS7_PD_DET_EN_SHIFT 23 > +#define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 > +#define EXYNOS7_TMU_INTEN_RISE1_SHIFT 1 > +#define EXYNOS7_TMU_INTEN_RISE2_SHIFT 2 > +#define EXYNOS7_TMU_INTEN_RISE3_SHIFT 3 > +#define EXYNOS7_TMU_INTEN_RISE4_SHIFT 4 > +#define EXYNOS7_TMU_INTEN_RISE5_SHIFT 5 > +#define EXYNOS7_TMU_INTEN_RISE6_SHIFT 6 > +#define EXYNOS7_TMU_INTEN_RISE7_SHIFT 7 > +#define EXYNOS7_EMUL_DATA_SHIFT 7 > +#define EXYNOS7_EMUL_DATA_MASK 0x1ff > + > #define MCELSIUS 1000 > /** > * struct exynos_tmu_data : A structure to hold the private data of > the TMU @@ -133,6 +153,7 @@ > * @lock: lock to implement synchronization. > * @clk: pointer to the clock structure. > * @clk_sec: pointer to the clock structure for accessing the > base_second. > + * @sclk: pointer to the clock structure for accessing the tmu > special clk. > * @temp_error1: fused value of the first point trim. > * @temp_error2: fused value of the second point trim. > * @regulator: pointer to the TMU regulator structure. > @@ -152,8 +173,8 @@ struct exynos_tmu_data { > enum soc_type soc; > struct work_struct irq_work; > struct mutex lock; > - struct clk *clk, *clk_sec; > - u8 temp_error1, temp_error2; > + struct clk *clk, *clk_sec, *sclk; > + u16 temp_error1, temp_error2; > struct regulator *regulator; > struct thermal_zone_device *tzd; > > @@ -223,7 +244,7 @@ static int temp_to_code(struct exynos_tmu_data > *data, u8 temp) > * Calculate a temperature value from a temperature code. > * The unit of the temperature is degree Celsius. > */ > -static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code) > +static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) > { > struct exynos_tmu_platform_data *pdata = data->pdata; > int temp; > @@ -513,6 +534,84 @@ static int exynos5440_tmu_initialize(struct > platform_device *pdev) return ret; > } > > +static int exynos7_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 status, trim_info; > + unsigned int rising_threshold = 0, falling_threshold = 0; > + int ret = 0, threshold_code, i; > + unsigned long temp, temp_hist; > + unsigned int reg_off, bit_off; > + > + status = readb(data->base + EXYNOS_TMU_REG_STATUS); > + if (!status) { > + ret = -EBUSY; > + goto out; > + } > + > + trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); > + > + data->temp_error1 = trim_info & EXYNOS7_TMU_TEMP_MASK; > + if (!data->temp_error1 || > + (pdata->min_efuse_value > data->temp_error1) || > + (data->temp_error1 > pdata->max_efuse_value)) > + data->temp_error1 = pdata->efuse_value & > 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. EXYNOS7_THD_TEMP_RISE7_6 (0x50) > + * [24:16] - Threshold level 7 > + * [8:0] - Threshold level 6 > + * e.g. EXYNOS7_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 + > + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); > + rising_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * > bit_off)); > + rising_threshold |= threshold_code << (16 * bit_off); > + writel(rising_threshold, > + data->base + EXYNOS7_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 &= ~(EXYNOS7_TMU_TEMP_MASK << (16 > * bit_off)); > + falling_threshold |= threshold_code << (16 * > bit_off); > + writel(falling_threshold, > + data->base + EXYNOS7_THD_TEMP_FALL7_6 + > reg_off); > + } > + > + data->tmu_clear_irqs(data); > +out: > + return ret; > +} > + > static void exynos4210_tmu_control(struct platform_device *pdev, > bool on) { > struct exynos_tmu_data *data = platform_get_drvdata(pdev); > @@ -573,6 +672,46 @@ static void exynos5440_tmu_control(struct > platform_device *pdev, bool on) writel(con, data->base + > EXYNOS5440_TMU_S0_7_CTRL); } > > +static void exynos7_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; > + > + 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 */ > + } > + con |= 1 << EXYNOS7_PD_DET_EN_SHIFT; > + > + writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); > + writel(con, data->base + EXYNOS_TMU_REG_CONTROL); > +} > + > static int exynos_get_temp(void *p, long *temp) > { > struct exynos_tmu_data *data = p; > @@ -602,9 +741,19 @@ static u32 get_emul_con_reg(struct > exynos_tmu_data *data, unsigned int val, val &= > ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); val |= > (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); } > - val &= ~(EXYNOS_EMUL_DATA_MASK << > EXYNOS_EMUL_DATA_SHIFT); > - val |= (temp_to_code(data, temp) << > EXYNOS_EMUL_DATA_SHIFT) | > - EXYNOS_EMUL_ENABLE; > + if (data->soc == SOC_ARCH_EXYNOS7) { > + val &= ~(EXYNOS7_EMUL_DATA_MASK << > + EXYNOS7_EMUL_DATA_SHIFT); > + val |= (temp_to_code(data, temp) << > + EXYNOS7_EMUL_DATA_SHIFT) | > + EXYNOS_EMUL_ENABLE; > + } else { > + 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; > } > @@ -620,6 +769,8 @@ static void exynos4412_tmu_set_emulation(struct > exynos_tmu_data *data, > if (data->soc == SOC_ARCH_EXYNOS5260) > emul_con = EXYNOS5260_EMUL_CON; > + else if (data->soc == SOC_ARCH_EXYNOS7) > + emul_con = EXYNOS7_TMU_REG_EMUL_CON; > else > emul_con = EXYNOS_EMUL_CON; > > @@ -683,6 +834,12 @@ static int exynos5440_tmu_read(struct > exynos_tmu_data *data) return readb(data->base + > EXYNOS5440_TMU_S0_7_TEMP); } > > +static int exynos7_tmu_read(struct exynos_tmu_data *data) > +{ > + return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & > + EXYNOS7_TMU_TEMP_MASK; > +} > + > static void exynos_tmu_work(struct work_struct *work) > { > struct exynos_tmu_data *data = container_of(work, > @@ -721,6 +878,8 @@ static void exynos4210_tmu_clear_irqs(struct > exynos_tmu_data *data) if (data->soc == SOC_ARCH_EXYNOS5260) { > tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; > tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; > + } else if (data->soc == SOC_ARCH_EXYNOS7) { > + tmu_intstat = tmu_intclear = EXYNOS7_TMU_REG_INTPEND; > } else { > tmu_intstat = EXYNOS_TMU_REG_INTSTAT; > tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; > @@ -782,6 +941,9 @@ static const struct of_device_id > exynos_tmu_match[] = { { > .compatible = "samsung,exynos5440-tmu", > }, > + { > + .compatible = "samsung,exynos7-tmu", > + }, > {}, > }; > MODULE_DEVICE_TABLE(of, exynos_tmu_match); > @@ -805,6 +967,8 @@ static int exynos_of_get_soc_type(struct > device_node *np) return SOC_ARCH_EXYNOS5420_TRIMINFO; > else if (of_device_is_compatible(np, > "samsung,exynos5440-tmu")) return SOC_ARCH_EXYNOS5440; > + else if (of_device_is_compatible(np, "samsung,exynos7-tmu")) > + return SOC_ARCH_EXYNOS7; > > return -EINVAL; > } > @@ -928,6 +1092,13 @@ static int exynos_map_dt_data(struct > platform_device *pdev) data->tmu_set_emulation = > exynos5440_tmu_set_emulation; data->tmu_clear_irqs = > exynos5440_tmu_clear_irqs; break; > + case SOC_ARCH_EXYNOS7: > + data->tmu_initialize = exynos7_tmu_initialize; > + data->tmu_control = exynos7_tmu_control; > + data->tmu_read = exynos7_tmu_read; > + data->tmu_set_emulation = > exynos4412_tmu_set_emulation; > + data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; > + break; > default: > dev_err(&pdev->dev, "Platform not supported\n"); > return -EINVAL; > @@ -1018,21 +1189,37 @@ static int exynos_tmu_probe(struct > platform_device *pdev) goto err_clk_sec; > } > > + if (data->soc == 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"); > + goto err_clk; > + } else { > + ret = clk_prepare_enable(data->sclk); > + if (ret) { > + dev_err(&pdev->dev, "Failed to > enable sclk\n"); > + goto err_clk; > + } > + } > + } > + > ret = exynos_tmu_initialize(pdev); > if (ret) { > dev_err(&pdev->dev, "Failed to initialize TMU\n"); > - goto err_clk; > + goto err_sclk; > } > > ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, > IRQF_TRIGGER_RISING | IRQF_SHARED, > dev_name(&pdev->dev), data); if (ret) { > dev_err(&pdev->dev, "Failed to request irq: %d\n", > data->irq); > - goto err_clk; > + goto err_sclk; > } > > exynos_tmu_control(pdev, true); > return 0; > +err_sclk: > + clk_disable_unprepare(data->sclk); > err_clk: > clk_unprepare(data->clk); > err_clk_sec: > @@ -1052,6 +1239,7 @@ static int exynos_tmu_remove(struct > platform_device *pdev) thermal_zone_of_sensor_unregister(&pdev->dev, > 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); > diff --git a/drivers/thermal/samsung/exynos_tmu.h > b/drivers/thermal/samsung/exynos_tmu.h index 9f9b1b8..4d71ec6 100644 > --- a/drivers/thermal/samsung/exynos_tmu.h > +++ b/drivers/thermal/samsung/exynos_tmu.h > @@ -34,6 +34,7 @@ enum soc_type { > SOC_ARCH_EXYNOS5420, > SOC_ARCH_EXYNOS5420_TRIMINFO, > SOC_ARCH_EXYNOS5440, > + SOC_ARCH_EXYNOS7, > }; > > /** Acked-by: Lukasz Majewski <l.majewski@samsung.com> Tested-by: Lukasz Majewski <l.majewski@samsung.com> Test HW: Odroid-U3 (Exynos4412) linux-soc-thermal/fixes SHA1: 252454f5cbda2c6b40c5d36f58cac2938437b85d
Hi Lukasz, On Thu, Jan 29, 2015 at 1:56 PM, Lukasz Majewski <l.majewski@samsung.com> wrote: > Hi Abhilash, > >> Add registers, bit fields and compatible strings for Exynos7 TMU >> (Thermal Management Unit). Following are a few of the differences >> in the Exynos7 TMU from earlier SoCs: >> - 8 trigger levels >> - Different bit offsets and more registers for the rising >> and falling thresholds. >> - New power down detection bit in the TMU_CONTROL register >> which does not update the CURRENT_TEMP0 when tmu power down >> is detected. >> - Change in bit offset for the NEXT_DATA field of EMUL_CON >> register. EMUL_CON register address has also changed. >> - INTSTAT and INTCLEAR registers present in earlier SoCs >> have been combined into one INTPEND register. The register >> address for INTCLEAR and INTPEND is also different. >> - Since there are 8 rising/falling interrupts as against >> at most 4 in earlier SoCs the INTEN bit offsets are different. >> - Multiple probe support which is handled by a TMU_CONTROL1 >> register (No support for this in the current patch). >> >> This patch adds special clock support required only for Exynos7. It >> also updates the "code_to_temp" prototype as Exynos7 has 9 bit >> code-temp mapping. >> >> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com> >> --- >> This patch set has been tested on linux next-20150123 with Eduardo's >> thermal-next branch merged along with the arch-side exynos7 related >> dts changes applied. >> >> Changes since v3: >> Addressed comments from Lukasz Majewski: >> - Added more comments in the code setting the thresholds. >> - Split the documentation out into another patch. >> Changes since v2: >> - Rebased on top of Lukasz' latest exynos tmu series (v4). >> - Added new exynos7 soc_type. >> Changes since v1: >> - Rebased on top of Lukasz' latest exynos tmu series (v2). >> - Added sclk support to patch adding Exynos7 tmu support. >> Previously, it was a separate patch. >> - Used the SOC type to decide if sclk is present. >> >> drivers/thermal/samsung/exynos_tmu.c | 204 >> ++++++++++++++++++++++++++++++++-- >> drivers/thermal/samsung/exynos_tmu.h | 1 + 2 files changed, 197 >> insertions(+), 8 deletions(-) >> >> diff --git a/drivers/thermal/samsung/exynos_tmu.c >> b/drivers/thermal/samsung/exynos_tmu.c index 852e622..660ff69 100644 >> --- a/drivers/thermal/samsung/exynos_tmu.c >> +++ b/drivers/thermal/samsung/exynos_tmu.c >> @@ -119,6 +119,26 @@ >> #define EXYNOS5440_TMU_TH_RISE4_SHIFT 24 >> #define EXYNOS5440_EFUSE_SWAP_OFFSET 8 >> >> +/* Exynos7 specific registers */ >> +#define EXYNOS7_THD_TEMP_RISE7_6 0x50 >> +#define EXYNOS7_THD_TEMP_FALL7_6 0x60 >> +#define EXYNOS7_TMU_REG_INTEN 0x110 >> +#define EXYNOS7_TMU_REG_INTPEND 0x118 >> +#define EXYNOS7_TMU_REG_EMUL_CON 0x160 >> + >> +#define EXYNOS7_TMU_TEMP_MASK 0x1ff >> +#define EXYNOS7_PD_DET_EN_SHIFT 23 >> +#define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 >> +#define EXYNOS7_TMU_INTEN_RISE1_SHIFT 1 >> +#define EXYNOS7_TMU_INTEN_RISE2_SHIFT 2 >> +#define EXYNOS7_TMU_INTEN_RISE3_SHIFT 3 >> +#define EXYNOS7_TMU_INTEN_RISE4_SHIFT 4 >> +#define EXYNOS7_TMU_INTEN_RISE5_SHIFT 5 >> +#define EXYNOS7_TMU_INTEN_RISE6_SHIFT 6 >> +#define EXYNOS7_TMU_INTEN_RISE7_SHIFT 7 >> +#define EXYNOS7_EMUL_DATA_SHIFT 7 >> +#define EXYNOS7_EMUL_DATA_MASK 0x1ff >> + >> #define MCELSIUS 1000 >> /** >> * struct exynos_tmu_data : A structure to hold the private data of >> the TMU @@ -133,6 +153,7 @@ >> * @lock: lock to implement synchronization. >> * @clk: pointer to the clock structure. >> * @clk_sec: pointer to the clock structure for accessing the >> base_second. >> + * @sclk: pointer to the clock structure for accessing the tmu >> special clk. >> * @temp_error1: fused value of the first point trim. >> * @temp_error2: fused value of the second point trim. >> * @regulator: pointer to the TMU regulator structure. >> @@ -152,8 +173,8 @@ struct exynos_tmu_data { >> enum soc_type soc; >> struct work_struct irq_work; >> struct mutex lock; >> - struct clk *clk, *clk_sec; >> - u8 temp_error1, temp_error2; >> + struct clk *clk, *clk_sec, *sclk; >> + u16 temp_error1, temp_error2; >> struct regulator *regulator; >> struct thermal_zone_device *tzd; >> >> @@ -223,7 +244,7 @@ static int temp_to_code(struct exynos_tmu_data >> *data, u8 temp) >> * Calculate a temperature value from a temperature code. >> * The unit of the temperature is degree Celsius. >> */ >> -static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code) >> +static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) >> { >> struct exynos_tmu_platform_data *pdata = data->pdata; >> int temp; >> @@ -513,6 +534,84 @@ static int exynos5440_tmu_initialize(struct >> platform_device *pdev) return ret; >> } >> >> +static int exynos7_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 status, trim_info; >> + unsigned int rising_threshold = 0, falling_threshold = 0; >> + int ret = 0, threshold_code, i; >> + unsigned long temp, temp_hist; >> + unsigned int reg_off, bit_off; >> + >> + status = readb(data->base + EXYNOS_TMU_REG_STATUS); >> + if (!status) { >> + ret = -EBUSY; >> + goto out; >> + } >> + >> + trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); >> + >> + data->temp_error1 = trim_info & EXYNOS7_TMU_TEMP_MASK; >> + if (!data->temp_error1 || >> + (pdata->min_efuse_value > data->temp_error1) || >> + (data->temp_error1 > pdata->max_efuse_value)) >> + data->temp_error1 = pdata->efuse_value & >> 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. EXYNOS7_THD_TEMP_RISE7_6 (0x50) >> + * [24:16] - Threshold level 7 >> + * [8:0] - Threshold level 6 >> + * e.g. EXYNOS7_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 + >> + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); >> + rising_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * >> bit_off)); >> + rising_threshold |= threshold_code << (16 * bit_off); >> + writel(rising_threshold, >> + data->base + EXYNOS7_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 &= ~(EXYNOS7_TMU_TEMP_MASK << (16 >> * bit_off)); >> + falling_threshold |= threshold_code << (16 * >> bit_off); >> + writel(falling_threshold, >> + data->base + EXYNOS7_THD_TEMP_FALL7_6 + >> reg_off); >> + } >> + >> + data->tmu_clear_irqs(data); >> +out: >> + return ret; >> +} >> + >> static void exynos4210_tmu_control(struct platform_device *pdev, >> bool on) { >> struct exynos_tmu_data *data = platform_get_drvdata(pdev); >> @@ -573,6 +672,46 @@ static void exynos5440_tmu_control(struct >> platform_device *pdev, bool on) writel(con, data->base + >> EXYNOS5440_TMU_S0_7_CTRL); } >> >> +static void exynos7_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; >> + >> + 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 */ >> + } >> + con |= 1 << EXYNOS7_PD_DET_EN_SHIFT; >> + >> + writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); >> + writel(con, data->base + EXYNOS_TMU_REG_CONTROL); >> +} >> + >> static int exynos_get_temp(void *p, long *temp) >> { >> struct exynos_tmu_data *data = p; >> @@ -602,9 +741,19 @@ static u32 get_emul_con_reg(struct >> exynos_tmu_data *data, unsigned int val, val &= >> ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); val |= >> (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); } >> - val &= ~(EXYNOS_EMUL_DATA_MASK << >> EXYNOS_EMUL_DATA_SHIFT); >> - val |= (temp_to_code(data, temp) << >> EXYNOS_EMUL_DATA_SHIFT) | >> - EXYNOS_EMUL_ENABLE; >> + if (data->soc == SOC_ARCH_EXYNOS7) { >> + val &= ~(EXYNOS7_EMUL_DATA_MASK << >> + EXYNOS7_EMUL_DATA_SHIFT); >> + val |= (temp_to_code(data, temp) << >> + EXYNOS7_EMUL_DATA_SHIFT) | >> + EXYNOS_EMUL_ENABLE; >> + } else { >> + 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; >> } >> @@ -620,6 +769,8 @@ static void exynos4412_tmu_set_emulation(struct >> exynos_tmu_data *data, >> if (data->soc == SOC_ARCH_EXYNOS5260) >> emul_con = EXYNOS5260_EMUL_CON; >> + else if (data->soc == SOC_ARCH_EXYNOS7) >> + emul_con = EXYNOS7_TMU_REG_EMUL_CON; >> else >> emul_con = EXYNOS_EMUL_CON; >> >> @@ -683,6 +834,12 @@ static int exynos5440_tmu_read(struct >> exynos_tmu_data *data) return readb(data->base + >> EXYNOS5440_TMU_S0_7_TEMP); } >> >> +static int exynos7_tmu_read(struct exynos_tmu_data *data) >> +{ >> + return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & >> + EXYNOS7_TMU_TEMP_MASK; >> +} >> + >> static void exynos_tmu_work(struct work_struct *work) >> { >> struct exynos_tmu_data *data = container_of(work, >> @@ -721,6 +878,8 @@ static void exynos4210_tmu_clear_irqs(struct >> exynos_tmu_data *data) if (data->soc == SOC_ARCH_EXYNOS5260) { >> tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; >> tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; >> + } else if (data->soc == SOC_ARCH_EXYNOS7) { >> + tmu_intstat = tmu_intclear = EXYNOS7_TMU_REG_INTPEND; >> } else { >> tmu_intstat = EXYNOS_TMU_REG_INTSTAT; >> tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; >> @@ -782,6 +941,9 @@ static const struct of_device_id >> exynos_tmu_match[] = { { >> .compatible = "samsung,exynos5440-tmu", >> }, >> + { >> + .compatible = "samsung,exynos7-tmu", >> + }, >> {}, >> }; >> MODULE_DEVICE_TABLE(of, exynos_tmu_match); >> @@ -805,6 +967,8 @@ static int exynos_of_get_soc_type(struct >> device_node *np) return SOC_ARCH_EXYNOS5420_TRIMINFO; >> else if (of_device_is_compatible(np, >> "samsung,exynos5440-tmu")) return SOC_ARCH_EXYNOS5440; >> + else if (of_device_is_compatible(np, "samsung,exynos7-tmu")) >> + return SOC_ARCH_EXYNOS7; >> >> return -EINVAL; >> } >> @@ -928,6 +1092,13 @@ static int exynos_map_dt_data(struct >> platform_device *pdev) data->tmu_set_emulation = >> exynos5440_tmu_set_emulation; data->tmu_clear_irqs = >> exynos5440_tmu_clear_irqs; break; >> + case SOC_ARCH_EXYNOS7: >> + data->tmu_initialize = exynos7_tmu_initialize; >> + data->tmu_control = exynos7_tmu_control; >> + data->tmu_read = exynos7_tmu_read; >> + data->tmu_set_emulation = >> exynos4412_tmu_set_emulation; >> + data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; >> + break; >> default: >> dev_err(&pdev->dev, "Platform not supported\n"); >> return -EINVAL; >> @@ -1018,21 +1189,37 @@ static int exynos_tmu_probe(struct >> platform_device *pdev) goto err_clk_sec; >> } >> >> + if (data->soc == 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"); >> + goto err_clk; >> + } else { >> + ret = clk_prepare_enable(data->sclk); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to >> enable sclk\n"); >> + goto err_clk; >> + } >> + } >> + } >> + >> ret = exynos_tmu_initialize(pdev); >> if (ret) { >> dev_err(&pdev->dev, "Failed to initialize TMU\n"); >> - goto err_clk; >> + goto err_sclk; >> } >> >> ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, >> IRQF_TRIGGER_RISING | IRQF_SHARED, >> dev_name(&pdev->dev), data); if (ret) { >> dev_err(&pdev->dev, "Failed to request irq: %d\n", >> data->irq); >> - goto err_clk; >> + goto err_sclk; >> } >> >> exynos_tmu_control(pdev, true); >> return 0; >> +err_sclk: >> + clk_disable_unprepare(data->sclk); >> err_clk: >> clk_unprepare(data->clk); >> err_clk_sec: >> @@ -1052,6 +1239,7 @@ static int exynos_tmu_remove(struct >> platform_device *pdev) thermal_zone_of_sensor_unregister(&pdev->dev, >> 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); >> diff --git a/drivers/thermal/samsung/exynos_tmu.h >> b/drivers/thermal/samsung/exynos_tmu.h index 9f9b1b8..4d71ec6 100644 >> --- a/drivers/thermal/samsung/exynos_tmu.h >> +++ b/drivers/thermal/samsung/exynos_tmu.h >> @@ -34,6 +34,7 @@ enum soc_type { >> SOC_ARCH_EXYNOS5420, >> SOC_ARCH_EXYNOS5420_TRIMINFO, >> SOC_ARCH_EXYNOS5440, >> + SOC_ARCH_EXYNOS7, >> }; >> >> /** > > > Acked-by: Lukasz Majewski <l.majewski@samsung.com> > Tested-by: Lukasz Majewski <l.majewski@samsung.com> > > Test HW: Odroid-U3 (Exynos4412) > > linux-soc-thermal/fixes > SHA1: 252454f5cbda2c6b40c5d36f58cac2938437b85d Thanks a lot for reviewing and testing these two patches. Regards, Abhilash > > -- > Best regards, > > Lukasz Majewski > > Samsung R&D Institute Poland (SRPOL) | Linux Platform Group -- To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Tue, Jan 27, 2015 at 11:18:22AM +0530, Abhilash Kesavan wrote: > Add registers, bit fields and compatible strings for Exynos7 TMU > (Thermal Management Unit). Following are a few of the differences > in the Exynos7 TMU from earlier SoCs: > - 8 trigger levels > - Different bit offsets and more registers for the rising > and falling thresholds. > - New power down detection bit in the TMU_CONTROL register > which does not update the CURRENT_TEMP0 when tmu power down > is detected. > - Change in bit offset for the NEXT_DATA field of EMUL_CON > register. EMUL_CON register address has also changed. > - INTSTAT and INTCLEAR registers present in earlier SoCs > have been combined into one INTPEND register. The register > address for INTCLEAR and INTPEND is also different. > - Since there are 8 rising/falling interrupts as against > at most 4 in earlier SoCs the INTEN bit offsets are different. > - Multiple probe support which is handled by a TMU_CONTROL1 > register (No support for this in the current patch). > > This patch adds special clock support required only for Exynos7. It > also updates the "code_to_temp" prototype as Exynos7 has 9 bit > code-temp mapping. > > Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com> Applied to my -fixes branch. However, I had to amend it myself to make checkpatch.pl --strict silent. In this version, it still outputs: CHECK: Alignment should match open parenthesis #209: FILE: drivers/thermal/samsung/exynos_tmu.c:558: + if (!data->temp_error1 || + (pdata->min_efuse_value > data->temp_error1) || CHECK: multiple assignments should be avoided #366: FILE: drivers/thermal/samsung/exynos_tmu.c:882: + tmu_intstat = tmu_intclear = EXYNOS7_TMU_REG_INTPEND; total: 0 errors, 0 warnings, 2 checks, 314 lines checked next, make sure you run checkpatch.pl --strict before sending patches. > --- > This patch set has been tested on linux next-20150123 with Eduardo's > thermal-next branch merged along with the arch-side exynos7 related > dts changes applied. > > Changes since v3: > Addressed comments from Lukasz Majewski: > - Added more comments in the code setting the thresholds. > - Split the documentation out into another patch. > Changes since v2: > - Rebased on top of Lukasz' latest exynos tmu series (v4). > - Added new exynos7 soc_type. > Changes since v1: > - Rebased on top of Lukasz' latest exynos tmu series (v2). > - Added sclk support to patch adding Exynos7 tmu support. > Previously, it was a separate patch. > - Used the SOC type to decide if sclk is present. > > drivers/thermal/samsung/exynos_tmu.c | 204 ++++++++++++++++++++++++++++++++-- > drivers/thermal/samsung/exynos_tmu.h | 1 + > 2 files changed, 197 insertions(+), 8 deletions(-) > > diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c > index 852e622..660ff69 100644 > --- a/drivers/thermal/samsung/exynos_tmu.c > +++ b/drivers/thermal/samsung/exynos_tmu.c > @@ -119,6 +119,26 @@ > #define EXYNOS5440_TMU_TH_RISE4_SHIFT 24 > #define EXYNOS5440_EFUSE_SWAP_OFFSET 8 > > +/* Exynos7 specific registers */ > +#define EXYNOS7_THD_TEMP_RISE7_6 0x50 > +#define EXYNOS7_THD_TEMP_FALL7_6 0x60 > +#define EXYNOS7_TMU_REG_INTEN 0x110 > +#define EXYNOS7_TMU_REG_INTPEND 0x118 > +#define EXYNOS7_TMU_REG_EMUL_CON 0x160 > + > +#define EXYNOS7_TMU_TEMP_MASK 0x1ff > +#define EXYNOS7_PD_DET_EN_SHIFT 23 > +#define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 > +#define EXYNOS7_TMU_INTEN_RISE1_SHIFT 1 > +#define EXYNOS7_TMU_INTEN_RISE2_SHIFT 2 > +#define EXYNOS7_TMU_INTEN_RISE3_SHIFT 3 > +#define EXYNOS7_TMU_INTEN_RISE4_SHIFT 4 > +#define EXYNOS7_TMU_INTEN_RISE5_SHIFT 5 > +#define EXYNOS7_TMU_INTEN_RISE6_SHIFT 6 > +#define EXYNOS7_TMU_INTEN_RISE7_SHIFT 7 > +#define EXYNOS7_EMUL_DATA_SHIFT 7 > +#define EXYNOS7_EMUL_DATA_MASK 0x1ff > + > #define MCELSIUS 1000 > /** > * struct exynos_tmu_data : A structure to hold the private data of the TMU > @@ -133,6 +153,7 @@ > * @lock: lock to implement synchronization. > * @clk: pointer to the clock structure. > * @clk_sec: pointer to the clock structure for accessing the base_second. > + * @sclk: pointer to the clock structure for accessing the tmu special clk. > * @temp_error1: fused value of the first point trim. > * @temp_error2: fused value of the second point trim. > * @regulator: pointer to the TMU regulator structure. > @@ -152,8 +173,8 @@ struct exynos_tmu_data { > enum soc_type soc; > struct work_struct irq_work; > struct mutex lock; > - struct clk *clk, *clk_sec; > - u8 temp_error1, temp_error2; > + struct clk *clk, *clk_sec, *sclk; > + u16 temp_error1, temp_error2; > struct regulator *regulator; > struct thermal_zone_device *tzd; > > @@ -223,7 +244,7 @@ static int temp_to_code(struct exynos_tmu_data *data, u8 temp) > * Calculate a temperature value from a temperature code. > * The unit of the temperature is degree Celsius. > */ > -static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code) > +static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) > { > struct exynos_tmu_platform_data *pdata = data->pdata; > int temp; > @@ -513,6 +534,84 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev) > return ret; > } > > +static int exynos7_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 status, trim_info; > + unsigned int rising_threshold = 0, falling_threshold = 0; > + int ret = 0, threshold_code, i; > + unsigned long temp, temp_hist; > + unsigned int reg_off, bit_off; > + > + status = readb(data->base + EXYNOS_TMU_REG_STATUS); > + if (!status) { > + ret = -EBUSY; > + goto out; > + } > + > + trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); > + > + data->temp_error1 = trim_info & EXYNOS7_TMU_TEMP_MASK; > + if (!data->temp_error1 || > + (pdata->min_efuse_value > data->temp_error1) || > + (data->temp_error1 > pdata->max_efuse_value)) > + data->temp_error1 = pdata->efuse_value & 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. EXYNOS7_THD_TEMP_RISE7_6 (0x50) > + * [24:16] - Threshold level 7 > + * [8:0] - Threshold level 6 > + * e.g. EXYNOS7_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 + > + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); > + rising_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); > + rising_threshold |= threshold_code << (16 * bit_off); > + writel(rising_threshold, > + data->base + EXYNOS7_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 &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); > + falling_threshold |= threshold_code << (16 * bit_off); > + writel(falling_threshold, > + data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); > + } > + > + data->tmu_clear_irqs(data); > +out: > + return ret; > +} > + > static void exynos4210_tmu_control(struct platform_device *pdev, bool on) > { > struct exynos_tmu_data *data = platform_get_drvdata(pdev); > @@ -573,6 +672,46 @@ static void exynos5440_tmu_control(struct platform_device *pdev, bool on) > writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL); > } > > +static void exynos7_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; > + > + 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 */ > + } > + con |= 1 << EXYNOS7_PD_DET_EN_SHIFT; > + > + writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); > + writel(con, data->base + EXYNOS_TMU_REG_CONTROL); > +} > + > static int exynos_get_temp(void *p, long *temp) > { > struct exynos_tmu_data *data = p; > @@ -602,9 +741,19 @@ static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, > val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); > val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); > } > - val &= ~(EXYNOS_EMUL_DATA_MASK << EXYNOS_EMUL_DATA_SHIFT); > - val |= (temp_to_code(data, temp) << EXYNOS_EMUL_DATA_SHIFT) | > - EXYNOS_EMUL_ENABLE; > + if (data->soc == SOC_ARCH_EXYNOS7) { > + val &= ~(EXYNOS7_EMUL_DATA_MASK << > + EXYNOS7_EMUL_DATA_SHIFT); > + val |= (temp_to_code(data, temp) << > + EXYNOS7_EMUL_DATA_SHIFT) | > + EXYNOS_EMUL_ENABLE; > + } else { > + 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; > } > @@ -620,6 +769,8 @@ static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, > > if (data->soc == SOC_ARCH_EXYNOS5260) > emul_con = EXYNOS5260_EMUL_CON; > + else if (data->soc == SOC_ARCH_EXYNOS7) > + emul_con = EXYNOS7_TMU_REG_EMUL_CON; > else > emul_con = EXYNOS_EMUL_CON; > > @@ -683,6 +834,12 @@ static int exynos5440_tmu_read(struct exynos_tmu_data *data) > return readb(data->base + EXYNOS5440_TMU_S0_7_TEMP); > } > > +static int exynos7_tmu_read(struct exynos_tmu_data *data) > +{ > + return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & > + EXYNOS7_TMU_TEMP_MASK; > +} > + > static void exynos_tmu_work(struct work_struct *work) > { > struct exynos_tmu_data *data = container_of(work, > @@ -721,6 +878,8 @@ static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) > if (data->soc == SOC_ARCH_EXYNOS5260) { > tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; > tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; > + } else if (data->soc == SOC_ARCH_EXYNOS7) { > + tmu_intstat = tmu_intclear = EXYNOS7_TMU_REG_INTPEND; > } else { > tmu_intstat = EXYNOS_TMU_REG_INTSTAT; > tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; > @@ -782,6 +941,9 @@ static const struct of_device_id exynos_tmu_match[] = { > { > .compatible = "samsung,exynos5440-tmu", > }, > + { > + .compatible = "samsung,exynos7-tmu", > + }, > {}, > }; > MODULE_DEVICE_TABLE(of, exynos_tmu_match); > @@ -805,6 +967,8 @@ static int exynos_of_get_soc_type(struct device_node *np) > return SOC_ARCH_EXYNOS5420_TRIMINFO; > else if (of_device_is_compatible(np, "samsung,exynos5440-tmu")) > return SOC_ARCH_EXYNOS5440; > + else if (of_device_is_compatible(np, "samsung,exynos7-tmu")) > + return SOC_ARCH_EXYNOS7; > > return -EINVAL; > } > @@ -928,6 +1092,13 @@ static int exynos_map_dt_data(struct platform_device *pdev) > data->tmu_set_emulation = exynos5440_tmu_set_emulation; > data->tmu_clear_irqs = exynos5440_tmu_clear_irqs; > break; > + case SOC_ARCH_EXYNOS7: > + data->tmu_initialize = exynos7_tmu_initialize; > + data->tmu_control = exynos7_tmu_control; > + data->tmu_read = exynos7_tmu_read; > + data->tmu_set_emulation = exynos4412_tmu_set_emulation; > + data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; > + break; > default: > dev_err(&pdev->dev, "Platform not supported\n"); > return -EINVAL; > @@ -1018,21 +1189,37 @@ static int exynos_tmu_probe(struct platform_device *pdev) > goto err_clk_sec; > } > > + if (data->soc == 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"); > + goto err_clk; > + } else { > + ret = clk_prepare_enable(data->sclk); > + if (ret) { > + dev_err(&pdev->dev, "Failed to enable sclk\n"); > + goto err_clk; > + } > + } > + } > + > ret = exynos_tmu_initialize(pdev); > if (ret) { > dev_err(&pdev->dev, "Failed to initialize TMU\n"); > - goto err_clk; > + goto err_sclk; > } > > ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, > IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); > if (ret) { > dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); > - goto err_clk; > + goto err_sclk; > } > > exynos_tmu_control(pdev, true); > return 0; > +err_sclk: > + clk_disable_unprepare(data->sclk); > err_clk: > clk_unprepare(data->clk); > err_clk_sec: > @@ -1052,6 +1239,7 @@ static int exynos_tmu_remove(struct platform_device *pdev) > thermal_zone_of_sensor_unregister(&pdev->dev, 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); > diff --git a/drivers/thermal/samsung/exynos_tmu.h b/drivers/thermal/samsung/exynos_tmu.h > index 9f9b1b8..4d71ec6 100644 > --- a/drivers/thermal/samsung/exynos_tmu.h > +++ b/drivers/thermal/samsung/exynos_tmu.h > @@ -34,6 +34,7 @@ enum soc_type { > SOC_ARCH_EXYNOS5420, > SOC_ARCH_EXYNOS5420_TRIMINFO, > SOC_ARCH_EXYNOS5440, > + SOC_ARCH_EXYNOS7, > }; > > /** > -- > 1.7.9.5 >
Hi Eduardo, On Sun, Feb 1, 2015 at 12:54 AM, Eduardo Valentin <edubezval@gmail.com> wrote: > On Tue, Jan 27, 2015 at 11:18:22AM +0530, Abhilash Kesavan wrote: >> Add registers, bit fields and compatible strings for Exynos7 TMU >> (Thermal Management Unit). Following are a few of the differences >> in the Exynos7 TMU from earlier SoCs: >> - 8 trigger levels >> - Different bit offsets and more registers for the rising >> and falling thresholds. >> - New power down detection bit in the TMU_CONTROL register >> which does not update the CURRENT_TEMP0 when tmu power down >> is detected. >> - Change in bit offset for the NEXT_DATA field of EMUL_CON >> register. EMUL_CON register address has also changed. >> - INTSTAT and INTCLEAR registers present in earlier SoCs >> have been combined into one INTPEND register. The register >> address for INTCLEAR and INTPEND is also different. >> - Since there are 8 rising/falling interrupts as against >> at most 4 in earlier SoCs the INTEN bit offsets are different. >> - Multiple probe support which is handled by a TMU_CONTROL1 >> register (No support for this in the current patch). >> >> This patch adds special clock support required only for Exynos7. It >> also updates the "code_to_temp" prototype as Exynos7 has 9 bit >> code-temp mapping. >> >> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com> > > Applied to my -fixes branch. However, I had to amend it myself to make > checkpatch.pl --strict silent. In this version, it still outputs: > CHECK: Alignment should match open parenthesis > #209: FILE: drivers/thermal/samsung/exynos_tmu.c:558: > + if (!data->temp_error1 || > + (pdata->min_efuse_value > data->temp_error1) || > > CHECK: multiple assignments should be avoided > #366: FILE: drivers/thermal/samsung/exynos_tmu.c:882: > + tmu_intstat = tmu_intclear = EXYNOS7_TMU_REG_INTPEND; > > total: 0 errors, 0 warnings, 2 checks, 314 lines checked > > next, make sure you run checkpatch.pl --strict before sending patches. Thanks for applying these patches. As this is adding support for a new SoC, should it not be part of your -next branch ? I generally just run checkpatch without the "strict" option. Will ensure that I run it with "strict" in the future. Regards, Abhilash > > >> --- >> This patch set has been tested on linux next-20150123 with Eduardo's >> thermal-next branch merged along with the arch-side exynos7 related >> dts changes applied. >> >> Changes since v3: >> Addressed comments from Lukasz Majewski: >> - Added more comments in the code setting the thresholds. >> - Split the documentation out into another patch. >> Changes since v2: >> - Rebased on top of Lukasz' latest exynos tmu series (v4). >> - Added new exynos7 soc_type. >> Changes since v1: >> - Rebased on top of Lukasz' latest exynos tmu series (v2). >> - Added sclk support to patch adding Exynos7 tmu support. >> Previously, it was a separate patch. >> - Used the SOC type to decide if sclk is present. >> >> drivers/thermal/samsung/exynos_tmu.c | 204 ++++++++++++++++++++++++++++++++-- >> drivers/thermal/samsung/exynos_tmu.h | 1 + >> 2 files changed, 197 insertions(+), 8 deletions(-) >> >> diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c >> index 852e622..660ff69 100644 >> --- a/drivers/thermal/samsung/exynos_tmu.c >> +++ b/drivers/thermal/samsung/exynos_tmu.c >> @@ -119,6 +119,26 @@ >> #define EXYNOS5440_TMU_TH_RISE4_SHIFT 24 >> #define EXYNOS5440_EFUSE_SWAP_OFFSET 8 >> >> +/* Exynos7 specific registers */ >> +#define EXYNOS7_THD_TEMP_RISE7_6 0x50 >> +#define EXYNOS7_THD_TEMP_FALL7_6 0x60 >> +#define EXYNOS7_TMU_REG_INTEN 0x110 >> +#define EXYNOS7_TMU_REG_INTPEND 0x118 >> +#define EXYNOS7_TMU_REG_EMUL_CON 0x160 >> + >> +#define EXYNOS7_TMU_TEMP_MASK 0x1ff >> +#define EXYNOS7_PD_DET_EN_SHIFT 23 >> +#define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 >> +#define EXYNOS7_TMU_INTEN_RISE1_SHIFT 1 >> +#define EXYNOS7_TMU_INTEN_RISE2_SHIFT 2 >> +#define EXYNOS7_TMU_INTEN_RISE3_SHIFT 3 >> +#define EXYNOS7_TMU_INTEN_RISE4_SHIFT 4 >> +#define EXYNOS7_TMU_INTEN_RISE5_SHIFT 5 >> +#define EXYNOS7_TMU_INTEN_RISE6_SHIFT 6 >> +#define EXYNOS7_TMU_INTEN_RISE7_SHIFT 7 >> +#define EXYNOS7_EMUL_DATA_SHIFT 7 >> +#define EXYNOS7_EMUL_DATA_MASK 0x1ff >> + >> #define MCELSIUS 1000 >> /** >> * struct exynos_tmu_data : A structure to hold the private data of the TMU >> @@ -133,6 +153,7 @@ >> * @lock: lock to implement synchronization. >> * @clk: pointer to the clock structure. >> * @clk_sec: pointer to the clock structure for accessing the base_second. >> + * @sclk: pointer to the clock structure for accessing the tmu special clk. >> * @temp_error1: fused value of the first point trim. >> * @temp_error2: fused value of the second point trim. >> * @regulator: pointer to the TMU regulator structure. >> @@ -152,8 +173,8 @@ struct exynos_tmu_data { >> enum soc_type soc; >> struct work_struct irq_work; >> struct mutex lock; >> - struct clk *clk, *clk_sec; >> - u8 temp_error1, temp_error2; >> + struct clk *clk, *clk_sec, *sclk; >> + u16 temp_error1, temp_error2; >> struct regulator *regulator; >> struct thermal_zone_device *tzd; >> >> @@ -223,7 +244,7 @@ static int temp_to_code(struct exynos_tmu_data *data, u8 temp) >> * Calculate a temperature value from a temperature code. >> * The unit of the temperature is degree Celsius. >> */ >> -static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code) >> +static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) >> { >> struct exynos_tmu_platform_data *pdata = data->pdata; >> int temp; >> @@ -513,6 +534,84 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev) >> return ret; >> } >> >> +static int exynos7_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 status, trim_info; >> + unsigned int rising_threshold = 0, falling_threshold = 0; >> + int ret = 0, threshold_code, i; >> + unsigned long temp, temp_hist; >> + unsigned int reg_off, bit_off; >> + >> + status = readb(data->base + EXYNOS_TMU_REG_STATUS); >> + if (!status) { >> + ret = -EBUSY; >> + goto out; >> + } >> + >> + trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); >> + >> + data->temp_error1 = trim_info & EXYNOS7_TMU_TEMP_MASK; >> + if (!data->temp_error1 || >> + (pdata->min_efuse_value > data->temp_error1) || >> + (data->temp_error1 > pdata->max_efuse_value)) >> + data->temp_error1 = pdata->efuse_value & 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. EXYNOS7_THD_TEMP_RISE7_6 (0x50) >> + * [24:16] - Threshold level 7 >> + * [8:0] - Threshold level 6 >> + * e.g. EXYNOS7_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 + >> + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); >> + rising_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); >> + rising_threshold |= threshold_code << (16 * bit_off); >> + writel(rising_threshold, >> + data->base + EXYNOS7_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 &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); >> + falling_threshold |= threshold_code << (16 * bit_off); >> + writel(falling_threshold, >> + data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); >> + } >> + >> + data->tmu_clear_irqs(data); >> +out: >> + return ret; >> +} >> + >> static void exynos4210_tmu_control(struct platform_device *pdev, bool on) >> { >> struct exynos_tmu_data *data = platform_get_drvdata(pdev); >> @@ -573,6 +672,46 @@ static void exynos5440_tmu_control(struct platform_device *pdev, bool on) >> writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL); >> } >> >> +static void exynos7_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; >> + >> + 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 */ >> + } >> + con |= 1 << EXYNOS7_PD_DET_EN_SHIFT; >> + >> + writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); >> + writel(con, data->base + EXYNOS_TMU_REG_CONTROL); >> +} >> + >> static int exynos_get_temp(void *p, long *temp) >> { >> struct exynos_tmu_data *data = p; >> @@ -602,9 +741,19 @@ static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, >> val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); >> val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); >> } >> - val &= ~(EXYNOS_EMUL_DATA_MASK << EXYNOS_EMUL_DATA_SHIFT); >> - val |= (temp_to_code(data, temp) << EXYNOS_EMUL_DATA_SHIFT) | >> - EXYNOS_EMUL_ENABLE; >> + if (data->soc == SOC_ARCH_EXYNOS7) { >> + val &= ~(EXYNOS7_EMUL_DATA_MASK << >> + EXYNOS7_EMUL_DATA_SHIFT); >> + val |= (temp_to_code(data, temp) << >> + EXYNOS7_EMUL_DATA_SHIFT) | >> + EXYNOS_EMUL_ENABLE; >> + } else { >> + 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; >> } >> @@ -620,6 +769,8 @@ static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, >> >> if (data->soc == SOC_ARCH_EXYNOS5260) >> emul_con = EXYNOS5260_EMUL_CON; >> + else if (data->soc == SOC_ARCH_EXYNOS7) >> + emul_con = EXYNOS7_TMU_REG_EMUL_CON; >> else >> emul_con = EXYNOS_EMUL_CON; >> >> @@ -683,6 +834,12 @@ static int exynos5440_tmu_read(struct exynos_tmu_data *data) >> return readb(data->base + EXYNOS5440_TMU_S0_7_TEMP); >> } >> >> +static int exynos7_tmu_read(struct exynos_tmu_data *data) >> +{ >> + return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & >> + EXYNOS7_TMU_TEMP_MASK; >> +} >> + >> static void exynos_tmu_work(struct work_struct *work) >> { >> struct exynos_tmu_data *data = container_of(work, >> @@ -721,6 +878,8 @@ static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) >> if (data->soc == SOC_ARCH_EXYNOS5260) { >> tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; >> tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; >> + } else if (data->soc == SOC_ARCH_EXYNOS7) { >> + tmu_intstat = tmu_intclear = EXYNOS7_TMU_REG_INTPEND; >> } else { >> tmu_intstat = EXYNOS_TMU_REG_INTSTAT; >> tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; >> @@ -782,6 +941,9 @@ static const struct of_device_id exynos_tmu_match[] = { >> { >> .compatible = "samsung,exynos5440-tmu", >> }, >> + { >> + .compatible = "samsung,exynos7-tmu", >> + }, >> {}, >> }; >> MODULE_DEVICE_TABLE(of, exynos_tmu_match); >> @@ -805,6 +967,8 @@ static int exynos_of_get_soc_type(struct device_node *np) >> return SOC_ARCH_EXYNOS5420_TRIMINFO; >> else if (of_device_is_compatible(np, "samsung,exynos5440-tmu")) >> return SOC_ARCH_EXYNOS5440; >> + else if (of_device_is_compatible(np, "samsung,exynos7-tmu")) >> + return SOC_ARCH_EXYNOS7; >> >> return -EINVAL; >> } >> @@ -928,6 +1092,13 @@ static int exynos_map_dt_data(struct platform_device *pdev) >> data->tmu_set_emulation = exynos5440_tmu_set_emulation; >> data->tmu_clear_irqs = exynos5440_tmu_clear_irqs; >> break; >> + case SOC_ARCH_EXYNOS7: >> + data->tmu_initialize = exynos7_tmu_initialize; >> + data->tmu_control = exynos7_tmu_control; >> + data->tmu_read = exynos7_tmu_read; >> + data->tmu_set_emulation = exynos4412_tmu_set_emulation; >> + data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; >> + break; >> default: >> dev_err(&pdev->dev, "Platform not supported\n"); >> return -EINVAL; >> @@ -1018,21 +1189,37 @@ static int exynos_tmu_probe(struct platform_device *pdev) >> goto err_clk_sec; >> } >> >> + if (data->soc == 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"); >> + goto err_clk; >> + } else { >> + ret = clk_prepare_enable(data->sclk); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to enable sclk\n"); >> + goto err_clk; >> + } >> + } >> + } >> + >> ret = exynos_tmu_initialize(pdev); >> if (ret) { >> dev_err(&pdev->dev, "Failed to initialize TMU\n"); >> - goto err_clk; >> + goto err_sclk; >> } >> >> ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, >> IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); >> if (ret) { >> dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); >> - goto err_clk; >> + goto err_sclk; >> } >> >> exynos_tmu_control(pdev, true); >> return 0; >> +err_sclk: >> + clk_disable_unprepare(data->sclk); >> err_clk: >> clk_unprepare(data->clk); >> err_clk_sec: >> @@ -1052,6 +1239,7 @@ static int exynos_tmu_remove(struct platform_device *pdev) >> thermal_zone_of_sensor_unregister(&pdev->dev, 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); >> diff --git a/drivers/thermal/samsung/exynos_tmu.h b/drivers/thermal/samsung/exynos_tmu.h >> index 9f9b1b8..4d71ec6 100644 >> --- a/drivers/thermal/samsung/exynos_tmu.h >> +++ b/drivers/thermal/samsung/exynos_tmu.h >> @@ -34,6 +34,7 @@ enum soc_type { >> SOC_ARCH_EXYNOS5420, >> SOC_ARCH_EXYNOS5420_TRIMINFO, >> SOC_ARCH_EXYNOS5440, >> + SOC_ARCH_EXYNOS7, >> }; >> >> /** >> -- >> 1.7.9.5 >> -- To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 852e622..660ff69 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -119,6 +119,26 @@ #define EXYNOS5440_TMU_TH_RISE4_SHIFT 24 #define EXYNOS5440_EFUSE_SWAP_OFFSET 8 +/* Exynos7 specific registers */ +#define EXYNOS7_THD_TEMP_RISE7_6 0x50 +#define EXYNOS7_THD_TEMP_FALL7_6 0x60 +#define EXYNOS7_TMU_REG_INTEN 0x110 +#define EXYNOS7_TMU_REG_INTPEND 0x118 +#define EXYNOS7_TMU_REG_EMUL_CON 0x160 + +#define EXYNOS7_TMU_TEMP_MASK 0x1ff +#define EXYNOS7_PD_DET_EN_SHIFT 23 +#define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 +#define EXYNOS7_TMU_INTEN_RISE1_SHIFT 1 +#define EXYNOS7_TMU_INTEN_RISE2_SHIFT 2 +#define EXYNOS7_TMU_INTEN_RISE3_SHIFT 3 +#define EXYNOS7_TMU_INTEN_RISE4_SHIFT 4 +#define EXYNOS7_TMU_INTEN_RISE5_SHIFT 5 +#define EXYNOS7_TMU_INTEN_RISE6_SHIFT 6 +#define EXYNOS7_TMU_INTEN_RISE7_SHIFT 7 +#define EXYNOS7_EMUL_DATA_SHIFT 7 +#define EXYNOS7_EMUL_DATA_MASK 0x1ff + #define MCELSIUS 1000 /** * struct exynos_tmu_data : A structure to hold the private data of the TMU @@ -133,6 +153,7 @@ * @lock: lock to implement synchronization. * @clk: pointer to the clock structure. * @clk_sec: pointer to the clock structure for accessing the base_second. + * @sclk: pointer to the clock structure for accessing the tmu special clk. * @temp_error1: fused value of the first point trim. * @temp_error2: fused value of the second point trim. * @regulator: pointer to the TMU regulator structure. @@ -152,8 +173,8 @@ struct exynos_tmu_data { enum soc_type soc; struct work_struct irq_work; struct mutex lock; - struct clk *clk, *clk_sec; - u8 temp_error1, temp_error2; + struct clk *clk, *clk_sec, *sclk; + u16 temp_error1, temp_error2; struct regulator *regulator; struct thermal_zone_device *tzd; @@ -223,7 +244,7 @@ static int temp_to_code(struct exynos_tmu_data *data, u8 temp) * Calculate a temperature value from a temperature code. * The unit of the temperature is degree Celsius. */ -static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code) +static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) { struct exynos_tmu_platform_data *pdata = data->pdata; int temp; @@ -513,6 +534,84 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev) return ret; } +static int exynos7_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 status, trim_info; + unsigned int rising_threshold = 0, falling_threshold = 0; + int ret = 0, threshold_code, i; + unsigned long temp, temp_hist; + unsigned int reg_off, bit_off; + + status = readb(data->base + EXYNOS_TMU_REG_STATUS); + if (!status) { + ret = -EBUSY; + goto out; + } + + trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); + + data->temp_error1 = trim_info & EXYNOS7_TMU_TEMP_MASK; + if (!data->temp_error1 || + (pdata->min_efuse_value > data->temp_error1) || + (data->temp_error1 > pdata->max_efuse_value)) + data->temp_error1 = pdata->efuse_value & 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. EXYNOS7_THD_TEMP_RISE7_6 (0x50) + * [24:16] - Threshold level 7 + * [8:0] - Threshold level 6 + * e.g. EXYNOS7_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 + + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); + rising_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); + rising_threshold |= threshold_code << (16 * bit_off); + writel(rising_threshold, + data->base + EXYNOS7_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 &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); + falling_threshold |= threshold_code << (16 * bit_off); + writel(falling_threshold, + data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); + } + + data->tmu_clear_irqs(data); +out: + return ret; +} + static void exynos4210_tmu_control(struct platform_device *pdev, bool on) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); @@ -573,6 +672,46 @@ static void exynos5440_tmu_control(struct platform_device *pdev, bool on) writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL); } +static void exynos7_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; + + 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 */ + } + con |= 1 << EXYNOS7_PD_DET_EN_SHIFT; + + writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); + writel(con, data->base + EXYNOS_TMU_REG_CONTROL); +} + static int exynos_get_temp(void *p, long *temp) { struct exynos_tmu_data *data = p; @@ -602,9 +741,19 @@ static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); } - val &= ~(EXYNOS_EMUL_DATA_MASK << EXYNOS_EMUL_DATA_SHIFT); - val |= (temp_to_code(data, temp) << EXYNOS_EMUL_DATA_SHIFT) | - EXYNOS_EMUL_ENABLE; + if (data->soc == SOC_ARCH_EXYNOS7) { + val &= ~(EXYNOS7_EMUL_DATA_MASK << + EXYNOS7_EMUL_DATA_SHIFT); + val |= (temp_to_code(data, temp) << + EXYNOS7_EMUL_DATA_SHIFT) | + EXYNOS_EMUL_ENABLE; + } else { + 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; } @@ -620,6 +769,8 @@ static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, if (data->soc == SOC_ARCH_EXYNOS5260) emul_con = EXYNOS5260_EMUL_CON; + else if (data->soc == SOC_ARCH_EXYNOS7) + emul_con = EXYNOS7_TMU_REG_EMUL_CON; else emul_con = EXYNOS_EMUL_CON; @@ -683,6 +834,12 @@ static int exynos5440_tmu_read(struct exynos_tmu_data *data) return readb(data->base + EXYNOS5440_TMU_S0_7_TEMP); } +static int exynos7_tmu_read(struct exynos_tmu_data *data) +{ + return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & + EXYNOS7_TMU_TEMP_MASK; +} + static void exynos_tmu_work(struct work_struct *work) { struct exynos_tmu_data *data = container_of(work, @@ -721,6 +878,8 @@ static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) if (data->soc == SOC_ARCH_EXYNOS5260) { tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; + } else if (data->soc == SOC_ARCH_EXYNOS7) { + tmu_intstat = tmu_intclear = EXYNOS7_TMU_REG_INTPEND; } else { tmu_intstat = EXYNOS_TMU_REG_INTSTAT; tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; @@ -782,6 +941,9 @@ static const struct of_device_id exynos_tmu_match[] = { { .compatible = "samsung,exynos5440-tmu", }, + { + .compatible = "samsung,exynos7-tmu", + }, {}, }; MODULE_DEVICE_TABLE(of, exynos_tmu_match); @@ -805,6 +967,8 @@ static int exynos_of_get_soc_type(struct device_node *np) return SOC_ARCH_EXYNOS5420_TRIMINFO; else if (of_device_is_compatible(np, "samsung,exynos5440-tmu")) return SOC_ARCH_EXYNOS5440; + else if (of_device_is_compatible(np, "samsung,exynos7-tmu")) + return SOC_ARCH_EXYNOS7; return -EINVAL; } @@ -928,6 +1092,13 @@ static int exynos_map_dt_data(struct platform_device *pdev) data->tmu_set_emulation = exynos5440_tmu_set_emulation; data->tmu_clear_irqs = exynos5440_tmu_clear_irqs; break; + case SOC_ARCH_EXYNOS7: + data->tmu_initialize = exynos7_tmu_initialize; + data->tmu_control = exynos7_tmu_control; + data->tmu_read = exynos7_tmu_read; + data->tmu_set_emulation = exynos4412_tmu_set_emulation; + data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; + break; default: dev_err(&pdev->dev, "Platform not supported\n"); return -EINVAL; @@ -1018,21 +1189,37 @@ static int exynos_tmu_probe(struct platform_device *pdev) goto err_clk_sec; } + if (data->soc == 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"); + goto err_clk; + } else { + ret = clk_prepare_enable(data->sclk); + if (ret) { + dev_err(&pdev->dev, "Failed to enable sclk\n"); + goto err_clk; + } + } + } + ret = exynos_tmu_initialize(pdev); if (ret) { dev_err(&pdev->dev, "Failed to initialize TMU\n"); - goto err_clk; + goto err_sclk; } ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); if (ret) { dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); - goto err_clk; + goto err_sclk; } exynos_tmu_control(pdev, true); return 0; +err_sclk: + clk_disable_unprepare(data->sclk); err_clk: clk_unprepare(data->clk); err_clk_sec: @@ -1052,6 +1239,7 @@ static int exynos_tmu_remove(struct platform_device *pdev) thermal_zone_of_sensor_unregister(&pdev->dev, 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); diff --git a/drivers/thermal/samsung/exynos_tmu.h b/drivers/thermal/samsung/exynos_tmu.h index 9f9b1b8..4d71ec6 100644 --- a/drivers/thermal/samsung/exynos_tmu.h +++ b/drivers/thermal/samsung/exynos_tmu.h @@ -34,6 +34,7 @@ enum soc_type { SOC_ARCH_EXYNOS5420, SOC_ARCH_EXYNOS5420_TRIMINFO, SOC_ARCH_EXYNOS5440, + SOC_ARCH_EXYNOS7, }; /**
Add registers, bit fields and compatible strings for Exynos7 TMU (Thermal Management Unit). Following are a few of the differences in the Exynos7 TMU from earlier SoCs: - 8 trigger levels - Different bit offsets and more registers for the rising and falling thresholds. - New power down detection bit in the TMU_CONTROL register which does not update the CURRENT_TEMP0 when tmu power down is detected. - Change in bit offset for the NEXT_DATA field of EMUL_CON register. EMUL_CON register address has also changed. - INTSTAT and INTCLEAR registers present in earlier SoCs have been combined into one INTPEND register. The register address for INTCLEAR and INTPEND is also different. - Since there are 8 rising/falling interrupts as against at most 4 in earlier SoCs the INTEN bit offsets are different. - Multiple probe support which is handled by a TMU_CONTROL1 register (No support for this in the current patch). This patch adds special clock support required only for Exynos7. It also updates the "code_to_temp" prototype as Exynos7 has 9 bit code-temp mapping. Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com> --- This patch set has been tested on linux next-20150123 with Eduardo's thermal-next branch merged along with the arch-side exynos7 related dts changes applied. Changes since v3: Addressed comments from Lukasz Majewski: - Added more comments in the code setting the thresholds. - Split the documentation out into another patch. Changes since v2: - Rebased on top of Lukasz' latest exynos tmu series (v4). - Added new exynos7 soc_type. Changes since v1: - Rebased on top of Lukasz' latest exynos tmu series (v2). - Added sclk support to patch adding Exynos7 tmu support. Previously, it was a separate patch. - Used the SOC type to decide if sclk is present. drivers/thermal/samsung/exynos_tmu.c | 204 ++++++++++++++++++++++++++++++++-- drivers/thermal/samsung/exynos_tmu.h | 1 + 2 files changed, 197 insertions(+), 8 deletions(-)