[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <4ca669ea-fdf0-f4bb-7c68-47a26e72a4ce@kernel.org>
Date: Tue, 22 Mar 2022 18:48:12 +0100
From: Krzysztof Kozlowski <krzk@...nel.org>
To: hypmean.kim@...sung.com, "bzolnier@...il.com" <bzolnier@...il.com>,
"rafael@...nel.org" <rafael@...nel.org>,
"daniel.lezcano@...aro.org" <daniel.lezcano@...aro.org>,
"amitk@...nel.org" <amitk@...nel.org>,
"rui.zhang@...el.com" <rui.zhang@...el.com>,
ALIM AKHTAR <alim.akhtar@...sung.com>,
"lgirdwood@...il.com" <lgirdwood@...il.com>,
"broonie@...nel.org" <broonie@...nel.org>
Cc: "linux-pm@...r.kernel.org" <linux-pm@...r.kernel.org>,
"linux-samsung-soc@...r.kernel.org"
<linux-samsung-soc@...r.kernel.org>,
"linux-arm-kernel@...ts.infradead.org"
<linux-arm-kernel@...ts.infradead.org>,
"linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH 2/2] thermal: exynos: Add support for ARTPEC-8
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@...sung.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
Powered by blists - more mailing lists