[PATCH 2/2] drivers: thermal: Introduce sun8i_thermal
qianfan
qianfanguijin at 163.com
Mon Feb 27 11:20:01 CET 2023
在 2023/2/24 19:06, Andre Przywara 写道:
> On Mon, 13 Feb 2023 10:42:56 +0800
> qianfanguijin at 163.com wrote:
>
> Hi,
>
> sorry for the delay, somehow this mail never made it into my inbox.
>
>> From: qianfan Zhao <qianfanguijin at 163.com>
>>
>> Portting sun8i_thermal.c from linux-5.15 drivers and drop interrupt
>> features. Next is an example from allwinner T3:
> So what is the use case for this driver? I see how temperature reporting is
> useful in the kernel, and even essential for things like DVFS, but what's
> the reason for having that in U-Boot?
The u-boot is boot faster than the linux kernel and I want do some hardware
test, so add this feature to u-boot. The u-boot also provide dm thermal
framework, so I portting the driver.
>
>> => temperature get thermal-sensor at 1c24c00
>> thermal-sensor at 1c24c00: 39437 C
> The documentation says that the unit is "degree Celsius". The kernel seems
> to use "millidegree of Celsius", at least internally and in sysfs, so this
> would need to be adjusted.
OK, I will change this the degress in u-boot and push later.
>
> Cheers,
> Andre
>
>> =>
>> thermal-sensor at 1c24c00: 39776 C
>> =>
>> thermal-sensor at 1c24c00: 39437 C
>>
>> Signed-off-by: qianfan Zhao <qianfanguijin at 163.com>
>> ---
>> drivers/thermal/Kconfig | 8 +
>> drivers/thermal/Makefile | 1 +
>> drivers/thermal/sun8i_thermal.c | 368 ++++++++++++++++++++++++++++++++
>> 3 files changed, 377 insertions(+)
>> create mode 100644 drivers/thermal/sun8i_thermal.c
>>
>> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
>> index 97d4163e8e..be3118175f 100644
>> --- a/drivers/thermal/Kconfig
>> +++ b/drivers/thermal/Kconfig
>> @@ -41,4 +41,12 @@ config TI_DRA7_THERMAL
>> Enable thermal support for for the Texas Instruments DRA752 SoC family.
>> The driver supports reading CPU temperature.
>>
>> +config SUN8I_THERMAL
>> + bool "Temperature sensor driver for allwinner sunxi SOCs"
>> + depends on ARCH_SUNXI
>> + select REGMAP
>> + help
>> + Enable thermal support for for the sunxi SoC family.
>> + The driver supports reading CPU temperature.
>> +
>> endif # if DM_THERMAL
>> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
>> index 8acc7d20cb..9f419962cc 100644
>> --- a/drivers/thermal/Makefile
>> +++ b/drivers/thermal/Makefile
>> @@ -9,3 +9,4 @@ obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
>> obj-$(CONFIG_IMX_SCU_THERMAL) += imx_scu_thermal.o
>> obj-$(CONFIG_TI_DRA7_THERMAL) += ti-bandgap.o
>> obj-$(CONFIG_IMX_TMU) += imx_tmu.o
>> +obj-$(CONFIG_SUN8I_THERMAL) += sun8i_thermal.o
>> diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
>> new file mode 100644
>> index 0000000000..51ea81ccf7
>> --- /dev/null
>> +++ b/drivers/thermal/sun8i_thermal.c
>> @@ -0,0 +1,368 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Thermal sensor driver for Allwinner SOC
>> + * Based on the linux driver
>> + *
>> + * Copyright (C) 2023 qianfan Zhao
>> + */
>> +#include <config.h>
>> +#include <common.h>
>> +#include <asm/global_data.h>
>> +#include <asm/io.h>
>> +#include <asm/arch/clock.h>
>> +#include <asm/arch/sys_proto.h>
>> +#include <dm.h>
>> +#include <dm/device-internal.h>
>> +#include <dm/device.h>
>> +#include <errno.h>
>> +#include <regmap.h>
>> +#include <reset.h>
>> +#include <clk.h>
>> +#include <linux/delay.h>
>> +#include <dm/device_compat.h>
>> +#include <malloc.h>
>> +#include <thermal.h>
>> +
>> +#define FT_TEMP_MASK GENMASK(11, 0)
>> +#define TEMP_CALIB_MASK GENMASK(11, 0)
>> +#define CALIBRATE_DEFAULT 0x800
>> +
>> +#define SUN8I_THS_CTRL0 0x00
>> +#define SUN8I_THS_CTRL2 0x40
>> +#define SUN8I_THS_IC 0x44
>> +#define SUN8I_THS_IS 0x48
>> +#define SUN8I_THS_MFC 0x70
>> +#define SUN8I_THS_TEMP_CALIB 0x74
>> +#define SUN8I_THS_TEMP_DATA 0x80
>> +
>> +#define SUN50I_THS_CTRL0 0x00
>> +#define SUN50I_H6_THS_ENABLE 0x04
>> +#define SUN50I_H6_THS_PC 0x08
>> +#define SUN50I_H6_THS_DIC 0x10
>> +#define SUN50I_H6_THS_DIS 0x20
>> +#define SUN50I_H6_THS_MFC 0x30
>> +#define SUN50I_H6_THS_TEMP_CALIB 0xa0
>> +#define SUN50I_H6_THS_TEMP_DATA 0xc0
>> +
>> +#define SUN8I_THS_CTRL0_T_ACQ0(x) (GENMASK(15, 0) & (x))
>> +#define SUN8I_THS_CTRL2_T_ACQ1(x) ((GENMASK(15, 0) & (x)) << 16)
>> +#define SUN8I_THS_DATA_IRQ_STS(x) BIT(x + 8)
>> +
>> +#define SUN50I_THS_CTRL0_T_ACQ(x) ((GENMASK(15, 0) & (x)) << 16)
>> +#define SUN50I_THS_FILTER_EN BIT(2)
>> +#define SUN50I_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
>> +#define SUN50I_H6_THS_PC_TEMP_PERIOD(x) ((GENMASK(19, 0) & (x)) << 12)
>> +#define SUN50I_H6_THS_DATA_IRQ_STS(x) BIT(x)
>> +
>> +struct ths_device;
>> +
>> +struct ths_thermal_chip {
>> + bool has_mod_clk;
>> + bool has_bus_clk_reset;
>> + int sensor_num;
>> + int offset;
>> + int scale;
>> + int ft_deviation;
>> + int temp_data_base;
>> + int (*init)(struct ths_device *tmdev);
>> + int (*calc_temp)(struct ths_device *tmdev,
>> + int id, int reg);
>> +};
>> +
>> +struct ths_device {
>> + const struct ths_thermal_chip *chip;
>> + struct udevice *dev;
>> + struct regmap *regmap;
>> + struct reset_ctl *reset;
>> + struct clk *bus_clk;
>> + struct clk *mod_clk;
>> +};
>> +
>> +/* Temp Unit: millidegree Celsius */
>> +static int sun8i_ths_calc_temp(struct ths_device *tmdev,
>> + int id, int reg)
>> +{
>> + return tmdev->chip->offset - (reg * tmdev->chip->scale / 10);
>> +}
>> +
>> +static int sun50i_h5_calc_temp(struct ths_device *tmdev,
>> + int id, int reg)
>> +{
>> + if (reg >= 0x500)
>> + return -1191 * reg / 10 + 223000;
>> + else if (!id)
>> + return -1452 * reg / 10 + 259000;
>> + else
>> + return -1590 * reg / 10 + 276000;
>> +}
>> +
>> +static int sun8i_ths_get_temp(struct udevice *dev, int *temp)
>> +{
>> + struct ths_device *tmdev = dev_get_plat(dev);
>> + int val = 0;
>> +
>> + regmap_read(tmdev->regmap, tmdev->chip->temp_data_base, &val);
>> +
>> + /* ths have no data yet */
>> + if (!val)
>> + return -EAGAIN;
>> +
>> + *temp = tmdev->chip->calc_temp(tmdev, 0, val);
>> + /*
>> + * According to the original sdk, there are some platforms(rarely)
>> + * that add a fixed offset value after calculating the temperature
>> + * value. We can't simply put it on the formula for calculating the
>> + * temperature above, because the formula for calculating the
>> + * temperature above is also used when the sensor is calibrated. If
>> + * do this, the correct calibration formula is hard to know.
>> + */
>> + *temp += tmdev->chip->ft_deviation;
>> +
>> + return 0;
>> +}
>> +
>> +static int sun8i_ths_resource_init(struct ths_device *tmdev)
>> +{
>> + struct udevice *dev = tmdev->dev;
>> + int ret;
>> +
>> + tmdev->regmap = devm_regmap_init(dev, NULL, NULL, NULL);
>> + if (IS_ERR(tmdev->regmap))
>> + return PTR_ERR(tmdev->regmap);
>> +
>> + if (tmdev->chip->has_bus_clk_reset) {
>> + tmdev->reset = devm_reset_control_get_by_index(dev, 0);
>> + if (IS_ERR(tmdev->reset))
>> + return PTR_ERR(tmdev->reset);
>> +
>> + tmdev->bus_clk = devm_clk_get(dev, "bus");
>> + if (IS_ERR(tmdev->bus_clk))
>> + return PTR_ERR(tmdev->bus_clk);
>> + }
>> +
>> + if (tmdev->chip->has_mod_clk) {
>> + tmdev->mod_clk = devm_clk_get(dev, "mod");
>> + if (IS_ERR(tmdev->mod_clk))
>> + return PTR_ERR(tmdev->mod_clk);
>> + }
>> +
>> + ret = reset_deassert(tmdev->reset);
>> + if (ret)
>> + return ret;
>> +
>> + ret = clk_prepare_enable(tmdev->bus_clk);
>> + if (ret)
>> + goto assert_reset;
>> +
>> + ret = clk_prepare_enable(tmdev->mod_clk);
>> + if (ret)
>> + goto bus_disable;
>> +
>> + return 0;
>> +
>> +bus_disable:
>> + clk_disable_unprepare(tmdev->bus_clk);
>> +assert_reset:
>> + reset_assert(tmdev->reset);
>> +
>> + return ret;
>> +}
>> +
>> +static int sun8i_h3_thermal_init(struct ths_device *tmdev)
>> +{
>> + int val;
>> +
>> + /* average over 4 samples */
>> + regmap_write(tmdev->regmap, SUN8I_THS_MFC,
>> + SUN50I_THS_FILTER_EN |
>> + SUN50I_THS_FILTER_TYPE(1));
>> + /*
>> + * clkin = 24MHz
>> + * filter_samples = 4
>> + * period = 0.25s
>> + *
>> + * x = period * clkin / 4096 / filter_samples - 1
>> + * = 365
>> + */
>> + val = GENMASK(7 + tmdev->chip->sensor_num, 8);
>> + regmap_write(tmdev->regmap, SUN8I_THS_IC,
>> + SUN50I_H6_THS_PC_TEMP_PERIOD(365) | val);
>> + /*
>> + * T_acq = 20us
>> + * clkin = 24MHz
>> + *
>> + * x = T_acq * clkin - 1
>> + * = 479
>> + */
>> + regmap_write(tmdev->regmap, SUN8I_THS_CTRL0,
>> + SUN8I_THS_CTRL0_T_ACQ0(479));
>> + val = GENMASK(tmdev->chip->sensor_num - 1, 0);
>> + regmap_write(tmdev->regmap, SUN8I_THS_CTRL2,
>> + SUN8I_THS_CTRL2_T_ACQ1(479) | val);
>> +
>> + return 0;
>> +}
>> +
>> +/*
>> + * Without this undocumented value, the returned temperatures would
>> + * be higher than real ones by about 20C.
>> + */
>> +#define SUN50I_H6_CTRL0_UNK 0x0000002f
>> +
>> +static int sun50i_h6_thermal_init(struct ths_device *tmdev)
>> +{
>> + int val;
>> +
>> + /*
>> + * T_acq = 20us
>> + * clkin = 24MHz
>> + *
>> + * x = T_acq * clkin - 1
>> + * = 479
>> + */
>> + regmap_write(tmdev->regmap, SUN50I_THS_CTRL0,
>> + SUN50I_H6_CTRL0_UNK | SUN50I_THS_CTRL0_T_ACQ(479));
>> + /* average over 4 samples */
>> + regmap_write(tmdev->regmap, SUN50I_H6_THS_MFC,
>> + SUN50I_THS_FILTER_EN |
>> + SUN50I_THS_FILTER_TYPE(1));
>> + /*
>> + * clkin = 24MHz
>> + * filter_samples = 4
>> + * period = 0.25s
>> + *
>> + * x = period * clkin / 4096 / filter_samples - 1
>> + * = 365
>> + */
>> + regmap_write(tmdev->regmap, SUN50I_H6_THS_PC,
>> + SUN50I_H6_THS_PC_TEMP_PERIOD(365));
>> + /* enable sensor */
>> + val = GENMASK(tmdev->chip->sensor_num - 1, 0);
>> + regmap_write(tmdev->regmap, SUN50I_H6_THS_ENABLE, val);
>> + /* thermal data interrupt enable */
>> + val = GENMASK(tmdev->chip->sensor_num - 1, 0);
>> + regmap_write(tmdev->regmap, SUN50I_H6_THS_DIC, val);
>> +
>> + return 0;
>> +}
>> +
>> +static int sun8i_ths_probe(struct udevice *udev)
>> +{
>> + struct ths_device *tmdev = dev_get_plat(udev);
>> + int ret;
>> +
>> + tmdev->dev = udev;
>> +
>> + tmdev->chip =
>> + (const struct ths_thermal_chip *)dev_get_driver_data(udev);
>> + if (!tmdev->chip)
>> + return -EINVAL;
>> +
>> + ret = sun8i_ths_resource_init(tmdev);
>> + if (ret)
>> + return ret;
>> +
>> + ret = tmdev->chip->init(tmdev);
>> + if (ret)
>> + return ret;
>> +
>> + return 0;
>> +}
>> +
>> +static const struct dm_thermal_ops sun8i_ths_ops = {
>> + .get_temp = sun8i_ths_get_temp,
>> +};
>> +
>> +static const struct ths_thermal_chip sun8i_a83t_ths = {
>> + .sensor_num = 3,
>> + .scale = 705,
>> + .offset = 191668,
>> + .temp_data_base = SUN8I_THS_TEMP_DATA,
>> + .init = sun8i_h3_thermal_init,
>> + .calc_temp = sun8i_ths_calc_temp,
>> +};
>> +
>> +static const struct ths_thermal_chip sun8i_h3_ths = {
>> + .sensor_num = 1,
>> + .scale = 1211,
>> + .offset = 217000,
>> + .has_mod_clk = true,
>> + .has_bus_clk_reset = true,
>> + .temp_data_base = SUN8I_THS_TEMP_DATA,
>> + .init = sun8i_h3_thermal_init,
>> + .calc_temp = sun8i_ths_calc_temp,
>> +};
>> +
>> +static const struct ths_thermal_chip sun8i_r40_ths = {
>> + .sensor_num = 2,
>> + .offset = 251086,
>> + .scale = 1130,
>> + .has_mod_clk = true,
>> + .has_bus_clk_reset = true,
>> + .temp_data_base = SUN8I_THS_TEMP_DATA,
>> + .init = sun8i_h3_thermal_init,
>> + .calc_temp = sun8i_ths_calc_temp,
>> +};
>> +
>> +static const struct ths_thermal_chip sun50i_a64_ths = {
>> + .sensor_num = 3,
>> + .offset = 260890,
>> + .scale = 1170,
>> + .has_mod_clk = true,
>> + .has_bus_clk_reset = true,
>> + .temp_data_base = SUN8I_THS_TEMP_DATA,
>> + .init = sun8i_h3_thermal_init,
>> + .calc_temp = sun8i_ths_calc_temp,
>> +};
>> +
>> +static const struct ths_thermal_chip sun50i_a100_ths = {
>> + .sensor_num = 3,
>> + .has_bus_clk_reset = true,
>> + .ft_deviation = 8000,
>> + .offset = 187744,
>> + .scale = 672,
>> + .temp_data_base = SUN50I_H6_THS_TEMP_DATA,
>> + .init = sun50i_h6_thermal_init,
>> + .calc_temp = sun8i_ths_calc_temp,
>> +};
>> +
>> +static const struct ths_thermal_chip sun50i_h5_ths = {
>> + .sensor_num = 2,
>> + .has_mod_clk = true,
>> + .has_bus_clk_reset = true,
>> + .temp_data_base = SUN8I_THS_TEMP_DATA,
>> + .init = sun8i_h3_thermal_init,
>> + .calc_temp = sun50i_h5_calc_temp,
>> +};
>> +
>> +static const struct ths_thermal_chip sun50i_h6_ths = {
>> + .sensor_num = 2,
>> + .has_bus_clk_reset = true,
>> + .ft_deviation = 7000,
>> + .offset = 187744,
>> + .scale = 672,
>> + .temp_data_base = SUN50I_H6_THS_TEMP_DATA,
>> + .init = sun50i_h6_thermal_init,
>> + .calc_temp = sun8i_ths_calc_temp,
>> +};
>> +
>> +static const struct udevice_id of_ths_match[] = {
>> + { .compatible = "allwinner,sun8i-a83t-ths", .data = (ulong)&sun8i_a83t_ths },
>> + { .compatible = "allwinner,sun8i-h3-ths", .data = (ulong)&sun8i_h3_ths },
>> + { .compatible = "allwinner,sun8i-r40-ths", .data = (ulong)&sun8i_r40_ths },
>> + { .compatible = "allwinner,sun50i-a64-ths", .data = (ulong)&sun50i_a64_ths },
>> + { .compatible = "allwinner,sun50i-a100-ths", .data = (ulong)&sun50i_a100_ths },
>> + { .compatible = "allwinner,sun50i-h5-ths", .data = (ulong)&sun50i_h5_ths },
>> + { .compatible = "allwinner,sun50i-h6-ths", .data = (ulong)&sun50i_h6_ths },
>> + { /* sentinel */ },
>> +};
>> +
>> +U_BOOT_DRIVER(sun8i_thermal) = {
>> + .name = "sun8i_thermal",
>> + .id = UCLASS_THERMAL,
>> + .ops = &sun8i_ths_ops,
>> + .of_match = of_ths_match,
>> + .probe = sun8i_ths_probe,
>> + .plat_auto = sizeof(struct ths_device),
>> + .flags = DM_FLAG_PRE_RELOC,
>> +};
More information about the U-Boot
mailing list