From: Krzysztof Kozlowski <krzk@kernel.org> To: hypmean.kim@samsung.com, "bzolnier@gmail.com" <bzolnier@gmail.com>, "rafael@kernel.org" <rafael@kernel.org>, "daniel.lezcano@linaro.org" <daniel.lezcano@linaro.org>, "amitk@kernel.org" <amitk@kernel.org>, "rui.zhang@intel.com" <rui.zhang@intel.com>, ALIM AKHTAR <alim.akhtar@samsung.com>, "lgirdwood@gmail.com" <lgirdwood@gmail.com>, "broonie@kernel.org" <broonie@kernel.org> Cc: "linux-pm@vger.kernel.org" <linux-pm@vger.kernel.org>, "linux-samsung-soc@vger.kernel.org" <linux-samsung-soc@vger.kernel.org>, "linux-arm-kernel@lists.infradead.org" <linux-arm-kernel@lists.infradead.org>, "linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org> Subject: Re: [PATCH 2/2] thermal: exynos: Add support for ARTPEC-8 Date: Tue, 22 Mar 2022 18:48:12 +0100 [thread overview] Message-ID: <4ca669ea-fdf0-f4bb-7c68-47a26e72a4ce@kernel.org> (raw) In-Reply-To: <20220322082242epcms2p5b5e9e24a947e1d6c4b77fb9c95baf992@epcms2p5> On 22/03/2022 09:22, Sang Min Kim wrote: > Add support thermal management for Axis ARTPEC-8 SoC. > ARTPEC-8 is the SoC platform of Axis Communications. > In the existing thermal management function of exynos, functions that support > remote sensors have been added. > > Signed-off-by: sangmin kim <hypmean.kim@samsung.com> > --- > drivers/thermal/samsung/exynos_tmu.c | 666 ++++++++++++++++++++++++++++++++--- > 1 file changed, 616 insertions(+), 50 deletions(-) > > diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c > index f4ab4c5..9837f42 100644 > --- a/drivers/thermal/samsung/exynos_tmu.c > +++ b/drivers/thermal/samsung/exynos_tmu.c > @@ -14,6 +14,7 @@ > #include <linux/clk.h> > #include <linux/io.h> > #include <linux/interrupt.h> > +#include <linux/kernel.h> > #include <linux/module.h> > #include <linux/of_device.h> > #include <linux/of_address.h> > @@ -124,6 +125,77 @@ > > #define MCELSIUS 1000 > > +/* Artpec8 specific registers */ Pu all defines just after Exynos specific bits, so before MCELSIUS. > +#define ARTPEC8_TMU_REG_TRIMINFO 0x0 > +#define ARTPEC8_TMU_REG_TRIMINFO1 0x4 > +#define ARTPEC8_TMU_REG_TRIMINFO2 0x8 > +#define ARTPEC8_TMU_REG_TRIMINFO3 0xC > +#define ARTPEC8_TMU_REG_TRIMINFO4 0x10 > +#define ARTPEC8_TMU_REG_TRIMINFO5 0x14 > +#define ARTPEC8_TMU_REG_CONTROL 0x20 > +#define ARTPEC8_TMU_REG_CONTROL1 0x24 > +#define ARTPEC8_TMU_REG_STATUS 0x28 > + > +#define ARTPEC8_TMU_REG_AVG_CONTROL 0x38 > +#define ARTPEC8_TMU_REG_TMU_TRIM0 0x3C > + > +#define ARTPEC8_TMU_REG_EMUL_CON 0x160 > +#define NUM_PROBE_OFFSET 16 This is not prefixed with artpec and is very cryptic. Please document this and other defines which are not obvious, comparing to current code. > + > +#define ARTPEC8_FIRST_POINT_TRIM 25 > +#define ARTPEC8_SECOND_POINT_TRIM 105 > + > +#define ARTPEC8_EMUL_EN 1 > +#define ARTPEC8_TIME_OFFSET 16 Don't duplicate defines with existing ones. Define only differences. This applies to all other defines as well. > +#define ARTPEC8_EMUL_NEXT_TIME (0x4e20 << ARTPEC8_TIME_OFFSET) > + > +#define ARTPEC8_TMU_TEMP_MASK 0x1ff > +#define ARTPEC8_CALIB_SEL_SHIFT 23 > + > +#define ARTPEC8_EMUL_DATA_SHIFT 7 > + > +#define ARTPEC8_T_BUF_VREF_SEL_SHIFT 18 > +#define ARTPEC8_T_BUF_SLOPE_SEL_SHIFT 18 > +#define ARTPEC8_INTEN_TRIPPING_SHIFT 7 > +#define ARTPEC8_INTEN_CLOCKDOWN_SHIFT 8 > +#define ARTPEC8_TRIMINFO_105_SHIFT 9 > +#define ARTPEC8_INTEN_FALL0_SHIFT 16 > +#define ARTPEC8_TMU_REF_VOLTAGE_SHIFT 24 > +#define ARTPEC8_TMU_REF_VOLTAGE_MASK 0x1f > +#define ARTPEC8_TMU_BUF_SLOPE_SEL_SHIFT 8 > +#define ARTPEC8_TMU_BUF_SLOPE_SEL_MASK 0xf > + > +#define ARTPEC8_TMU_CONTROL_CORE_EN 1 > +#define ARTPEC8_TMU_CONTROL_AUTO_MODE 2 > +#define ARTPEC8_TMU_CONTROL_TRIP_EN (1 << 12) > +#define ARTPEC8_LPI_MODE_EN (1 << 10) > + > +#define ARTPEC8_TRIM0_BGR_I_SHIFT 20 > +#define ARTPEC8_TRIM0_VREF_SHIFT 12 > +#define ARTPEC8_TRIM0_VBE_I_SHIFT 8 > + > +#define INTPEND_RISE_MASK 0xff > +#define INTPEND_FALL_MASK 0xff0000 prefix > +#define ARTPEC8_TRIM0_MASK 0xf > +#define ARTPEC8_TRIM2_MASK 0x7 > + > +#define ARTPEC8_TRIMINFO_TRIM0_SHIFT 18 > + > +#define LOW_TEMP_WEIGHT 9203 > +#define HIGH_TEMP_WEIGHT 9745 > +#define TEMP_WEIGHT 10000 All these need explanation why/what/how did you get these values. > + > +struct sensor_offset { kerneldoc needed > + u32 trim_offset; > + u32 temp_offset; > + u32 temp_reg_shift; > + u32 rise_offset; > + u32 fall_offset; > + u32 past_offset; > + u32 inten; > + u32 intpend; > +}; > + > enum soc_type { > SOC_ARCH_EXYNOS3250 = 1, > SOC_ARCH_EXYNOS4210, > @@ -134,6 +206,63 @@ enum soc_type { > SOC_ARCH_EXYNOS5420_TRIMINFO, > SOC_ARCH_EXYNOS5433, > SOC_ARCH_EXYNOS7, > + SOC_ARCH_ARTPEC8, Put it alphabetically. > +}; > + > +#define SENSOR(_tr, _te, _sh, _ri, _fa, _pa, _en, _pend) \ > + { \ > + .trim_offset = _tr, \ > + .temp_offset = _te, \ > + .temp_reg_shift = _sh, \ > + .rise_offset = _ri, \ > + .fall_offset = _fa, \ > + .past_offset = _pa, \ > + .inten = _en, \ > + .intpend = _pend, \ > + } > + > +static const struct sensor_offset artpec8_sensors[] = { > + SENSOR(0x0, 0x40, 0, 0x50, 0x60, 0x70, 0x110, 0x118), 0x118 is existing value, right? All these should be using rather a macro - either dedicated defines or offset-based macros. > + SENSOR(0x4, 0x40, 9, 0x170, 0x180, 0x90, 0x120, 0x128), Here and further it looks like you have all registers distributed according to specific pattern. Define macro, probably with an offset based on first sensor. > + SENSOR(0x8, 0x44, 0, 0x190, 0x1a0, 0xb0, 0x130, 0x138), > + SENSOR(0xc, 0x44, 9, 0x1b0, 0x1c0, 0xd0, 0x140, 0x148), > + SENSOR(0x10, 0x44, 18, 0x1d0, 0x1e0, 0xf0, 0x150, 0x158), > + SENSOR(0x14, 0x48, 0, 0x1f0, 0x200, 0x250, 0x310, 0x318), > +}; > + > +/** > + * struct artpec8_sensor: A structure to hold the private data of the sensor > + * @tmudev: The tmu device which this sensor is connected. > + * @tzd: Thermal zonde device pointer to register this sensor. > + * @id: Identifier of the one instance of the thermal sensor. > + * @ntrip: Number of threshols for this sensor. > + * @triminfo_25: OTP information to trim temperature sensor error for 25C > + * @triminfo_105: OTP information to trim temperature sensor error for 105C > + * @trim_offset: Offset of triminfo register. > + * @temp_offset: Offset of current temperature. The temperature values of > + * 2 to 3 remote sensors are stored in this register. > + * @temp_reg_shift: start location of each tempt in temp_off > + * @rise_offset: Offset of rising threshold level 6 and 7. > + * @fall_offset: Offset of falling thershold level 6 and 7. > + * @past_offset: Offset of Past temperature 0,1. > + * @inten: Offset of interrupt enable sfr. > + * @intpend: Offset of interrupt pending sfr. > + */ > +struct artpec8_sensor { > + struct exynos_tmu_data *tmudev; > + struct thermal_zone_device *tzd; Why does the sensor duplicate struct exynos_tmu_data? > + int id; > + unsigned int ntrip; > + u16 triminfo_25; > + u16 triminfo_105; > + u32 trim_offset; > + u32 temp_offset; > + u32 temp_reg_shift; > + u32 rise_offset; > + u32 fall_offset; > + u32 past_offset; > + u32 inten; > + u32 intpend; You have all these in sensor_offset, don't you? Why do you need them second time? > }; > > /** > @@ -193,6 +322,7 @@ struct exynos_tmu_data { > struct thermal_zone_device *tzd; > unsigned int ntrip; > bool enabled; > + u32 nr_remote; Missing doc. Did you compile your code with W=1? > > void (*tmu_set_trip_temp)(struct exynos_tmu_data *data, int trip, > u8 temp); > @@ -203,6 +333,8 @@ struct exynos_tmu_data { > int (*tmu_read)(struct exynos_tmu_data *data); > void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp); > void (*tmu_clear_irqs)(struct exynos_tmu_data *data); > + > + struct artpec8_sensor sensor[0]; No artpec8_sensor, but Exynos sensor. You need to convert existing code for working with 1 sensors and X sensors. Don't just add X sensors duplicating parts of driver. Also - why this is array of [0]? > }; > > /* > @@ -220,6 +352,28 @@ static int temp_to_code(struct exynos_tmu_data *data, u8 temp) > data->temp_error1; > } > > +static u16 artpec8_temp_to_code(struct artpec8_sensor *sensor, int temp) Maintain consistent code with existing implementation. Why types are different than temp_to_code()? Maybe temp_to_code() is not good? > +{ > + int code; > + int weight; > + > + if (sensor->tmudev->cal_type == TYPE_ONE_POINT_TRIMMING) > + return temp + sensor->triminfo_25 - ARTPEC8_FIRST_POINT_TRIM; > + > + if (temp > ARTPEC8_FIRST_POINT_TRIM) > + weight = HIGH_TEMP_WEIGHT; > + else > + weight = LOW_TEMP_WEIGHT; > + > + code = DIV_ROUND_CLOSEST((temp - ARTPEC8_FIRST_POINT_TRIM) * > + (sensor->triminfo_105 - sensor->triminfo_25) * TEMP_WEIGHT, > + (ARTPEC8_SECOND_POINT_TRIM - ARTPEC8_FIRST_POINT_TRIM) * > + weight); > + code += sensor->triminfo_25; > + > + return (u16)code; > +} > + > /* > * Calculate a temperature value from a temperature code. > * The unit of the temperature is degree Celsius. > @@ -235,6 +389,27 @@ static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) > EXYNOS_FIRST_POINT_TRIM; > } > > +static int artpec8_code_to_temp(struct artpec8_sensor *sensor, u16 code) > +{ > + int temp; > + int weight; > + > + if (sensor->tmudev->cal_type == TYPE_ONE_POINT_TRIMMING) > + return code - sensor->triminfo_25 + ARTPEC8_FIRST_POINT_TRIM; > + > + if (code > sensor->triminfo_25) > + weight = HIGH_TEMP_WEIGHT; > + else > + weight = LOW_TEMP_WEIGHT; > + > + temp = DIV_ROUND_CLOSEST((code - sensor->triminfo_25) * > + (ARTPEC8_SECOND_POINT_TRIM - ARTPEC8_FIRST_POINT_TRIM) * weight, > + (sensor->triminfo_105 - sensor->triminfo_25) * TEMP_WEIGHT); > + temp += ARTPEC8_FIRST_POINT_TRIM; > + > + return temp; > +} > + > static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) > { > u16 tmu_temp_mask = > @@ -338,7 +513,8 @@ static u32 get_con_reg(struct exynos_tmu_data *data, u32 con) > con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT); > con |= data->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; > > - con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); > + con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << > + EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); How is this related? > con |= (data->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); > > con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); > @@ -558,6 +734,120 @@ static void exynos7_tmu_initialize(struct platform_device *pdev) > sanitize_temp_error(data, trim_info); > } > > +static void artpec8_tmu_set_trip_temp(struct exynos_tmu_data *data, > + int trip, int temp, int remote) > +{ > + unsigned int reg_off, bit_off; > + u32 th; > + struct artpec8_sensor *sensor; > + unsigned int temp_rise; > + > + sensor = &data->sensor[remote]; > + temp_rise = sensor->rise_offset; > + > + reg_off = ((7 - trip) / 2) * 4; > + bit_off = ((8 - trip) % 2); Please explain the offsets in comment. > + > + th = readl(data->base + temp_rise + reg_off); > + th &= ~(ARTPEC8_TMU_TEMP_MASK << (16 * bit_off)); > + th |= artpec8_temp_to_code(sensor, temp) << (16 * bit_off); > + writel(th, data->base + temp_rise + reg_off); > +} > + > +static void artpec8_tmu_set_trip_hyst(struct exynos_tmu_data *data, > + int trip, int temp, int hyst, int remote) > +{ > + unsigned int reg_off, bit_off; > + u32 th; > + struct artpec8_sensor *sensor; > + unsigned int temp_fall; > + > + sensor = &data->sensor[remote]; > + temp_fall = sensor->fall_offset; > + > + reg_off = ((7 - trip) / 2) * 4; > + bit_off = ((8 - trip) % 2); > + > + th = readl(data->base + temp_fall + reg_off); > + th &= ~(ARTPEC8_TMU_TEMP_MASK << (16 * bit_off)); > + th |= artpec8_temp_to_code(sensor, temp - hyst) << (16 * bit_off); > + writel(th, data->base + temp_fall + reg_off); > +} > + > +static void artpec8_tmu_clear_irqs(struct exynos_tmu_data *data, int i) > +{ > + u32 intp = readl(data->base + data->sensor[i].intpend); > + > + writel(intp, data->base + data->sensor[i].intpend); > +} > + I'll skip reviewing this part. Half of it will be gone once you convert driver to have uniform approach to sensors. (...) > +static int artpec8_register_tzd(struct platform_device *pdev) > +{ > + struct exynos_tmu_data *data = platform_get_drvdata(pdev); > + struct artpec8_sensor *sensor; > + struct device *dev = &pdev->dev; > + int sensor_idx, ret = 0; > + struct thermal_zone_device *tzd; > + const struct thermal_trip *trips; > + > + for (sensor_idx = 0; sensor_idx < data->nr_remote; sensor_idx++) { > + sensor = &data->sensor[sensor_idx]; > + > + ret = artpec8_map_sensor_data(data, sensor_idx, sensor); > + if (ret) > + break; > + > + tzd = devm_thermal_zone_of_sensor_register(dev, > + sensor_idx, sensor, &artpec8_ops); > + if (IS_ERR(tzd)) > + continue; > + > + sensor->tzd = tzd; > + trips = of_thermal_get_trip_points(tzd); > + if (!trips) { > + dev_warn(dev, > + "Cannot get trip points from device tree!\n"); > + ret = -ENODEV; > + break; > + } > + sensor->ntrip = of_thermal_get_ntrips(tzd); > + } > + > + return ret; > +} > + > static int exynos_tmu_probe(struct platform_device *pdev) > { > struct exynos_tmu_data *data; > int ret; > + int sensor_idx; > + int nr_remote = 0; > + struct device *dev; > + const struct of_device_id *dev_id; > > - data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), > - GFP_KERNEL); > + if (pdev->dev.of_node) > + dev = &pdev->dev; > + else > + dev = pdev->dev.parent; Whaaaaat? > + > + dev_id = of_match_node(exynos_tmu_match, dev->of_node); > + if (dev_id) { > + �� data = (struct exynos_tmu_data *)dev_id->data; Some unusual characters appeared here. Did you run checkpatch, smatch and sparse? > + } else { > + dev_warn(dev, "dev id error\n"); Is it possible? > + return -EINVAL; > + } > + > + ret = of_property_read_u32(dev->of_node, "remote_sensors", &nr_remote); Do not add undocumented properties. > + if (ret < 0) > + data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), > + GFP_KERNEL); > + else > + data = devm_kzalloc(dev, sizeof(struct exynos_tmu_data) + > + (sizeof(struct artpec8_sensor) * nr_remote), > + GFP_KERNEL); > if (!data) > return -ENOMEM; > > platform_set_drvdata(pdev, data); > mutex_init(&data->lock); > > + if (data->soc != SOC_ARCH_ARTPEC8) { > /* > * Try enabling the regulator if found > * TODO: Add regulator as an SOC feature, so that regulator enable > * is a compulsory call. > */ Does not look like proper indentation. > - data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu"); > - if (!IS_ERR(data->regulator)) { > - ret = regulator_enable(data->regulator); > - if (ret) { > - dev_err(&pdev->dev, "failed to enable vtmu\n"); > - return ret; > + data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu"); > + if (!IS_ERR(data->regulator)) { > + ret = regulator_enable(data->regulator); > + if (ret) { > + dev_err(&pdev->dev, "failed to enable vtmu\n"); > + return ret; > + } > + } else { > + if (PTR_ERR(data->regulator) == -EPROBE_DEFER) > + return -EPROBE_DEFER; > + dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); > } > - } else { > - if (PTR_ERR(data->regulator) == -EPROBE_DEFER) > - return -EPROBE_DEFER; > - dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); > } > > ret = exynos_map_dt_data(pdev); > if (ret) > goto err_sensor; > > - INIT_WORK(&data->irq_work, exynos_tmu_work); > + if (data->soc == SOC_ARCH_ARTPEC8) { > + ret = artpec8_register_tzd(pdev); > + if (ret) > + return -EINVAL; > + > + INIT_WORK(&data->irq_work, artpec8_tmu_work); > + } else { > + INIT_WORK(&data->irq_work, exynos_tmu_work); > + } No, this should be parametrized via ops in exynos_tmu_data (one more op for "work"). > > data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); > if (IS_ERR(data->clk)) { > @@ -1046,18 +1590,21 @@ static int exynos_tmu_probe(struct platform_device *pdev) > goto err_sensor; > } > > - data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); > - if (IS_ERR(data->clk_sec)) { > - if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { > - dev_err(&pdev->dev, "Failed to get triminfo clock\n"); > - ret = PTR_ERR(data->clk_sec); > - goto err_sensor; > - } > - } else { > - ret = clk_prepare(data->clk_sec); > - if (ret) { > - dev_err(&pdev->dev, "Failed to get clock\n"); > - goto err_sensor; > + if (data->soc != SOC_ARCH_ARTPEC8) { > + data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); > + if (IS_ERR(data->clk_sec)) { > + if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { > + dev_err(&pdev->dev, > + "Failed to get triminfo clock\n"); > + ret = PTR_ERR(data->clk_sec); > + goto err_sensor; > + } > + } else { > + ret = clk_prepare(data->clk_sec); > + if (ret) { > + dev_err(&pdev->dev, "Failed to get clock\n"); > + goto err_sensor; > + } > } > } > > @@ -1070,6 +1617,7 @@ static int exynos_tmu_probe(struct platform_device *pdev) > switch (data->soc) { > case SOC_ARCH_EXYNOS5433: > case SOC_ARCH_EXYNOS7: > + case SOC_ARCH_ARTPEC8: > data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk"); > if (IS_ERR(data->sclk)) { > dev_err(&pdev->dev, "Failed to get sclk\n"); > @@ -1087,24 +1635,26 @@ static int exynos_tmu_probe(struct platform_device *pdev) > break; > } > > + if (data->soc != SOC_ARCH_ARTPEC8) { > /* > * data->tzd must be registered before calling exynos_tmu_initialize(), > * requesting irq and calling exynos_tmu_control(). > */ Again wrong indentation. Best regards, Krzysztof
WARNING: multiple messages have this Message-ID (diff)
From: Krzysztof Kozlowski <krzk@kernel.org> To: hypmean.kim@samsung.com, "bzolnier@gmail.com" <bzolnier@gmail.com>, "rafael@kernel.org" <rafael@kernel.org>, "daniel.lezcano@linaro.org" <daniel.lezcano@linaro.org>, "amitk@kernel.org" <amitk@kernel.org>, "rui.zhang@intel.com" <rui.zhang@intel.com>, ALIM AKHTAR <alim.akhtar@samsung.com>, "lgirdwood@gmail.com" <lgirdwood@gmail.com>, "broonie@kernel.org" <broonie@kernel.org> Cc: "linux-pm@vger.kernel.org" <linux-pm@vger.kernel.org>, "linux-samsung-soc@vger.kernel.org" <linux-samsung-soc@vger.kernel.org>, "linux-arm-kernel@lists.infradead.org" <linux-arm-kernel@lists.infradead.org>, "linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org> Subject: Re: [PATCH 2/2] thermal: exynos: Add support for ARTPEC-8 Date: Tue, 22 Mar 2022 18:48:12 +0100 [thread overview] Message-ID: <4ca669ea-fdf0-f4bb-7c68-47a26e72a4ce@kernel.org> (raw) In-Reply-To: <20220322082242epcms2p5b5e9e24a947e1d6c4b77fb9c95baf992@epcms2p5> On 22/03/2022 09:22, Sang Min Kim wrote: > Add support thermal management for Axis ARTPEC-8 SoC. > ARTPEC-8 is the SoC platform of Axis Communications. > In the existing thermal management function of exynos, functions that support > remote sensors have been added. > > Signed-off-by: sangmin kim <hypmean.kim@samsung.com> > --- > drivers/thermal/samsung/exynos_tmu.c | 666 ++++++++++++++++++++++++++++++++--- > 1 file changed, 616 insertions(+), 50 deletions(-) > > diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c > index f4ab4c5..9837f42 100644 > --- a/drivers/thermal/samsung/exynos_tmu.c > +++ b/drivers/thermal/samsung/exynos_tmu.c > @@ -14,6 +14,7 @@ > #include <linux/clk.h> > #include <linux/io.h> > #include <linux/interrupt.h> > +#include <linux/kernel.h> > #include <linux/module.h> > #include <linux/of_device.h> > #include <linux/of_address.h> > @@ -124,6 +125,77 @@ > > #define MCELSIUS 1000 > > +/* Artpec8 specific registers */ Pu all defines just after Exynos specific bits, so before MCELSIUS. > +#define ARTPEC8_TMU_REG_TRIMINFO 0x0 > +#define ARTPEC8_TMU_REG_TRIMINFO1 0x4 > +#define ARTPEC8_TMU_REG_TRIMINFO2 0x8 > +#define ARTPEC8_TMU_REG_TRIMINFO3 0xC > +#define ARTPEC8_TMU_REG_TRIMINFO4 0x10 > +#define ARTPEC8_TMU_REG_TRIMINFO5 0x14 > +#define ARTPEC8_TMU_REG_CONTROL 0x20 > +#define ARTPEC8_TMU_REG_CONTROL1 0x24 > +#define ARTPEC8_TMU_REG_STATUS 0x28 > + > +#define ARTPEC8_TMU_REG_AVG_CONTROL 0x38 > +#define ARTPEC8_TMU_REG_TMU_TRIM0 0x3C > + > +#define ARTPEC8_TMU_REG_EMUL_CON 0x160 > +#define NUM_PROBE_OFFSET 16 This is not prefixed with artpec and is very cryptic. Please document this and other defines which are not obvious, comparing to current code. > + > +#define ARTPEC8_FIRST_POINT_TRIM 25 > +#define ARTPEC8_SECOND_POINT_TRIM 105 > + > +#define ARTPEC8_EMUL_EN 1 > +#define ARTPEC8_TIME_OFFSET 16 Don't duplicate defines with existing ones. Define only differences. This applies to all other defines as well. > +#define ARTPEC8_EMUL_NEXT_TIME (0x4e20 << ARTPEC8_TIME_OFFSET) > + > +#define ARTPEC8_TMU_TEMP_MASK 0x1ff > +#define ARTPEC8_CALIB_SEL_SHIFT 23 > + > +#define ARTPEC8_EMUL_DATA_SHIFT 7 > + > +#define ARTPEC8_T_BUF_VREF_SEL_SHIFT 18 > +#define ARTPEC8_T_BUF_SLOPE_SEL_SHIFT 18 > +#define ARTPEC8_INTEN_TRIPPING_SHIFT 7 > +#define ARTPEC8_INTEN_CLOCKDOWN_SHIFT 8 > +#define ARTPEC8_TRIMINFO_105_SHIFT 9 > +#define ARTPEC8_INTEN_FALL0_SHIFT 16 > +#define ARTPEC8_TMU_REF_VOLTAGE_SHIFT 24 > +#define ARTPEC8_TMU_REF_VOLTAGE_MASK 0x1f > +#define ARTPEC8_TMU_BUF_SLOPE_SEL_SHIFT 8 > +#define ARTPEC8_TMU_BUF_SLOPE_SEL_MASK 0xf > + > +#define ARTPEC8_TMU_CONTROL_CORE_EN 1 > +#define ARTPEC8_TMU_CONTROL_AUTO_MODE 2 > +#define ARTPEC8_TMU_CONTROL_TRIP_EN (1 << 12) > +#define ARTPEC8_LPI_MODE_EN (1 << 10) > + > +#define ARTPEC8_TRIM0_BGR_I_SHIFT 20 > +#define ARTPEC8_TRIM0_VREF_SHIFT 12 > +#define ARTPEC8_TRIM0_VBE_I_SHIFT 8 > + > +#define INTPEND_RISE_MASK 0xff > +#define INTPEND_FALL_MASK 0xff0000 prefix > +#define ARTPEC8_TRIM0_MASK 0xf > +#define ARTPEC8_TRIM2_MASK 0x7 > + > +#define ARTPEC8_TRIMINFO_TRIM0_SHIFT 18 > + > +#define LOW_TEMP_WEIGHT 9203 > +#define HIGH_TEMP_WEIGHT 9745 > +#define TEMP_WEIGHT 10000 All these need explanation why/what/how did you get these values. > + > +struct sensor_offset { kerneldoc needed > + u32 trim_offset; > + u32 temp_offset; > + u32 temp_reg_shift; > + u32 rise_offset; > + u32 fall_offset; > + u32 past_offset; > + u32 inten; > + u32 intpend; > +}; > + > enum soc_type { > SOC_ARCH_EXYNOS3250 = 1, > SOC_ARCH_EXYNOS4210, > @@ -134,6 +206,63 @@ enum soc_type { > SOC_ARCH_EXYNOS5420_TRIMINFO, > SOC_ARCH_EXYNOS5433, > SOC_ARCH_EXYNOS7, > + SOC_ARCH_ARTPEC8, Put it alphabetically. > +}; > + > +#define SENSOR(_tr, _te, _sh, _ri, _fa, _pa, _en, _pend) \ > + { \ > + .trim_offset = _tr, \ > + .temp_offset = _te, \ > + .temp_reg_shift = _sh, \ > + .rise_offset = _ri, \ > + .fall_offset = _fa, \ > + .past_offset = _pa, \ > + .inten = _en, \ > + .intpend = _pend, \ > + } > + > +static const struct sensor_offset artpec8_sensors[] = { > + SENSOR(0x0, 0x40, 0, 0x50, 0x60, 0x70, 0x110, 0x118), 0x118 is existing value, right? All these should be using rather a macro - either dedicated defines or offset-based macros. > + SENSOR(0x4, 0x40, 9, 0x170, 0x180, 0x90, 0x120, 0x128), Here and further it looks like you have all registers distributed according to specific pattern. Define macro, probably with an offset based on first sensor. > + SENSOR(0x8, 0x44, 0, 0x190, 0x1a0, 0xb0, 0x130, 0x138), > + SENSOR(0xc, 0x44, 9, 0x1b0, 0x1c0, 0xd0, 0x140, 0x148), > + SENSOR(0x10, 0x44, 18, 0x1d0, 0x1e0, 0xf0, 0x150, 0x158), > + SENSOR(0x14, 0x48, 0, 0x1f0, 0x200, 0x250, 0x310, 0x318), > +}; > + > +/** > + * struct artpec8_sensor: A structure to hold the private data of the sensor > + * @tmudev: The tmu device which this sensor is connected. > + * @tzd: Thermal zonde device pointer to register this sensor. > + * @id: Identifier of the one instance of the thermal sensor. > + * @ntrip: Number of threshols for this sensor. > + * @triminfo_25: OTP information to trim temperature sensor error for 25C > + * @triminfo_105: OTP information to trim temperature sensor error for 105C > + * @trim_offset: Offset of triminfo register. > + * @temp_offset: Offset of current temperature. The temperature values of > + * 2 to 3 remote sensors are stored in this register. > + * @temp_reg_shift: start location of each tempt in temp_off > + * @rise_offset: Offset of rising threshold level 6 and 7. > + * @fall_offset: Offset of falling thershold level 6 and 7. > + * @past_offset: Offset of Past temperature 0,1. > + * @inten: Offset of interrupt enable sfr. > + * @intpend: Offset of interrupt pending sfr. > + */ > +struct artpec8_sensor { > + struct exynos_tmu_data *tmudev; > + struct thermal_zone_device *tzd; Why does the sensor duplicate struct exynos_tmu_data? > + int id; > + unsigned int ntrip; > + u16 triminfo_25; > + u16 triminfo_105; > + u32 trim_offset; > + u32 temp_offset; > + u32 temp_reg_shift; > + u32 rise_offset; > + u32 fall_offset; > + u32 past_offset; > + u32 inten; > + u32 intpend; You have all these in sensor_offset, don't you? Why do you need them second time? > }; > > /** > @@ -193,6 +322,7 @@ struct exynos_tmu_data { > struct thermal_zone_device *tzd; > unsigned int ntrip; > bool enabled; > + u32 nr_remote; Missing doc. Did you compile your code with W=1? > > void (*tmu_set_trip_temp)(struct exynos_tmu_data *data, int trip, > u8 temp); > @@ -203,6 +333,8 @@ struct exynos_tmu_data { > int (*tmu_read)(struct exynos_tmu_data *data); > void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp); > void (*tmu_clear_irqs)(struct exynos_tmu_data *data); > + > + struct artpec8_sensor sensor[0]; No artpec8_sensor, but Exynos sensor. You need to convert existing code for working with 1 sensors and X sensors. Don't just add X sensors duplicating parts of driver. Also - why this is array of [0]? > }; > > /* > @@ -220,6 +352,28 @@ static int temp_to_code(struct exynos_tmu_data *data, u8 temp) > data->temp_error1; > } > > +static u16 artpec8_temp_to_code(struct artpec8_sensor *sensor, int temp) Maintain consistent code with existing implementation. Why types are different than temp_to_code()? Maybe temp_to_code() is not good? > +{ > + int code; > + int weight; > + > + if (sensor->tmudev->cal_type == TYPE_ONE_POINT_TRIMMING) > + return temp + sensor->triminfo_25 - ARTPEC8_FIRST_POINT_TRIM; > + > + if (temp > ARTPEC8_FIRST_POINT_TRIM) > + weight = HIGH_TEMP_WEIGHT; > + else > + weight = LOW_TEMP_WEIGHT; > + > + code = DIV_ROUND_CLOSEST((temp - ARTPEC8_FIRST_POINT_TRIM) * > + (sensor->triminfo_105 - sensor->triminfo_25) * TEMP_WEIGHT, > + (ARTPEC8_SECOND_POINT_TRIM - ARTPEC8_FIRST_POINT_TRIM) * > + weight); > + code += sensor->triminfo_25; > + > + return (u16)code; > +} > + > /* > * Calculate a temperature value from a temperature code. > * The unit of the temperature is degree Celsius. > @@ -235,6 +389,27 @@ static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) > EXYNOS_FIRST_POINT_TRIM; > } > > +static int artpec8_code_to_temp(struct artpec8_sensor *sensor, u16 code) > +{ > + int temp; > + int weight; > + > + if (sensor->tmudev->cal_type == TYPE_ONE_POINT_TRIMMING) > + return code - sensor->triminfo_25 + ARTPEC8_FIRST_POINT_TRIM; > + > + if (code > sensor->triminfo_25) > + weight = HIGH_TEMP_WEIGHT; > + else > + weight = LOW_TEMP_WEIGHT; > + > + temp = DIV_ROUND_CLOSEST((code - sensor->triminfo_25) * > + (ARTPEC8_SECOND_POINT_TRIM - ARTPEC8_FIRST_POINT_TRIM) * weight, > + (sensor->triminfo_105 - sensor->triminfo_25) * TEMP_WEIGHT); > + temp += ARTPEC8_FIRST_POINT_TRIM; > + > + return temp; > +} > + > static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) > { > u16 tmu_temp_mask = > @@ -338,7 +513,8 @@ static u32 get_con_reg(struct exynos_tmu_data *data, u32 con) > con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT); > con |= data->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; > > - con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); > + con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << > + EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); How is this related? > con |= (data->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); > > con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); > @@ -558,6 +734,120 @@ static void exynos7_tmu_initialize(struct platform_device *pdev) > sanitize_temp_error(data, trim_info); > } > > +static void artpec8_tmu_set_trip_temp(struct exynos_tmu_data *data, > + int trip, int temp, int remote) > +{ > + unsigned int reg_off, bit_off; > + u32 th; > + struct artpec8_sensor *sensor; > + unsigned int temp_rise; > + > + sensor = &data->sensor[remote]; > + temp_rise = sensor->rise_offset; > + > + reg_off = ((7 - trip) / 2) * 4; > + bit_off = ((8 - trip) % 2); Please explain the offsets in comment. > + > + th = readl(data->base + temp_rise + reg_off); > + th &= ~(ARTPEC8_TMU_TEMP_MASK << (16 * bit_off)); > + th |= artpec8_temp_to_code(sensor, temp) << (16 * bit_off); > + writel(th, data->base + temp_rise + reg_off); > +} > + > +static void artpec8_tmu_set_trip_hyst(struct exynos_tmu_data *data, > + int trip, int temp, int hyst, int remote) > +{ > + unsigned int reg_off, bit_off; > + u32 th; > + struct artpec8_sensor *sensor; > + unsigned int temp_fall; > + > + sensor = &data->sensor[remote]; > + temp_fall = sensor->fall_offset; > + > + reg_off = ((7 - trip) / 2) * 4; > + bit_off = ((8 - trip) % 2); > + > + th = readl(data->base + temp_fall + reg_off); > + th &= ~(ARTPEC8_TMU_TEMP_MASK << (16 * bit_off)); > + th |= artpec8_temp_to_code(sensor, temp - hyst) << (16 * bit_off); > + writel(th, data->base + temp_fall + reg_off); > +} > + > +static void artpec8_tmu_clear_irqs(struct exynos_tmu_data *data, int i) > +{ > + u32 intp = readl(data->base + data->sensor[i].intpend); > + > + writel(intp, data->base + data->sensor[i].intpend); > +} > + I'll skip reviewing this part. Half of it will be gone once you convert driver to have uniform approach to sensors. (...) > +static int artpec8_register_tzd(struct platform_device *pdev) > +{ > + struct exynos_tmu_data *data = platform_get_drvdata(pdev); > + struct artpec8_sensor *sensor; > + struct device *dev = &pdev->dev; > + int sensor_idx, ret = 0; > + struct thermal_zone_device *tzd; > + const struct thermal_trip *trips; > + > + for (sensor_idx = 0; sensor_idx < data->nr_remote; sensor_idx++) { > + sensor = &data->sensor[sensor_idx]; > + > + ret = artpec8_map_sensor_data(data, sensor_idx, sensor); > + if (ret) > + break; > + > + tzd = devm_thermal_zone_of_sensor_register(dev, > + sensor_idx, sensor, &artpec8_ops); > + if (IS_ERR(tzd)) > + continue; > + > + sensor->tzd = tzd; > + trips = of_thermal_get_trip_points(tzd); > + if (!trips) { > + dev_warn(dev, > + "Cannot get trip points from device tree!\n"); > + ret = -ENODEV; > + break; > + } > + sensor->ntrip = of_thermal_get_ntrips(tzd); > + } > + > + return ret; > +} > + > static int exynos_tmu_probe(struct platform_device *pdev) > { > struct exynos_tmu_data *data; > int ret; > + int sensor_idx; > + int nr_remote = 0; > + struct device *dev; > + const struct of_device_id *dev_id; > > - data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), > - GFP_KERNEL); > + if (pdev->dev.of_node) > + dev = &pdev->dev; > + else > + dev = pdev->dev.parent; Whaaaaat? > + > + dev_id = of_match_node(exynos_tmu_match, dev->of_node); > + if (dev_id) { > + �� data = (struct exynos_tmu_data *)dev_id->data; Some unusual characters appeared here. Did you run checkpatch, smatch and sparse? > + } else { > + dev_warn(dev, "dev id error\n"); Is it possible? > + return -EINVAL; > + } > + > + ret = of_property_read_u32(dev->of_node, "remote_sensors", &nr_remote); Do not add undocumented properties. > + if (ret < 0) > + data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), > + GFP_KERNEL); > + else > + data = devm_kzalloc(dev, sizeof(struct exynos_tmu_data) + > + (sizeof(struct artpec8_sensor) * nr_remote), > + GFP_KERNEL); > if (!data) > return -ENOMEM; > > platform_set_drvdata(pdev, data); > mutex_init(&data->lock); > > + if (data->soc != SOC_ARCH_ARTPEC8) { > /* > * Try enabling the regulator if found > * TODO: Add regulator as an SOC feature, so that regulator enable > * is a compulsory call. > */ Does not look like proper indentation. > - data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu"); > - if (!IS_ERR(data->regulator)) { > - ret = regulator_enable(data->regulator); > - if (ret) { > - dev_err(&pdev->dev, "failed to enable vtmu\n"); > - return ret; > + data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu"); > + if (!IS_ERR(data->regulator)) { > + ret = regulator_enable(data->regulator); > + if (ret) { > + dev_err(&pdev->dev, "failed to enable vtmu\n"); > + return ret; > + } > + } else { > + if (PTR_ERR(data->regulator) == -EPROBE_DEFER) > + return -EPROBE_DEFER; > + dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); > } > - } else { > - if (PTR_ERR(data->regulator) == -EPROBE_DEFER) > - return -EPROBE_DEFER; > - dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); > } > > ret = exynos_map_dt_data(pdev); > if (ret) > goto err_sensor; > > - INIT_WORK(&data->irq_work, exynos_tmu_work); > + if (data->soc == SOC_ARCH_ARTPEC8) { > + ret = artpec8_register_tzd(pdev); > + if (ret) > + return -EINVAL; > + > + INIT_WORK(&data->irq_work, artpec8_tmu_work); > + } else { > + INIT_WORK(&data->irq_work, exynos_tmu_work); > + } No, this should be parametrized via ops in exynos_tmu_data (one more op for "work"). > > data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); > if (IS_ERR(data->clk)) { > @@ -1046,18 +1590,21 @@ static int exynos_tmu_probe(struct platform_device *pdev) > goto err_sensor; > } > > - data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); > - if (IS_ERR(data->clk_sec)) { > - if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { > - dev_err(&pdev->dev, "Failed to get triminfo clock\n"); > - ret = PTR_ERR(data->clk_sec); > - goto err_sensor; > - } > - } else { > - ret = clk_prepare(data->clk_sec); > - if (ret) { > - dev_err(&pdev->dev, "Failed to get clock\n"); > - goto err_sensor; > + if (data->soc != SOC_ARCH_ARTPEC8) { > + data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); > + if (IS_ERR(data->clk_sec)) { > + if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { > + dev_err(&pdev->dev, > + "Failed to get triminfo clock\n"); > + ret = PTR_ERR(data->clk_sec); > + goto err_sensor; > + } > + } else { > + ret = clk_prepare(data->clk_sec); > + if (ret) { > + dev_err(&pdev->dev, "Failed to get clock\n"); > + goto err_sensor; > + } > } > } > > @@ -1070,6 +1617,7 @@ static int exynos_tmu_probe(struct platform_device *pdev) > switch (data->soc) { > case SOC_ARCH_EXYNOS5433: > case SOC_ARCH_EXYNOS7: > + case SOC_ARCH_ARTPEC8: > data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk"); > if (IS_ERR(data->sclk)) { > dev_err(&pdev->dev, "Failed to get sclk\n"); > @@ -1087,24 +1635,26 @@ static int exynos_tmu_probe(struct platform_device *pdev) > break; > } > > + if (data->soc != SOC_ARCH_ARTPEC8) { > /* > * data->tzd must be registered before calling exynos_tmu_initialize(), > * requesting irq and calling exynos_tmu_control(). > */ Again wrong indentation. Best regards, Krzysztof _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
next prev parent reply other threads:[~2022-03-22 17:48 UTC|newest] Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top [not found] <20220322075638.110056-1-hypmean.kim@samsung.com> [not found] ` <CGME20220322075618epcas1p3c63582c62099312db6d40d71cfbee15b@epcms2p5> 2022-03-22 8:22 ` [PATCH 2/2] thermal: exynos: Add support for ARTPEC-8 Sang Min Kim 2022-03-22 8:22 ` Sang Min Kim 2022-03-22 17:48 ` Krzysztof Kozlowski [this message] 2022-03-22 17:48 ` Krzysztof Kozlowski 2022-03-22 17:49 ` Krzysztof Kozlowski 2022-03-22 17:49 ` Krzysztof Kozlowski
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=4ca669ea-fdf0-f4bb-7c68-47a26e72a4ce@kernel.org \ --to=krzk@kernel.org \ --cc=alim.akhtar@samsung.com \ --cc=amitk@kernel.org \ --cc=broonie@kernel.org \ --cc=bzolnier@gmail.com \ --cc=daniel.lezcano@linaro.org \ --cc=hypmean.kim@samsung.com \ --cc=lgirdwood@gmail.com \ --cc=linux-arm-kernel@lists.infradead.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux-pm@vger.kernel.org \ --cc=linux-samsung-soc@vger.kernel.org \ --cc=rafael@kernel.org \ --cc=rui.zhang@intel.com \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.