[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