All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] clk: sunxi: add THS clk/reset
@ 2023-02-13  2:42 qianfanguijin
  2023-02-13  2:42 ` [PATCH 2/2] drivers: thermal: Introduce sun8i_thermal qianfanguijin
  2023-02-25  0:50 ` [PATCH 1/2] clk: sunxi: add THS clk/reset Andre Przywara
  0 siblings, 2 replies; 5+ messages in thread
From: qianfanguijin @ 2023-02-13  2:42 UTC (permalink / raw
  To: u-boot; +Cc: Andre Przywara, Jagan Teki, Simon Glass, qianfan Zhao

From: qianfan Zhao <qianfanguijin@163.com>

Add clock/reset definitions for THS peripherals.

Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
---
 drivers/clk/sunxi/clk_h3.c  | 5 +++++
 drivers/clk/sunxi/clk_h6.c  | 4 ++++
 drivers/clk/sunxi/clk_r40.c | 5 +++++
 3 files changed, 14 insertions(+)

diff --git a/drivers/clk/sunxi/clk_h3.c b/drivers/clk/sunxi/clk_h3.c
index 213ab510ed..774d67cbfa 100644
--- a/drivers/clk/sunxi/clk_h3.c
+++ b/drivers/clk/sunxi/clk_h3.c
@@ -38,6 +38,7 @@ static struct ccu_clk_gate h3_gates[] = {
 	[CLK_BUS_DE]		= GATE(0x064, BIT(12)),
 
 	[CLK_BUS_PIO]		= GATE(0x068, BIT(5)),
+	[CLK_BUS_THS]		= GATE(0x068, BIT(8)),
 
 	[CLK_BUS_I2C0]		= GATE(0x06c, BIT(0)),
 	[CLK_BUS_I2C1]		= GATE(0x06c, BIT(1)),
@@ -49,6 +50,8 @@ static struct ccu_clk_gate h3_gates[] = {
 
 	[CLK_BUS_EPHY]		= GATE(0x070, BIT(0)),
 
+	[CLK_THS]		= GATE(0x074, BIT(31)),
+
 	[CLK_SPI0]		= GATE(0x0a0, BIT(31)),
 	[CLK_SPI1]		= GATE(0x0a4, BIT(31)),
 
@@ -98,6 +101,8 @@ static struct ccu_reset h3_resets[] = {
 
 	[RST_BUS_EPHY]		= RESET(0x2c8, BIT(2)),
 
+	[RST_BUS_THS]		= RESET(0x2d0, BIT(8)),
+
 	[RST_BUS_I2C0]		= RESET(0x2d8, BIT(0)),
 	[RST_BUS_I2C1]		= RESET(0x2d8, BIT(1)),
 	[RST_BUS_I2C2]		= RESET(0x2d8, BIT(2)),
diff --git a/drivers/clk/sunxi/clk_h6.c b/drivers/clk/sunxi/clk_h6.c
index 24eb9725db..5464530056 100644
--- a/drivers/clk/sunxi/clk_h6.c
+++ b/drivers/clk/sunxi/clk_h6.c
@@ -42,6 +42,8 @@ static struct ccu_clk_gate h6_gates[] = {
 
 	[CLK_BUS_EMAC]		= GATE(0x97c, BIT(0)),
 
+	[CLK_BUS_THS]		= GATE(0x9fc, BIT(0)),
+
 	[CLK_USB_PHY0]		= GATE(0xa70, BIT(29)),
 	[CLK_USB_OHCI0]		= GATE(0xa70, BIT(31)),
 
@@ -91,6 +93,8 @@ static struct ccu_reset h6_resets[] = {
 
 	[RST_BUS_EMAC]		= RESET(0x97c, BIT(16)),
 
+	[RST_BUS_THS]		= RESET(0x9fc, BIT(16)),
+
 	[RST_USB_PHY0]		= RESET(0xa70, BIT(30)),
 
 	[RST_USB_PHY1]		= RESET(0xa74, BIT(30)),
diff --git a/drivers/clk/sunxi/clk_r40.c b/drivers/clk/sunxi/clk_r40.c
index 630e80d2b4..4fbfd28207 100644
--- a/drivers/clk/sunxi/clk_r40.c
+++ b/drivers/clk/sunxi/clk_r40.c
@@ -42,6 +42,7 @@ static struct ccu_clk_gate r40_gates[] = {
 	[CLK_BUS_TCON_TOP]	= GATE(0x064, BIT(30)),
 
 	[CLK_BUS_PIO]		= GATE(0x068, BIT(5)),
+	[CLK_BUS_THS]		= GATE(0x068, BIT(8)),
 
 	[CLK_BUS_I2C0]		= GATE(0x06c, BIT(0)),
 	[CLK_BUS_I2C1]		= GATE(0x06c, BIT(1)),
@@ -57,6 +58,8 @@ static struct ccu_clk_gate r40_gates[] = {
 	[CLK_BUS_UART6]		= GATE(0x06c, BIT(22)),
 	[CLK_BUS_UART7]		= GATE(0x06c, BIT(23)),
 
+	[CLK_THS]		= GATE(0x074, BIT(31)),
+
 	[CLK_SPI0]		= GATE(0x0a0, BIT(31)),
 	[CLK_SPI1]		= GATE(0x0a4, BIT(31)),
 	[CLK_SPI2]		= GATE(0x0a8, BIT(31)),
@@ -113,6 +116,8 @@ static struct ccu_reset r40_resets[] = {
 	[RST_BUS_TCON_TV1]	= RESET(0x2c4, BIT(29)),
 	[RST_BUS_TCON_TOP]	= RESET(0x2c4, BIT(30)),
 
+	[RST_BUS_THS]		= RESET(0x2d0, BIT(8)),
+
 	[RST_BUS_I2C0]		= RESET(0x2d8, BIT(0)),
 	[RST_BUS_I2C1]		= RESET(0x2d8, BIT(1)),
 	[RST_BUS_I2C2]		= RESET(0x2d8, BIT(2)),
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] drivers: thermal: Introduce sun8i_thermal
  2023-02-13  2:42 [PATCH 1/2] clk: sunxi: add THS clk/reset qianfanguijin
@ 2023-02-13  2:42 ` qianfanguijin
  2023-02-24 11:06   ` Andre Przywara
  2023-02-25  0:50 ` [PATCH 1/2] clk: sunxi: add THS clk/reset Andre Przywara
  1 sibling, 1 reply; 5+ messages in thread
From: qianfanguijin @ 2023-02-13  2:42 UTC (permalink / raw
  To: u-boot; +Cc: Andre Przywara, Jagan Teki, Simon Glass, qianfan Zhao

From: qianfan Zhao <qianfanguijin@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@1c24c00
thermal-sensor@1c24c00: 39437 C
=>
thermal-sensor@1c24c00: 39776 C
=>
thermal-sensor@1c24c00: 39437 C

Signed-off-by: qianfan Zhao <qianfanguijin@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


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] drivers: thermal: Introduce sun8i_thermal
  2023-02-13  2:42 ` [PATCH 2/2] drivers: thermal: Introduce sun8i_thermal qianfanguijin
@ 2023-02-24 11:06   ` Andre Przywara
  2023-02-27 10:20     ` qianfan
  0 siblings, 1 reply; 5+ messages in thread
From: Andre Przywara @ 2023-02-24 11:06 UTC (permalink / raw
  To: qianfanguijin; +Cc: u-boot, Jagan Teki, Simon Glass

On Mon, 13 Feb 2023 10:42:56 +0800
qianfanguijin@163.com wrote:

Hi,

sorry for the delay, somehow this mail never made it into my inbox.

> From: qianfan Zhao <qianfanguijin@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?

> => temperature get thermal-sensor@1c24c00  
> thermal-sensor@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.

Cheers,
Andre

> =>  
> thermal-sensor@1c24c00: 39776 C
> =>  
> thermal-sensor@1c24c00: 39437 C
> 
> Signed-off-by: qianfan Zhao <qianfanguijin@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,
> +};


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] clk: sunxi: add THS clk/reset
  2023-02-13  2:42 [PATCH 1/2] clk: sunxi: add THS clk/reset qianfanguijin
  2023-02-13  2:42 ` [PATCH 2/2] drivers: thermal: Introduce sun8i_thermal qianfanguijin
@ 2023-02-25  0:50 ` Andre Przywara
  1 sibling, 0 replies; 5+ messages in thread
From: Andre Przywara @ 2023-02-25  0:50 UTC (permalink / raw
  To: qianfanguijin; +Cc: u-boot, Jagan Teki, Simon Glass

On Mon, 13 Feb 2023 10:42:55 +0800
qianfanguijin@163.com wrote:

Hi,

> From: qianfan Zhao <qianfanguijin@163.com>
> 
> Add clock/reset definitions for THS peripherals.

the bits look alright, compared against the manual. But it misses the
A64 clocks, the THS driver would cover that SoC, too.

Cheers,
Andre

> Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
> ---
>  drivers/clk/sunxi/clk_h3.c  | 5 +++++
>  drivers/clk/sunxi/clk_h6.c  | 4 ++++
>  drivers/clk/sunxi/clk_r40.c | 5 +++++
>  3 files changed, 14 insertions(+)
> 
> diff --git a/drivers/clk/sunxi/clk_h3.c b/drivers/clk/sunxi/clk_h3.c
> index 213ab510ed..774d67cbfa 100644
> --- a/drivers/clk/sunxi/clk_h3.c
> +++ b/drivers/clk/sunxi/clk_h3.c
> @@ -38,6 +38,7 @@ static struct ccu_clk_gate h3_gates[] = {
>  	[CLK_BUS_DE]		= GATE(0x064, BIT(12)),
>  
>  	[CLK_BUS_PIO]		= GATE(0x068, BIT(5)),
> +	[CLK_BUS_THS]		= GATE(0x068, BIT(8)),
>  
>  	[CLK_BUS_I2C0]		= GATE(0x06c, BIT(0)),
>  	[CLK_BUS_I2C1]		= GATE(0x06c, BIT(1)),
> @@ -49,6 +50,8 @@ static struct ccu_clk_gate h3_gates[] = {
>  
>  	[CLK_BUS_EPHY]		= GATE(0x070, BIT(0)),
>  
> +	[CLK_THS]		= GATE(0x074, BIT(31)),
> +
>  	[CLK_SPI0]		= GATE(0x0a0, BIT(31)),
>  	[CLK_SPI1]		= GATE(0x0a4, BIT(31)),
>  
> @@ -98,6 +101,8 @@ static struct ccu_reset h3_resets[] = {
>  
>  	[RST_BUS_EPHY]		= RESET(0x2c8, BIT(2)),
>  
> +	[RST_BUS_THS]		= RESET(0x2d0, BIT(8)),
> +
>  	[RST_BUS_I2C0]		= RESET(0x2d8, BIT(0)),
>  	[RST_BUS_I2C1]		= RESET(0x2d8, BIT(1)),
>  	[RST_BUS_I2C2]		= RESET(0x2d8, BIT(2)),
> diff --git a/drivers/clk/sunxi/clk_h6.c b/drivers/clk/sunxi/clk_h6.c
> index 24eb9725db..5464530056 100644
> --- a/drivers/clk/sunxi/clk_h6.c
> +++ b/drivers/clk/sunxi/clk_h6.c
> @@ -42,6 +42,8 @@ static struct ccu_clk_gate h6_gates[] = {
>  
>  	[CLK_BUS_EMAC]		= GATE(0x97c, BIT(0)),
>  
> +	[CLK_BUS_THS]		= GATE(0x9fc, BIT(0)),
> +
>  	[CLK_USB_PHY0]		= GATE(0xa70, BIT(29)),
>  	[CLK_USB_OHCI0]		= GATE(0xa70, BIT(31)),
>  
> @@ -91,6 +93,8 @@ static struct ccu_reset h6_resets[] = {
>  
>  	[RST_BUS_EMAC]		= RESET(0x97c, BIT(16)),
>  
> +	[RST_BUS_THS]		= RESET(0x9fc, BIT(16)),
> +
>  	[RST_USB_PHY0]		= RESET(0xa70, BIT(30)),
>  
>  	[RST_USB_PHY1]		= RESET(0xa74, BIT(30)),
> diff --git a/drivers/clk/sunxi/clk_r40.c b/drivers/clk/sunxi/clk_r40.c
> index 630e80d2b4..4fbfd28207 100644
> --- a/drivers/clk/sunxi/clk_r40.c
> +++ b/drivers/clk/sunxi/clk_r40.c
> @@ -42,6 +42,7 @@ static struct ccu_clk_gate r40_gates[] = {
>  	[CLK_BUS_TCON_TOP]	= GATE(0x064, BIT(30)),
>  
>  	[CLK_BUS_PIO]		= GATE(0x068, BIT(5)),
> +	[CLK_BUS_THS]		= GATE(0x068, BIT(8)),
>  
>  	[CLK_BUS_I2C0]		= GATE(0x06c, BIT(0)),
>  	[CLK_BUS_I2C1]		= GATE(0x06c, BIT(1)),
> @@ -57,6 +58,8 @@ static struct ccu_clk_gate r40_gates[] = {
>  	[CLK_BUS_UART6]		= GATE(0x06c, BIT(22)),
>  	[CLK_BUS_UART7]		= GATE(0x06c, BIT(23)),
>  
> +	[CLK_THS]		= GATE(0x074, BIT(31)),
> +
>  	[CLK_SPI0]		= GATE(0x0a0, BIT(31)),
>  	[CLK_SPI1]		= GATE(0x0a4, BIT(31)),
>  	[CLK_SPI2]		= GATE(0x0a8, BIT(31)),
> @@ -113,6 +116,8 @@ static struct ccu_reset r40_resets[] = {
>  	[RST_BUS_TCON_TV1]	= RESET(0x2c4, BIT(29)),
>  	[RST_BUS_TCON_TOP]	= RESET(0x2c4, BIT(30)),
>  
> +	[RST_BUS_THS]		= RESET(0x2d0, BIT(8)),
> +
>  	[RST_BUS_I2C0]		= RESET(0x2d8, BIT(0)),
>  	[RST_BUS_I2C1]		= RESET(0x2d8, BIT(1)),
>  	[RST_BUS_I2C2]		= RESET(0x2d8, BIT(2)),


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] drivers: thermal: Introduce sun8i_thermal
  2023-02-24 11:06   ` Andre Przywara
@ 2023-02-27 10:20     ` qianfan
  0 siblings, 0 replies; 5+ messages in thread
From: qianfan @ 2023-02-27 10:20 UTC (permalink / raw
  To: Andre Przywara; +Cc: u-boot, Jagan Teki, Simon Glass



在 2023/2/24 19:06, Andre Przywara 写道:
> On Mon, 13 Feb 2023 10:42:56 +0800
> qianfanguijin@163.com wrote:
>
> Hi,
>
> sorry for the delay, somehow this mail never made it into my inbox.
>
>> From: qianfan Zhao <qianfanguijin@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@1c24c00
>> thermal-sensor@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@1c24c00: 39776 C
>> =>
>> thermal-sensor@1c24c00: 39437 C
>>
>> Signed-off-by: qianfan Zhao <qianfanguijin@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,
>> +};


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2023-02-27 13:54 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-02-13  2:42 [PATCH 1/2] clk: sunxi: add THS clk/reset qianfanguijin
2023-02-13  2:42 ` [PATCH 2/2] drivers: thermal: Introduce sun8i_thermal qianfanguijin
2023-02-24 11:06   ` Andre Przywara
2023-02-27 10:20     ` qianfan
2023-02-25  0:50 ` [PATCH 1/2] clk: sunxi: add THS clk/reset Andre Przywara

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.