[PATCH v2 5/6] drivers/thermal/exynos: add initial Exynos850 support
Sam Protsenko
semen.protsenko at linaro.org
Fri Jul 26 11:44:49 PDT 2024
On Fri, Jul 26, 2024 at 6:01 AM Mateusz Majewski
<m.majewski2 at samsung.com> wrote:
>
> This is loosely adapted from an implementation available at
> https://gitlab.com/Linaro/96boards/e850-96/kernel/-/blob/android-exynos-4.14-linaro/drivers/thermal/samsung/exynos_tmu.c
> Some differences from that implementation:
> - unlike that implementation, we do not use the ACPM mechanism, instead
> we just access the registers, like we do for other SoCs,
> - the SoC is supposed to support multiple sensors inside one unit. The
> vendor implementation uses one kernel device per sensor, we would
> probably prefer to have one device for all sensors, have
> #thermal-sensor-cells = <1> and so on. We implemented this, but we
> could not get the extra sensors to work on our hardware so far. This
> might be due to a misconfiguration and we will probably come back to
> this, however our implementation only supports a single sensor for
> now,
> - the vendor implementation supports disabling CPU cores as a cooling
> device. We did not attempt to port this, and this would not really fit
> this driver anyway.
>
> Additionally, some differences from the other SoCs supported by this
> driver:
> - we do not really constrain the e-fuse information like the other SoCs
> do (data->{min,max}_efuse_value). In our tests, those values (as well
> as the raw sensor values) were much higher than in the other SoCs, to
> the degree that reusing the data->{min,max}_efuse_value from the other
> SoCs would cause instant critical temperature reset on boot,
> - this SoC provides more information in the e-fuse data than other SoCs,
> so we read some values inside exynos850_tmu_initialize instead of
> hardcoding them in exynos_map_dt_data.
>
> Signed-off-by: Mateusz Majewski <m.majewski2 at samsung.com>
> ---
Reviewed-by: Sam Protsenko <semen.protsenko at linaro.org>
> v1 -> v2: rename and reorder some registers, use the correct register
> offset for EXYNOS850_TMU_REG_AVG_CON, make the clock required,
> additionally do some minor style changes.
>
> drivers/thermal/samsung/exynos_tmu.c | 191 +++++++++++++++++++++++++--
> 1 file changed, 182 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c
> index 087a09628e23..2618a81fca53 100644
> --- a/drivers/thermal/samsung/exynos_tmu.c
> +++ b/drivers/thermal/samsung/exynos_tmu.c
> @@ -117,6 +117,41 @@
> #define EXYNOS7_EMUL_DATA_SHIFT 7
> #define EXYNOS7_EMUL_DATA_MASK 0x1ff
>
> +/* Exynos850 specific registers */
> +#define EXYNOS850_TMU_REG_CURRENT_TEMP0_1 0x40
> +#define EXYNOS850_TMU_REG_THD_TEMP0_RISE 0x50
> +#define EXYNOS850_TMU_REG_THD_TEMP0_FALL 0x60
> +
> +#define EXYNOS850_TMU_TRIMINFO_SHIFT 4
> +#define EXYNOS850_TMU_TRIMINFO_OFFSET(n) \
> + (EXYNOS_TMU_REG_TRIMINFO + (n) * EXYNOS850_TMU_TRIMINFO_SHIFT)
> +#define EXYNOS850_TMU_T_TRIM0_SHIFT 18
> +
> +#define EXYNOS850_TMU_REG_CONTROL1 0x24
> +#define EXYNOS850_TMU_LPI_MODE_MASK 1
> +#define EXYNOS850_TMU_LPI_MODE_SHIFT 10
> +
> +#define EXYNOS850_TMU_REG_COUNTER_VALUE0 0x30
> +#define EXYNOS850_TMU_EN_TEMP_SEN_OFF_MASK 0xffff
> +#define EXYNOS850_TMU_EN_TEMP_SEN_OFF_SHIFT 0
> +
> +#define EXYNOS850_TMU_REG_COUNTER_VALUE1 0x34
> +#define EXYNOS850_TMU_CLK_SENSE_ON_MASK 0xffff
> +#define EXYNOS850_TMU_CLK_SENSE_ON_SHIFT 16
> +
> +#define EXYNOS850_TMU_REG_AVG_CON 0x38
> +#define EXYNOS850_TMU_AVG_MODE_MASK 0x7
> +#define EXYNOS850_TMU_DEM_ENABLE BIT(4)
> +
> +#define EXYNOS850_TMU_REG_TRIM0 0x3c
> +#define EXYNOS850_TMU_TRIM0_MASK 0xf
> +#define EXYNOS850_TMU_VBEI_TRIM_SHIFT 8
> +#define EXYNOS850_TMU_VREF_TRIM_SHIFT 12
> +#define EXYNOS850_TMU_BGRI_TRIM_SHIFT 20
> +
> +#define EXYNOS850_TMU_TEM1051X_SENSE_VALUE 0x028a
> +#define EXYNOS850_TMU_TEM1456X_SENSE_VALUE 0x0a28
> +
> #define EXYNOS_FIRST_POINT_TRIM 25
> #define EXYNOS_SECOND_POINT_TRIM 85
>
> @@ -134,6 +169,7 @@ enum soc_type {
> SOC_ARCH_EXYNOS5420_TRIMINFO,
> SOC_ARCH_EXYNOS5433,
> SOC_ARCH_EXYNOS7,
> + SOC_ARCH_EXYNOS850,
> };
>
> /**
> @@ -232,12 +268,14 @@ static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code)
>
> static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info)
> {
> - u16 tmu_temp_mask =
> - (data->soc == SOC_ARCH_EXYNOS7) ? EXYNOS7_TMU_TEMP_MASK
> - : EXYNOS_TMU_TEMP_MASK;
> - int tmu_85_shift =
> - (data->soc == SOC_ARCH_EXYNOS7) ? EXYNOS7_TMU_TEMP_SHIFT
> - : EXYNOS_TRIMINFO_85_SHIFT;
> + u16 tmu_temp_mask = (data->soc == SOC_ARCH_EXYNOS7 ||
> + data->soc == SOC_ARCH_EXYNOS850) ?
> + EXYNOS7_TMU_TEMP_MASK :
> + EXYNOS_TMU_TEMP_MASK;
> + int tmu_85_shift = (data->soc == SOC_ARCH_EXYNOS7 ||
> + data->soc == SOC_ARCH_EXYNOS850) ?
> + EXYNOS7_TMU_TEMP_SHIFT :
> + EXYNOS_TRIMINFO_85_SHIFT;
>
> data->temp_error1 = trim_info & tmu_temp_mask;
> if (!data->temp_error1 ||
> @@ -587,6 +625,114 @@ static void exynos7_tmu_initialize(struct platform_device *pdev)
> sanitize_temp_error(data, trim_info);
> }
>
> +static void exynos850_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp)
> +{
> + exynos_tmu_update_temp(data, EXYNOS850_TMU_REG_THD_TEMP0_FALL + 12, 0,
> + temp);
> + exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN,
> + EXYNOS_TMU_INTEN_FALL0_SHIFT + 0, true);
> +}
> +
> +static void exynos850_tmu_set_high_temp(struct exynos_tmu_data *data, u8 temp)
> +{
> + exynos_tmu_update_temp(data, EXYNOS850_TMU_REG_THD_TEMP0_RISE + 12, 16,
> + temp);
> + exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN,
> + EXYNOS7_TMU_INTEN_RISE0_SHIFT + 1, true);
> +}
> +
> +static void exynos850_tmu_disable_low(struct exynos_tmu_data *data)
> +{
> + exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN,
> + EXYNOS_TMU_INTEN_FALL0_SHIFT + 0, false);
> +}
> +
> +static void exynos850_tmu_disable_high(struct exynos_tmu_data *data)
> +{
> + exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN,
> + EXYNOS7_TMU_INTEN_RISE0_SHIFT + 1, false);
> +}
> +
> +static void exynos850_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp)
> +{
> + exynos_tmu_update_temp(data, EXYNOS850_TMU_REG_THD_TEMP0_RISE + 0, 16,
> + temp);
> + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_CONTROL,
> + EXYNOS_TMU_THERM_TRIP_EN_SHIFT, true);
> + exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN,
> + EXYNOS7_TMU_INTEN_RISE0_SHIFT + 7, true);
> +}
> +
> +static void exynos850_tmu_initialize(struct platform_device *pdev)
> +{
> + struct exynos_tmu_data *data = platform_get_drvdata(pdev);
> + u32 cal_type, avg_mode, reg, bgri, vref, vbei;
> +
> + reg = readl(data->base + EXYNOS850_TMU_TRIMINFO_OFFSET(0));
> + cal_type = (reg & EXYNOS5433_TRIMINFO_CALIB_SEL_MASK) >>
> + EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT;
> + data->reference_voltage = (reg >> EXYNOS850_TMU_T_TRIM0_SHIFT) &
> + EXYNOS_TMU_REF_VOLTAGE_MASK;
> + reg = readl(data->base + EXYNOS850_TMU_TRIMINFO_OFFSET(1));
> + data->gain = (reg >> EXYNOS850_TMU_T_TRIM0_SHIFT) &
> + EXYNOS_TMU_BUF_SLOPE_SEL_MASK;
> + reg = readl(data->base + EXYNOS850_TMU_TRIMINFO_OFFSET(2));
> + avg_mode = (reg >> EXYNOS850_TMU_T_TRIM0_SHIFT) &
> + EXYNOS850_TMU_AVG_MODE_MASK;
> + reg = readl(data->base + EXYNOS850_TMU_TRIMINFO_OFFSET(3));
> + bgri = (reg >> EXYNOS850_TMU_T_TRIM0_SHIFT) & EXYNOS850_TMU_TRIM0_MASK;
> + reg = readl(data->base + EXYNOS850_TMU_TRIMINFO_OFFSET(4));
> + vref = (reg >> EXYNOS850_TMU_T_TRIM0_SHIFT) & EXYNOS850_TMU_TRIM0_MASK;
> + reg = readl(data->base + EXYNOS850_TMU_TRIMINFO_OFFSET(5));
> + vbei = (reg >> EXYNOS850_TMU_T_TRIM0_SHIFT) & EXYNOS850_TMU_TRIM0_MASK;
> +
> + data->cal_type = cal_type == EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING ?
> + TYPE_TWO_POINT_TRIMMING :
> + TYPE_ONE_POINT_TRIMMING;
> +
> + reg = readl(data->base + EXYNOS850_TMU_TRIMINFO_OFFSET(0));
> + sanitize_temp_error(data, reg);
> +
> + dev_info(&pdev->dev, "Calibration type is %d-point calibration\n",
> + cal_type ? 2 : 1);
> +
> + reg = readl(data->base + EXYNOS850_TMU_REG_AVG_CON);
> + reg &= ~EXYNOS850_TMU_AVG_MODE_MASK;
> + reg &= ~EXYNOS850_TMU_DEM_ENABLE;
> + if (avg_mode) {
> + reg |= avg_mode;
> + reg |= EXYNOS850_TMU_DEM_ENABLE;
> + }
> + writel(reg, data->base + EXYNOS850_TMU_REG_AVG_CON);
> +
> + reg = readl(data->base + EXYNOS850_TMU_REG_COUNTER_VALUE0);
> + reg &= ~(EXYNOS850_TMU_EN_TEMP_SEN_OFF_MASK
> + << EXYNOS850_TMU_EN_TEMP_SEN_OFF_SHIFT);
> + reg |= EXYNOS850_TMU_TEM1051X_SENSE_VALUE
> + << EXYNOS850_TMU_EN_TEMP_SEN_OFF_SHIFT;
> + writel(reg, data->base + EXYNOS850_TMU_REG_COUNTER_VALUE0);
> +
> + reg = readl(data->base + EXYNOS850_TMU_REG_COUNTER_VALUE1);
> + reg &= ~(EXYNOS850_TMU_CLK_SENSE_ON_MASK
> + << EXYNOS850_TMU_CLK_SENSE_ON_SHIFT);
> + reg |= EXYNOS850_TMU_TEM1051X_SENSE_VALUE
> + << EXYNOS850_TMU_CLK_SENSE_ON_SHIFT;
> + writel(reg, data->base + EXYNOS850_TMU_REG_COUNTER_VALUE1);
> +
> + reg = readl(data->base + EXYNOS850_TMU_REG_TRIM0);
> + reg &= ~(EXYNOS850_TMU_TRIM0_MASK << EXYNOS850_TMU_BGRI_TRIM_SHIFT);
> + reg &= ~(EXYNOS850_TMU_TRIM0_MASK << EXYNOS850_TMU_VREF_TRIM_SHIFT);
> + reg &= ~(EXYNOS850_TMU_TRIM0_MASK << EXYNOS850_TMU_VBEI_TRIM_SHIFT);
> + reg |= bgri << EXYNOS850_TMU_BGRI_TRIM_SHIFT;
> + reg |= vref << EXYNOS850_TMU_VREF_TRIM_SHIFT;
> + reg |= vbei << EXYNOS850_TMU_VBEI_TRIM_SHIFT;
> + writel(reg, data->base + EXYNOS850_TMU_REG_TRIM0);
> +
> + reg = readl(data->base + EXYNOS850_TMU_REG_CONTROL1);
> + reg &= ~(EXYNOS850_TMU_LPI_MODE_MASK << EXYNOS850_TMU_LPI_MODE_SHIFT);
> + writel(reg, data->base + EXYNOS850_TMU_REG_CONTROL1);
> +}
> +
> static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
> {
> struct exynos_tmu_data *data = platform_get_drvdata(pdev);
> @@ -676,7 +822,8 @@ 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);
> - if (data->soc == SOC_ARCH_EXYNOS7) {
> + if (data->soc == SOC_ARCH_EXYNOS7 ||
> + data->soc == SOC_ARCH_EXYNOS850) {
> val &= ~(EXYNOS7_EMUL_DATA_MASK <<
> EXYNOS7_EMUL_DATA_SHIFT);
> val |= (temp_to_code(data, temp) <<
> @@ -706,7 +853,8 @@ static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data,
> emul_con = EXYNOS5260_EMUL_CON;
> else if (data->soc == SOC_ARCH_EXYNOS5433)
> emul_con = EXYNOS5433_TMU_EMUL_CON;
> - else if (data->soc == SOC_ARCH_EXYNOS7)
> + else if (data->soc == SOC_ARCH_EXYNOS7 ||
> + data->soc == SOC_ARCH_EXYNOS850)
> emul_con = EXYNOS7_TMU_REG_EMUL_CON;
> else
> emul_con = EXYNOS_EMUL_CON;
> @@ -761,6 +909,12 @@ static int exynos7_tmu_read(struct exynos_tmu_data *data)
> EXYNOS7_TMU_TEMP_MASK;
> }
>
> +static int exynos850_tmu_read(struct exynos_tmu_data *data)
> +{
> + return readw(data->base + EXYNOS850_TMU_REG_CURRENT_TEMP0_1) &
> + EXYNOS7_TMU_TEMP_MASK;
> +}
> +
> static irqreturn_t exynos_tmu_threaded_irq(int irq, void *id)
> {
> struct exynos_tmu_data *data = id;
> @@ -787,7 +941,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) {
> + } else if (data->soc == SOC_ARCH_EXYNOS7 ||
> + data->soc == SOC_ARCH_EXYNOS850) {
> tmu_intstat = EXYNOS7_TMU_REG_INTPEND;
> tmu_intclear = EXYNOS7_TMU_REG_INTPEND;
> } else if (data->soc == SOC_ARCH_EXYNOS5433) {
> @@ -838,6 +993,9 @@ static const struct of_device_id exynos_tmu_match[] = {
> }, {
> .compatible = "samsung,exynos7-tmu",
> .data = (const void *)SOC_ARCH_EXYNOS7,
> + }, {
> + .compatible = "samsung,exynos850-tmu",
> + .data = (const void *)SOC_ARCH_EXYNOS850,
> },
> { },
> };
> @@ -950,6 +1108,21 @@ static int exynos_map_dt_data(struct platform_device *pdev)
> data->min_efuse_value = 15;
> data->max_efuse_value = 100;
> break;
> + case SOC_ARCH_EXYNOS850:
> + data->tmu_set_low_temp = exynos850_tmu_set_low_temp;
> + data->tmu_set_high_temp = exynos850_tmu_set_high_temp;
> + data->tmu_disable_low = exynos850_tmu_disable_low;
> + data->tmu_disable_high = exynos850_tmu_disable_high;
> + data->tmu_set_crit_temp = exynos850_tmu_set_crit_temp;
> + data->tmu_initialize = exynos850_tmu_initialize;
> + data->tmu_control = exynos4210_tmu_control;
> + data->tmu_read = exynos850_tmu_read;
> + data->tmu_set_emulation = exynos4412_tmu_set_emulation;
> + data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
> + data->efuse_value = 55;
> + data->min_efuse_value = 0;
> + data->max_efuse_value = 511;
> + break;
> default:
> dev_err(&pdev->dev, "Platform not supported\n");
> return -EINVAL;
> --
> 2.45.1
>
More information about the linux-arm-kernel
mailing list