[PATCH 2/2] drivers: thermal: Introduce sun8i_thermal

qianfanguijin at 163.com qianfanguijin at 163.com
Mon Feb 13 03:42:56 CET 2023


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:

=> temperature get thermal-sensor at 1c24c00
thermal-sensor at 1c24c00: 39437 C
=>
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,
+};
-- 
2.17.1



More information about the U-Boot mailing list