Linux-PM Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v6 0/7] add thermal sensor driver for A64, A83T, H3, H5, H6, R40
@ 2019-11-27  5:29 Vasily Khoruzhick
  2019-11-27  5:29 ` [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40 Vasily Khoruzhick
                   ` (6 more replies)
  0 siblings, 7 replies; 31+ messages in thread
From: Vasily Khoruzhick @ 2019-11-27  5:29 UTC (permalink / raw)
  To: Yangtao Li, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Maxime Ripard,
	Chen-Yu Tsai, Mauro Carvalho Chehab, David S. Miller,
	Greg Kroah-Hartman, linux-pm, devicetree, linux-arm-kernel,
	Ondřej Jirman, linux-kernel
  Cc: Vasily Khoruzhick

This patchset adds driver for thermal sensor in A64, A83T, H3, H5,
H6 and R40 SoCs.

v6:
	- [ondrej, vasily] Squash all driver related changes into a
			   single patch
	- [ondrej] Rename calib -> calibration
	- [ondrej] Fix thermal zone registration check
	- [ondrej] Lower rate of sensor data interrupts to 4/sec/sensor
	- [ondrej] Rework scale/offset values, H6 calibration
	- [ondrej] Explicitly set mod clock to 24 MHz
	- [ondrej] Set undocumented bits in CTRL0 for H6
	- [ondrej] Add support for A83T
	- [ondrej] Add dts changes for A83T, H3, H5, H6
	- [vasily] Add dts changes for A64
	- [vasily] Address Maxime's comments for YAML scheme
	- [vasily] Make .calc_temp callback mandatory
	- [vasily] Set .max_register in regmap config, so regs can be
		   inspected using debugfs

Ondrej Jirman (4):
  ARM: dts: sun8i-a83t: Add thermal sensor and thermal zones
  ARM: dts: sun8i-h3: Add thermal sensor and thermal zones
  arm64: dts: allwinner: sun50i-h5: Add thermal sensor and thermal zones
  arm64: dts: allwinner: sun50i-h6: Add thermal sensor and thermal zones

Vasily Khoruzhick (1):
  arm64: dts: allwinner: a64: Add thermal sensors and thermal zones

Yangtao Li (2):
  thermal: sun8i: add thermal driver for H6/H5/H3/A64/R40
  dt-bindings: thermal: add YAML schema for sun8i-thermal driver
    bindings

 .../thermal/allwinner,sun8i-a83t-ths.yaml     | 103 +++
 MAINTAINERS                                   |   7 +
 arch/arm/boot/dts/sun8i-a83t.dtsi             |  36 +
 arch/arm/boot/dts/sun8i-h3.dtsi               |  26 +
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi |  42 ++
 arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi  |  32 +
 arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi  |  33 +
 drivers/thermal/Kconfig                       |  14 +
 drivers/thermal/Makefile                      |   1 +
 drivers/thermal/sun8i_thermal.c               | 643 ++++++++++++++++++
 10 files changed, 937 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
 create mode 100644 drivers/thermal/sun8i_thermal.c

-- 
2.24.0


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

* [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-11-27  5:29 [PATCH v6 0/7] add thermal sensor driver for A64, A83T, H3, H5, H6, R40 Vasily Khoruzhick
@ 2019-11-27  5:29 ` Vasily Khoruzhick
  2019-11-27 11:14   ` Ondřej Jirman
                     ` (2 more replies)
  2019-11-27  5:29 ` [PATCH v6 2/7] dt-bindings: thermal: add YAML schema for sun8i-thermal driver bindings Vasily Khoruzhick
                   ` (5 subsequent siblings)
  6 siblings, 3 replies; 31+ messages in thread
From: Vasily Khoruzhick @ 2019-11-27  5:29 UTC (permalink / raw)
  To: Yangtao Li, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Maxime Ripard,
	Chen-Yu Tsai, Mauro Carvalho Chehab, David S. Miller,
	Greg Kroah-Hartman, linux-pm, devicetree, linux-arm-kernel,
	Ondřej Jirman, linux-kernel
  Cc: Vasily Khoruzhick

From: Yangtao Li <tiny.windzz@gmail.com>

This patch adds the support for allwinner thermal sensor, within
allwinner SoC. It will register sensors for thermal framework
and use device tree to bind cooling device.

Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
Signed-off-by: Ondrej Jirman <megous@megous.com>
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
 MAINTAINERS                     |   7 +
 drivers/thermal/Kconfig         |  14 +
 drivers/thermal/Makefile        |   1 +
 drivers/thermal/sun8i_thermal.c | 643 ++++++++++++++++++++++++++++++++
 4 files changed, 665 insertions(+)
 create mode 100644 drivers/thermal/sun8i_thermal.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 66cc549ac327..da34f3f2e80b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -688,6 +688,13 @@ L:	linux-crypto@vger.kernel.org
 S:	Maintained
 F:	drivers/crypto/allwinner/
 
+ALLWINNER THERMAL DRIVER
+M:	Yangtao Li <tiny.windzz@gmail.com>
+L:	linux-pm@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
+F:	drivers/thermal/sun8i_thermal.c
+
 ALLWINNER VPU DRIVER
 M:	Maxime Ripard <mripard@kernel.org>
 M:	Paul Kocialkowski <paul.kocialkowski@bootlin.com>
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 001a21abcc28..0b0422e89adb 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -262,6 +262,20 @@ config SPEAR_THERMAL
 	  Enable this to plug the SPEAr thermal sensor driver into the Linux
 	  thermal framework.
 
+config SUN8I_THERMAL
+	tristate "Allwinner sun8i thermal driver"
+	depends on ARCH_SUNXI || COMPILE_TEST
+	depends on HAS_IOMEM
+	depends on NVMEM
+	depends on OF
+	depends on RESET_CONTROLLER
+	help
+	  Support for the sun8i thermal sensor driver into the Linux thermal
+	  framework.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sun8i-thermal.
+
 config ROCKCHIP_THERMAL
 	tristate "Rockchip thermal driver"
 	depends on ARCH_ROCKCHIP || COMPILE_TEST
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 74a37c7f847a..fa6f8b206281 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -31,6 +31,7 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
 obj-y				+= broadcom/
 obj-$(CONFIG_THERMAL_MMIO)		+= thermal_mmio.o
 obj-$(CONFIG_SPEAR_THERMAL)	+= spear_thermal.o
+obj-$(CONFIG_SUN8I_THERMAL)     += sun8i_thermal.o
 obj-$(CONFIG_ROCKCHIP_THERMAL)	+= rockchip_thermal.o
 obj-$(CONFIG_RCAR_THERMAL)	+= rcar_thermal.o
 obj-$(CONFIG_RCAR_GEN3_THERMAL)	+= rcar_gen3_thermal.o
diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
new file mode 100644
index 000000000000..e86b64f51196
--- /dev/null
+++ b/drivers/thermal/sun8i_thermal.c
@@ -0,0 +1,643 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Thermal sensor driver for Allwinner SOC
+ * Copyright (C) 2019 Yangtao Li
+ *
+ * Based on the work of Icenowy Zheng <icenowy@aosc.io>
+ * Based on the work of Ondrej Jirman <megous@megous.com>
+ * Based on the work of Josef Gajdusek <atx@atx.name>
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+#define MAX_SENSOR_NUM	4
+
+#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)
+
+/* millidegree celsius */
+#define THS_EFUSE_CP_FT_MASK			0x3000
+#define THS_EFUSE_CP_FT_BIT			12
+#define THS_CALIBRATION_IN_FT			1
+
+struct ths_device;
+
+struct tsensor {
+	struct ths_device		*tmdev;
+	struct thermal_zone_device	*tzd;
+	int				id;
+};
+
+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		(*calibrate)(struct ths_device *tmdev,
+				     u16 *caldata, int callen);
+	int		(*init)(struct ths_device *tmdev);
+	int             (*irq_ack)(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 device				*dev;
+	struct regmap				*regmap;
+	struct reset_control			*reset;
+	struct clk				*bus_clk;
+	struct clk                              *mod_clk;
+	struct tsensor				sensor[MAX_SENSOR_NUM];
+	u32					cp_ft_flag;
+};
+
+/* 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(void *data, int *temp)
+{
+	struct tsensor *s = data;
+	struct ths_device *tmdev = s->tmdev;
+	int val = 0;
+
+	regmap_read(tmdev->regmap, tmdev->chip->temp_data_base +
+		    0x4 * s->id, &val);
+
+	/* ths have no data yet */
+	if (!val)
+		return -EAGAIN;
+
+	*temp = tmdev->chip->calc_temp(tmdev, s->id, 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 const struct thermal_zone_of_device_ops ths_ops = {
+	.get_temp = sun8i_ths_get_temp,
+};
+
+static const struct regmap_config config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.fast_io = true,
+	.max_register = 0xfc,
+};
+
+static int sun8i_h3_irq_ack(struct ths_device *tmdev)
+{
+	int i, state, ret = 0;
+
+	regmap_read(tmdev->regmap, SUN8I_THS_IS, &state);
+
+	for (i = 0; i < tmdev->chip->sensor_num; i++) {
+		if (state & SUN8I_THS_DATA_IRQ_STS(i)) {
+			regmap_write(tmdev->regmap, SUN8I_THS_IS,
+				     SUN8I_THS_DATA_IRQ_STS(i));
+			ret |= BIT(i);
+		}
+	}
+
+	return ret;
+}
+
+static int sun50i_h6_irq_ack(struct ths_device *tmdev)
+{
+	int i, state, ret = 0;
+
+	regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state);
+
+	for (i = 0; i < tmdev->chip->sensor_num; i++) {
+		if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
+			regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS,
+				     SUN50I_H6_THS_DATA_IRQ_STS(i));
+			ret |= BIT(i);
+		}
+	}
+
+	return ret;
+}
+
+static irqreturn_t sun8i_irq_thread(int irq, void *data)
+{
+	struct ths_device *tmdev = data;
+	int i, state;
+
+	state = tmdev->chip->irq_ack(tmdev);
+
+	for (i = 0; i < tmdev->chip->sensor_num; i++) {
+		if (state & BIT(i))
+			thermal_zone_device_update(tmdev->sensor[i].tzd,
+						   THERMAL_EVENT_UNSPECIFIED);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int sun8i_h3_ths_calibrate(struct ths_device *tmdev,
+				  u16 *caldata, int callen)
+{
+	int i;
+
+	if (!caldata[0] || callen < 2 * tmdev->chip->sensor_num)
+		return -EINVAL;
+
+	for (i = 0; i < tmdev->chip->sensor_num; i++) {
+		int offset = (i % 2) << 4;
+
+		regmap_update_bits(tmdev->regmap,
+				   SUN8I_THS_TEMP_CALIB + (4 * (i >> 1)),
+				   0xfff << offset,
+				   caldata[i] << offset);
+	}
+
+	return 0;
+}
+
+static int sun50i_h6_ths_calibrate(struct ths_device *tmdev,
+				   u16 *caldata, int callen)
+{
+	struct device *dev = tmdev->dev;
+	int i, ft_temp;
+
+	if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num)
+		return -EINVAL;
+
+	/*
+	 * efuse layout:
+	 *
+	 *	0   11  16	 32
+	 *	+-------+-------+-------+
+	 *	|temp|  |sensor0|sensor1|
+	 *	+-------+-------+-------+
+	 *
+	 * The calibration data on the H6 is the ambient temperature and
+	 * sensor values that are filled during the factory test stage.
+	 *
+	 * The unit of stored FT temperature is 0.1 degreee celusis.
+	 *
+	 * We need to calculate a delta between measured and caluclated
+	 * register values and this will become a calibration offset.
+	 */
+	ft_temp = (caldata[0] & FT_TEMP_MASK) * 100;
+	tmdev->cp_ft_flag = (caldata[0] & THS_EFUSE_CP_FT_MASK)
+		>> THS_EFUSE_CP_FT_BIT;
+
+	for (i = 0; i < tmdev->chip->sensor_num; i++) {
+		int sensor_reg = caldata[i + 1];
+		int cdata, offset;
+		int sensor_temp = tmdev->chip->calc_temp(tmdev, i, sensor_reg);
+
+		/*
+		 * Calibration data is CALIBRATE_DEFAULT - (calculated
+		 * temperature from sensor reading at factory temperature
+		 * minus actual factory temperature) * 14.88 (scale from
+		 * temperature to register values)
+		 */
+		cdata = CALIBRATE_DEFAULT -
+			((sensor_temp - ft_temp) * 10 / tmdev->chip->scale);
+		if (cdata & ~TEMP_CALIB_MASK) {
+			/*
+			 * Calibration value more than 12-bit, but calibration
+			 * register is 12-bit. In this case, ths hardware can
+			 * still work without calibration, although the data
+			 * won't be so accurate.
+			 */
+			dev_warn(dev, "sensor%d is not calibrated.\n", i);
+			continue;
+		}
+
+		offset = (i % 2) * 16;
+		regmap_update_bits(tmdev->regmap,
+				   SUN50I_H6_THS_TEMP_CALIB + (i / 2 * 4),
+				   0xfff << offset,
+				   cdata << offset);
+	}
+
+	return 0;
+}
+
+static int sun8i_ths_calibrate(struct ths_device *tmdev)
+{
+	struct nvmem_cell *calcell;
+	struct device *dev = tmdev->dev;
+	u16 *caldata;
+	size_t callen;
+	int ret = 0;
+
+	calcell = devm_nvmem_cell_get(dev, "calibration");
+	if (IS_ERR(calcell)) {
+		if (PTR_ERR(calcell) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		/*
+		 * Even if the external calibration data stored in sid is
+		 * not accessible, the THS hardware can still work, although
+		 * the data won't be so accurate.
+		 *
+		 * The default value of calibration register is 0x800 for
+		 * every sensor, and the calibration value is usually 0x7xx
+		 * or 0x8xx, so they won't be away from the default value
+		 * for a lot.
+		 *
+		 * So here we do not return error if the calibartion data is
+		 * not available, except the probe needs deferring.
+		 */
+		goto out;
+	}
+
+	caldata = nvmem_cell_read(calcell, &callen);
+	if (IS_ERR(caldata)) {
+		ret = PTR_ERR(caldata);
+		goto out;
+	}
+
+	tmdev->chip->calibrate(tmdev, caldata, callen);
+
+	kfree(caldata);
+out:
+	return ret;
+}
+
+static int sun8i_ths_resource_init(struct ths_device *tmdev)
+{
+	struct device *dev = tmdev->dev;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *mem;
+	void __iomem *base;
+	int ret;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(dev, mem);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	tmdev->regmap = devm_regmap_init_mmio(dev, base, &config);
+	if (IS_ERR(tmdev->regmap))
+		return PTR_ERR(tmdev->regmap);
+
+	if (tmdev->chip->has_bus_clk_reset) {
+		tmdev->reset = devm_reset_control_get(dev, 0);
+		if (IS_ERR(tmdev->reset))
+			return PTR_ERR(tmdev->reset);
+
+		tmdev->bus_clk = devm_clk_get(&pdev->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(&pdev->dev, "mod");
+		if (IS_ERR(tmdev->mod_clk))
+			return PTR_ERR(tmdev->mod_clk);
+	}
+
+	ret = reset_control_deassert(tmdev->reset);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(tmdev->bus_clk);
+	if (ret)
+		goto assert_reset;
+
+	ret = clk_set_rate(tmdev->mod_clk, 24000000);
+	if (ret)
+		goto bus_disable;
+
+	ret = clk_prepare_enable(tmdev->mod_clk);
+	if (ret)
+		goto bus_disable;
+
+	ret = sun8i_ths_calibrate(tmdev);
+	if (ret)
+		goto mod_disable;
+
+	return 0;
+
+mod_disable:
+	clk_disable_unprepare(tmdev->mod_clk);
+bus_disable:
+	clk_disable_unprepare(tmdev->bus_clk);
+assert_reset:
+	reset_control_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 undocummented 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_register(struct ths_device *tmdev)
+{
+	int i;
+
+	for (i = 0; i < tmdev->chip->sensor_num; i++) {
+		tmdev->sensor[i].tmdev = tmdev;
+		tmdev->sensor[i].id = i;
+		tmdev->sensor[i].tzd =
+			devm_thermal_zone_of_sensor_register(tmdev->dev,
+							     i,
+							     &tmdev->sensor[i],
+							     &ths_ops);
+		if (IS_ERR(tmdev->sensor[i].tzd))
+			return PTR_ERR(tmdev->sensor[i].tzd);
+	}
+
+	return 0;
+}
+
+static int sun8i_ths_probe(struct platform_device *pdev)
+{
+	struct ths_device *tmdev;
+	struct device *dev = &pdev->dev;
+	int ret, irq;
+
+	tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL);
+	if (!tmdev)
+		return -ENOMEM;
+
+	tmdev->dev = dev;
+	tmdev->chip = of_device_get_match_data(&pdev->dev);
+	if (!tmdev->chip)
+		return -EINVAL;
+
+	platform_set_drvdata(pdev, tmdev);
+
+	ret = sun8i_ths_resource_init(tmdev);
+	if (ret)
+		return ret;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	ret = tmdev->chip->init(tmdev);
+	if (ret)
+		return ret;
+
+	ret = sun8i_ths_register(tmdev);
+	if (ret)
+		return ret;
+
+	/*
+	 * Avoid entering the interrupt handler, the thermal device is not
+	 * registered yet, we deffer the registration of the interrupt to
+	 * the end.
+	 */
+	ret = devm_request_threaded_irq(dev, irq, NULL,
+					sun8i_irq_thread,
+					IRQF_ONESHOT, "ths", tmdev);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+static int sun8i_ths_remove(struct platform_device *pdev)
+{
+	struct ths_device *tmdev = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(tmdev->mod_clk);
+	clk_disable_unprepare(tmdev->bus_clk);
+	reset_control_assert(tmdev->reset);
+
+	return 0;
+}
+
+static const struct ths_thermal_chip sun8i_a83t_ths = {
+	.sensor_num = 3,
+	.scale = 705,
+	.offset = 191668,
+	.temp_data_base = SUN8I_THS_TEMP_DATA,
+	.calibrate = sun8i_h3_ths_calibrate,
+	.init = sun8i_h3_thermal_init,
+	.irq_ack = sun8i_h3_irq_ack,
+	.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,
+	.calibrate = sun8i_h3_ths_calibrate,
+	.init = sun8i_h3_thermal_init,
+	.irq_ack = sun8i_h3_irq_ack,
+	.calc_temp = sun8i_ths_calc_temp,
+};
+
+static const struct ths_thermal_chip sun8i_r40_ths = {
+	.sensor_num = 3,
+	.offset = 251086,
+	.scale = 1130,
+	.has_mod_clk = true,
+	.has_bus_clk_reset = true,
+	.temp_data_base = SUN8I_THS_TEMP_DATA,
+	.calibrate = sun8i_h3_ths_calibrate,
+	.init = sun8i_h3_thermal_init,
+	.irq_ack = sun8i_h3_irq_ack,
+	.calc_temp = sun8i_ths_calc_temp,
+};
+
+static const struct ths_thermal_chip sun50i_a64_ths = {
+	.sensor_num = 3,
+	.offset = 253890,
+	.scale = 1170,
+	.has_mod_clk = true,
+	.has_bus_clk_reset = true,
+	.temp_data_base = SUN8I_THS_TEMP_DATA,
+	.calibrate = sun8i_h3_ths_calibrate,
+	.init = sun8i_h3_thermal_init,
+	.irq_ack = sun8i_h3_irq_ack,
+	.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,
+	.calibrate = sun8i_h3_ths_calibrate,
+	.init = sun8i_h3_thermal_init,
+	.irq_ack = sun8i_h3_irq_ack,
+	.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,
+	.calibrate = sun50i_h6_ths_calibrate,
+	.init = sun50i_h6_thermal_init,
+	.irq_ack = sun50i_h6_irq_ack,
+	.calc_temp = sun8i_ths_calc_temp,
+};
+
+static const struct of_device_id of_ths_match[] = {
+	{ .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths },
+	{ .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths },
+	{ .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths },
+	{ .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths },
+	{ .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths },
+	{ .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, of_ths_match);
+
+static struct platform_driver ths_driver = {
+	.probe = sun8i_ths_probe,
+	.remove = sun8i_ths_remove,
+	.driver = {
+		.name = "sun8i-thermal",
+		.of_match_table = of_ths_match,
+	},
+};
+module_platform_driver(ths_driver);
+
+MODULE_DESCRIPTION("Thermal sensor driver for Allwinner SOC");
+MODULE_LICENSE("GPL v2");
-- 
2.24.0


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

* [PATCH v6 2/7] dt-bindings: thermal: add YAML schema for sun8i-thermal driver bindings
  2019-11-27  5:29 [PATCH v6 0/7] add thermal sensor driver for A64, A83T, H3, H5, H6, R40 Vasily Khoruzhick
  2019-11-27  5:29 ` [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40 Vasily Khoruzhick
@ 2019-11-27  5:29 ` Vasily Khoruzhick
  2019-11-27 17:44   ` Maxime Ripard
  2019-11-27  5:29 ` [PATCH v6 3/7] ARM: dts: sun8i-a83t: Add thermal sensor and thermal zones Vasily Khoruzhick
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 31+ messages in thread
From: Vasily Khoruzhick @ 2019-11-27  5:29 UTC (permalink / raw)
  To: Yangtao Li, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Maxime Ripard,
	Chen-Yu Tsai, Mauro Carvalho Chehab, David S. Miller,
	Greg Kroah-Hartman, linux-pm, devicetree, linux-arm-kernel,
	Ondřej Jirman, linux-kernel
  Cc: Vasily Khoruzhick

From: Yangtao Li <tiny.windzz@gmail.com>

sun8i-thermal driver supports thermal sensor in wide range of Allwinner
SoCs. Add YAML schema for its bindings.

Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
 .../thermal/allwinner,sun8i-a83t-ths.yaml     | 103 ++++++++++++++++++
 1 file changed, 103 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml

diff --git a/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml b/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
new file mode 100644
index 000000000000..e622f0a4be90
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
@@ -0,0 +1,103 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/allwinner,sun8i-a83t-ths.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner SUN8I Thermal Controller Device Tree Bindings
+
+maintainers:
+  - Yangtao Li <tiny.windzz@gmail.com>
+
+properties:
+  compatible:
+    oneOf:
+      - const: allwinner,sun8i-a83t-ths
+      - const: allwinner,sun8i-h3-ths
+      - const: allwinner,sun8i-r40-ths
+      - const: allwinner,sun50i-a64-ths
+      - const: allwinner,sun50i-h5-ths
+      - const: allwinner,sun50i-h6-ths
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
+  clocks:
+    minItems: 1
+    maxItems: 2
+
+  clock-names:
+    anyOf:
+      - items:
+        - const: bus
+        - const: mod
+      - items:
+        - const: bus
+
+  '#thermal-sensor-cells':
+    enum: [ 0, 1 ]
+    description: |
+      Definition depends on soc version:
+
+      For "allwinner,sun8i-h3-ths",
+      value must be 0.
+      For all other compatibles
+      value must be 1.
+
+  nvmem-cells:
+    maxItems: 1
+    items:
+      - description: Calibration data for thermal sensors
+
+  nvmem-cell-names:
+    items:
+      - const: calibration
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - '#thermal-sensor-cells'
+
+examples:
+  - |
+    ths_a83t: ths@1f04000 {
+         compatible = "allwinner,sun8i-a83t-ths";
+         reg = <0x01f04000 0x100>;
+         interrupts = <0 31 0>;
+         nvmem-cells = <&ths_calibration>;
+         nvmem-cell-names = "calibration";
+         #thermal-sensor-cells = <1>;
+    };
+  - |
+    ths_h3: ths@1c25000 {
+         compatible = "allwinner,sun8i-h3-ths";
+         reg = <0x01c25000 0x400>;
+         clocks = <&ccu 0>, <&ccu 1>;
+         clock-names = "bus", "mod";
+         resets = <&ccu 2>;
+         interrupts = <0 31 0>;
+         nvmem-cells = <&ths_calibration>;
+         nvmem-cell-names = "calibration";
+         #thermal-sensor-cells = <0>;
+    };
+  - |
+    ths_h6: ths@5070400 {
+         compatible = "allwinner,sun50i-h6-ths";
+         reg = <0x05070400 0x100>;
+         clocks = <&ccu 0>;
+         clock-names = "bus";
+         resets = <&ccu 2>;
+         interrupts = <0 15 0>;
+         nvmem-cells = <&ths_calibration>;
+         nvmem-cell-names = "calibration";
+         #thermal-sensor-cells = <1>;
+    };
+
+...
-- 
2.24.0


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

* [PATCH v6 3/7] ARM: dts: sun8i-a83t: Add thermal sensor and thermal zones
  2019-11-27  5:29 [PATCH v6 0/7] add thermal sensor driver for A64, A83T, H3, H5, H6, R40 Vasily Khoruzhick
  2019-11-27  5:29 ` [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40 Vasily Khoruzhick
  2019-11-27  5:29 ` [PATCH v6 2/7] dt-bindings: thermal: add YAML schema for sun8i-thermal driver bindings Vasily Khoruzhick
@ 2019-11-27  5:29 ` Vasily Khoruzhick
  2019-11-27  5:29 ` [PATCH v6 4/7] ARM: dts: sun8i-h3: " Vasily Khoruzhick
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 31+ messages in thread
From: Vasily Khoruzhick @ 2019-11-27  5:29 UTC (permalink / raw)
  To: Yangtao Li, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Maxime Ripard,
	Chen-Yu Tsai, Mauro Carvalho Chehab, David S. Miller,
	Greg Kroah-Hartman, linux-pm, devicetree, linux-arm-kernel,
	Ondřej Jirman, linux-kernel
  Cc: Vasily Khoruzhick

From: Ondrej Jirman <megous@megous.com>

There are three sensors, two for each CPU cluster, one for GPU.

Signed-off-by: Ondrej Jirman <megous@megous.com>
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
 arch/arm/boot/dts/sun8i-a83t.dtsi | 36 +++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi
index 74bb053cf23c..902fee8be688 100644
--- a/arch/arm/boot/dts/sun8i-a83t.dtsi
+++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
@@ -50,6 +50,7 @@
 #include <dt-bindings/reset/sun8i-a83t-ccu.h>
 #include <dt-bindings/reset/sun8i-de2.h>
 #include <dt-bindings/reset/sun8i-r-ccu.h>
+#include <dt-bindings/thermal/thermal.h>
 
 / {
 	interrupt-parent = <&gic>;
@@ -581,6 +582,12 @@
 		sid: eeprom@1c14000 {
 			compatible = "allwinner,sun8i-a83t-sid";
 			reg = <0x1c14000 0x400>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			ths_calibration: thermal-sensor-calibration@34 {
+				reg = <0x34 8>;
+			};
 		};
 
 		usb_otg: usb@1c19000 {
@@ -1156,5 +1163,34 @@
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
+
+		ths: ths@1f04000 {
+			compatible = "allwinner,sun8i-a83t-ths";
+			reg = <0x01f04000 0x100>;
+			interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
+			nvmem-cells = <&ths_calibration>;
+			nvmem-cell-names = "calibration";
+			#thermal-sensor-cells = <1>;
+		};
+	};
+
+	thermal-zones {
+		cpu0_thermal: cpu0-thermal {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&ths 0>;
+		};
+
+		cpu1_thermal: cpu1-thermal {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&ths 1>;
+		};
+
+		gpu_thermal: gpu-thermal {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&ths 2>;
+		};
 	};
 };
-- 
2.24.0


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

* [PATCH v6 4/7] ARM: dts: sun8i-h3: Add thermal sensor and thermal zones
  2019-11-27  5:29 [PATCH v6 0/7] add thermal sensor driver for A64, A83T, H3, H5, H6, R40 Vasily Khoruzhick
                   ` (2 preceding siblings ...)
  2019-11-27  5:29 ` [PATCH v6 3/7] ARM: dts: sun8i-a83t: Add thermal sensor and thermal zones Vasily Khoruzhick
@ 2019-11-27  5:29 ` " Vasily Khoruzhick
  2019-11-27 19:40   ` Samuel Holland
  2019-11-27  5:29 ` [PATCH v6 5/7] arm64: dts: allwinner: sun50i-h5: " Vasily Khoruzhick
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 31+ messages in thread
From: Vasily Khoruzhick @ 2019-11-27  5:29 UTC (permalink / raw)
  To: Yangtao Li, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Maxime Ripard,
	Chen-Yu Tsai, Mauro Carvalho Chehab, David S. Miller,
	Greg Kroah-Hartman, linux-pm, devicetree, linux-arm-kernel,
	Ondřej Jirman, linux-kernel
  Cc: Vasily Khoruzhick

From: Ondrej Jirman <megous@megous.com>

There is just one sensor for the CPU.

Signed-off-by: Ondrej Jirman <megous@megous.com>
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
 arch/arm/boot/dts/sun8i-h3.dtsi | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
index e37c30e811d3..42fd0418d678 100644
--- a/arch/arm/boot/dts/sun8i-h3.dtsi
+++ b/arch/arm/boot/dts/sun8i-h3.dtsi
@@ -177,6 +177,26 @@
 			assigned-clocks = <&ccu CLK_GPU>;
 			assigned-clock-rates = <384000000>;
 		};
+
+		ths: ths@1c25000 {
+			compatible = "allwinner,sun8i-h3-ths";
+			reg = <0x01c25000 0x400>;
+			interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+			resets = <&ccu RST_BUS_THS>;
+			clocks = <&ccu CLK_BUS_THS>, <&ccu CLK_THS>;
+			clock-names = "bus", "mod";
+			nvmem-cells = <&ths_calibration>;
+			nvmem-cell-names = "calibration";
+			#thermal-sensor-cells = <0>;
+		};
+	};
+
+	thermal-zones {
+		cpu_thermal: cpu-thermal {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&ths 0>;
+		};
 	};
 };
 
@@ -234,4 +254,10 @@
 
 &sid {
 	compatible = "allwinner,sun8i-h3-sid";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	ths_calibration: thermal-sensor-calibration@34 {
+		reg = <0x34 4>;
+	};
 };
-- 
2.24.0


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

* [PATCH v6 5/7] arm64: dts: allwinner: sun50i-h5: Add thermal sensor and thermal zones
  2019-11-27  5:29 [PATCH v6 0/7] add thermal sensor driver for A64, A83T, H3, H5, H6, R40 Vasily Khoruzhick
                   ` (3 preceding siblings ...)
  2019-11-27  5:29 ` [PATCH v6 4/7] ARM: dts: sun8i-h3: " Vasily Khoruzhick
@ 2019-11-27  5:29 ` " Vasily Khoruzhick
  2019-11-27  5:29 ` [PATCH v6 6/7] arm64: dts: allwinner: sun50i-h6: " Vasily Khoruzhick
  2019-11-27  5:29 ` [PATCH v6 7/7] arm64: dts: allwinner: a64: Add thermal sensors " Vasily Khoruzhick
  6 siblings, 0 replies; 31+ messages in thread
From: Vasily Khoruzhick @ 2019-11-27  5:29 UTC (permalink / raw)
  To: Yangtao Li, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Maxime Ripard,
	Chen-Yu Tsai, Mauro Carvalho Chehab, David S. Miller,
	Greg Kroah-Hartman, linux-pm, devicetree, linux-arm-kernel,
	Ondřej Jirman, linux-kernel
  Cc: Vasily Khoruzhick

From: Ondrej Jirman <megous@megous.com>

There are two sensors, one for CPU, one for GPU.

Signed-off-by: Ondrej Jirman <megous@megous.com>
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
 arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi | 32 ++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
index f002a496d7cb..27fb0d2fc9a7 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
@@ -167,6 +167,32 @@
 			assigned-clocks = <&ccu CLK_GPU>;
 			assigned-clock-rates = <384000000>;
 		};
+
+		ths: ths@1c25000 {
+			compatible = "allwinner,sun50i-h5-ths";
+			reg = <0x01c25000 0x400>;
+			interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+			resets = <&ccu RST_BUS_THS>;
+			clocks = <&ccu CLK_BUS_THS>, <&ccu CLK_THS>;
+			clock-names = "bus", "mod";
+			nvmem-cells = <&ths_calibration>;
+			nvmem-cell-names = "calibration";
+			#thermal-sensor-cells = <1>;
+		};
+	};
+
+	thermal-zones {
+		cpu_thermal: cpu-thermal {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&ths 0>;
+		};
+
+		gpu_thermal {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&ths 1>;
+		};
 	};
 };
 
@@ -212,4 +238,10 @@
 
 &sid {
 	compatible = "allwinner,sun50i-h5-sid";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	ths_calibration: thermal-sensor-calibration@34 {
+		reg = <0x34 4>;
+	};
 };
-- 
2.24.0


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

* [PATCH v6 6/7] arm64: dts: allwinner: sun50i-h6: Add thermal sensor and thermal zones
  2019-11-27  5:29 [PATCH v6 0/7] add thermal sensor driver for A64, A83T, H3, H5, H6, R40 Vasily Khoruzhick
                   ` (4 preceding siblings ...)
  2019-11-27  5:29 ` [PATCH v6 5/7] arm64: dts: allwinner: sun50i-h5: " Vasily Khoruzhick
@ 2019-11-27  5:29 ` " Vasily Khoruzhick
  2019-11-27  5:29 ` [PATCH v6 7/7] arm64: dts: allwinner: a64: Add thermal sensors " Vasily Khoruzhick
  6 siblings, 0 replies; 31+ messages in thread
From: Vasily Khoruzhick @ 2019-11-27  5:29 UTC (permalink / raw)
  To: Yangtao Li, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Maxime Ripard,
	Chen-Yu Tsai, Mauro Carvalho Chehab, David S. Miller,
	Greg Kroah-Hartman, linux-pm, devicetree, linux-arm-kernel,
	Ondřej Jirman, linux-kernel
  Cc: Vasily Khoruzhick

From: Ondrej Jirman <megous@megous.com>

There are two sensors, one for CPU, one for GPU.

Signed-off-by: Ondrej Jirman <megous@megous.com>
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
 arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 33 ++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
index 0d5ea19336a1..18288387762f 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
@@ -11,6 +11,7 @@
 #include <dt-bindings/reset/sun50i-h6-ccu.h>
 #include <dt-bindings/reset/sun50i-h6-r-ccu.h>
 #include <dt-bindings/reset/sun8i-de2.h>
+#include <dt-bindings/thermal/thermal.h>
 
 / {
 	interrupt-parent = <&gic>;
@@ -210,6 +211,12 @@
 		sid: efuse@3006000 {
 			compatible = "allwinner,sun50i-h6-sid";
 			reg = <0x03006000 0x400>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			ths_calibration: thermal-sensor-calibration@14 {
+				reg = <0x14 0x6>;
+			};
 		};
 
 		watchdog: watchdog@30090a0 {
@@ -792,5 +799,31 @@
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
+
+		ths: ths@5070400 {
+			compatible = "allwinner,sun50i-h6-ths";
+			reg = <0x05070400 0x100>;
+			interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_BUS_THS>;
+			clock-names = "bus";
+			resets = <&ccu RST_BUS_THS>;
+			nvmem-cells = <&ths_calibration>;
+			nvmem-cell-names = "calibration";
+			#thermal-sensor-cells = <1>;
+		};
+	};
+
+	thermal-zones {
+		cpu-thermal {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&ths 0>;
+		};
+
+		gpu-thermal {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&ths 1>;
+		};
 	};
 };
-- 
2.24.0


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

* [PATCH v6 7/7] arm64: dts: allwinner: a64: Add thermal sensors and thermal zones
  2019-11-27  5:29 [PATCH v6 0/7] add thermal sensor driver for A64, A83T, H3, H5, H6, R40 Vasily Khoruzhick
                   ` (5 preceding siblings ...)
  2019-11-27  5:29 ` [PATCH v6 6/7] arm64: dts: allwinner: sun50i-h6: " Vasily Khoruzhick
@ 2019-11-27  5:29 ` " Vasily Khoruzhick
  6 siblings, 0 replies; 31+ messages in thread
From: Vasily Khoruzhick @ 2019-11-27  5:29 UTC (permalink / raw)
  To: Yangtao Li, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Maxime Ripard,
	Chen-Yu Tsai, Mauro Carvalho Chehab, David S. Miller,
	Greg Kroah-Hartman, linux-pm, devicetree, linux-arm-kernel,
	Ondřej Jirman, linux-kernel
  Cc: Vasily Khoruzhick

A64 has 3 thermal sensors: 1 for CPU, 2 for GPU.

Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 42 +++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 70f4cce6be43..59ef6410c6e0 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -49,6 +49,7 @@
 #include <dt-bindings/reset/sun50i-a64-ccu.h>
 #include <dt-bindings/reset/sun8i-de2.h>
 #include <dt-bindings/reset/sun8i-r-ccu.h>
+#include <dt-bindings/thermal/thermal.h>
 
 / {
 	interrupt-parent = <&gic>;
@@ -202,6 +203,29 @@
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
 	};
 
+	thermal-zones {
+		cpu_thermal: cpu0-thermal {
+			/* milliseconds */
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&ths 0>;
+		};
+
+		gpu0_thermal: gpu0-thermal {
+			/* milliseconds */
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&ths 1>;
+		};
+
+		gpu1_thermal: gpu1-thermal {
+			/* milliseconds */
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&ths 2>;
+		};
+	};
+
 	soc {
 		compatible = "simple-bus";
 		#address-cells = <1>;
@@ -476,6 +500,12 @@
 		sid: eeprom@1c14000 {
 			compatible = "allwinner,sun50i-a64-sid";
 			reg = <0x1c14000 0x400>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			ths_calibration: thermal-sensor-calibration@34 {
+				reg = <0x34 0x6>;
+			};
 		};
 
 		usb_otg: usb@1c19000 {
@@ -792,6 +822,18 @@
 			status = "disabled";
 		};
 
+		ths: thermal-sensor@1c25000 {
+			compatible = "allwinner,sun50i-a64-ths";
+			reg = <0x01c25000 0x100>;
+			clocks = <&ccu CLK_BUS_THS>, <&ccu CLK_THS>;
+			clock-names = "bus", "mod";
+			interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+			resets = <&ccu RST_BUS_THS>;
+			nvmem-cells = <&ths_calibration>;
+			nvmem-cell-names = "calibration";
+			#thermal-sensor-cells = <1>;
+		};
+
 		uart0: serial@1c28000 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28000 0x400>;
-- 
2.24.0


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

* Re: [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-11-27  5:29 ` [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40 Vasily Khoruzhick
@ 2019-11-27 11:14   ` Ondřej Jirman
  2019-11-27 17:35     ` Maxime Ripard
  2019-11-27 17:27   ` Maxime Ripard
  2019-11-28 16:43   ` Frank Lee
  2 siblings, 1 reply; 31+ messages in thread
From: Ondřej Jirman @ 2019-11-27 11:14 UTC (permalink / raw)
  To: Vasily Khoruzhick
  Cc: Yangtao Li, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Maxime Ripard,
	Chen-Yu Tsai, Mauro Carvalho Chehab, David S. Miller,
	Greg Kroah-Hartman, linux-pm, devicetree, linux-arm-kernel,
	linux-kernel

Hello Vasily,

I have found just a bunch of trivial simplifications, no functional changes:

On Tue, Nov 26, 2019 at 09:29:29PM -0800, Vasily Khoruzhick wrote:
> From: Yangtao Li <tiny.windzz@gmail.com>
> 
> This patch adds the support for allwinner thermal sensor, within
> allwinner SoC. It will register sensors for thermal framework
> and use device tree to bind cooling device.
> 
> Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
> Signed-off-by: Ondrej Jirman <megous@megous.com>
> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> ---
>  MAINTAINERS                     |   7 +
>  drivers/thermal/Kconfig         |  14 +
>  drivers/thermal/Makefile        |   1 +
>  drivers/thermal/sun8i_thermal.c | 643 ++++++++++++++++++++++++++++++++
>  4 files changed, 665 insertions(+)
>  create mode 100644 drivers/thermal/sun8i_thermal.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 66cc549ac327..da34f3f2e80b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -688,6 +688,13 @@ L:	linux-crypto@vger.kernel.org
>  S:	Maintained
>  F:	drivers/crypto/allwinner/
>  
> +ALLWINNER THERMAL DRIVER
> +M:	Yangtao Li <tiny.windzz@gmail.com>
> +L:	linux-pm@vger.kernel.org
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> +F:	drivers/thermal/sun8i_thermal.c
> +
>  ALLWINNER VPU DRIVER
>  M:	Maxime Ripard <mripard@kernel.org>
>  M:	Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index 001a21abcc28..0b0422e89adb 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -262,6 +262,20 @@ config SPEAR_THERMAL
>  	  Enable this to plug the SPEAr thermal sensor driver into the Linux
>  	  thermal framework.
>  
> +config SUN8I_THERMAL
> +	tristate "Allwinner sun8i thermal driver"
> +	depends on ARCH_SUNXI || COMPILE_TEST
> +	depends on HAS_IOMEM
> +	depends on NVMEM
> +	depends on OF
> +	depends on RESET_CONTROLLER
> +	help
> +	  Support for the sun8i thermal sensor driver into the Linux thermal
> +	  framework.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called sun8i-thermal.
> +
>  config ROCKCHIP_THERMAL
>  	tristate "Rockchip thermal driver"
>  	depends on ARCH_ROCKCHIP || COMPILE_TEST
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 74a37c7f847a..fa6f8b206281 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -31,6 +31,7 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
>  obj-y				+= broadcom/
>  obj-$(CONFIG_THERMAL_MMIO)		+= thermal_mmio.o
>  obj-$(CONFIG_SPEAR_THERMAL)	+= spear_thermal.o
> +obj-$(CONFIG_SUN8I_THERMAL)     += sun8i_thermal.o
>  obj-$(CONFIG_ROCKCHIP_THERMAL)	+= rockchip_thermal.o
>  obj-$(CONFIG_RCAR_THERMAL)	+= rcar_thermal.o
>  obj-$(CONFIG_RCAR_GEN3_THERMAL)	+= rcar_gen3_thermal.o
> diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
> new file mode 100644
> index 000000000000..e86b64f51196
> --- /dev/null
> +++ b/drivers/thermal/sun8i_thermal.c
> @@ -0,0 +1,643 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Thermal sensor driver for Allwinner SOC
> + * Copyright (C) 2019 Yangtao Li
> + *
> + * Based on the work of Icenowy Zheng <icenowy@aosc.io>
> + * Based on the work of Ondrej Jirman <megous@megous.com>
> + * Based on the work of Josef Gajdusek <atx@atx.name>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +#include <linux/thermal.h>
> +
> +#define MAX_SENSOR_NUM	4
> +
> +#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)
> +
> +/* millidegree celsius */
> +#define THS_EFUSE_CP_FT_MASK			0x3000
> +#define THS_EFUSE_CP_FT_BIT			12
> +#define THS_CALIBRATION_IN_FT			1
> +
> +struct ths_device;

This can be dropped ^.

> +struct tsensor {
> +	struct ths_device		*tmdev;
> +	struct thermal_zone_device	*tzd;
> +	int				id;
> +};
> +
> +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		(*calibrate)(struct ths_device *tmdev,
> +				     u16 *caldata, int callen);
> +	int		(*init)(struct ths_device *tmdev);
> +	int             (*irq_ack)(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 device				*dev;
> +	struct regmap				*regmap;
> +	struct reset_control			*reset;
> +	struct clk				*bus_clk;
> +	struct clk                              *mod_clk;
> +	struct tsensor				sensor[MAX_SENSOR_NUM];
> +	u32					cp_ft_flag;
> +};
> +
> +/* 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(void *data, int *temp)
> +{
> +	struct tsensor *s = data;
> +	struct ths_device *tmdev = s->tmdev;
> +	int val = 0;
> +
> +	regmap_read(tmdev->regmap, tmdev->chip->temp_data_base +
> +		    0x4 * s->id, &val);
> +
> +	/* ths have no data yet */
> +	if (!val)
> +		return -EAGAIN;
> +
> +	*temp = tmdev->chip->calc_temp(tmdev, s->id, 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 const struct thermal_zone_of_device_ops ths_ops = {
> +	.get_temp = sun8i_ths_get_temp,
> +};
> +
> +static const struct regmap_config config = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.fast_io = true,
> +	.max_register = 0xfc,
> +};
> +
> +static int sun8i_h3_irq_ack(struct ths_device *tmdev)
> +{
> +	int i, state, ret = 0;
> +
> +	regmap_read(tmdev->regmap, SUN8I_THS_IS, &state);
> +
> +	for (i = 0; i < tmdev->chip->sensor_num; i++) {
> +		if (state & SUN8I_THS_DATA_IRQ_STS(i)) {
> +			regmap_write(tmdev->regmap, SUN8I_THS_IS,
> +				     SUN8I_THS_DATA_IRQ_STS(i));
> +			ret |= BIT(i);
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int sun50i_h6_irq_ack(struct ths_device *tmdev)
> +{
> +	int i, state, ret = 0;
> +
> +	regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state);
> +
> +	for (i = 0; i < tmdev->chip->sensor_num; i++) {
> +		if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
> +			regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS,
> +				     SUN50I_H6_THS_DATA_IRQ_STS(i));
> +			ret |= BIT(i);
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static irqreturn_t sun8i_irq_thread(int irq, void *data)
> +{
> +	struct ths_device *tmdev = data;
> +	int i, state;
> +
> +	state = tmdev->chip->irq_ack(tmdev);
> +
> +	for (i = 0; i < tmdev->chip->sensor_num; i++) {
> +		if (state & BIT(i))
> +			thermal_zone_device_update(tmdev->sensor[i].tzd,
> +						   THERMAL_EVENT_UNSPECIFIED);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int sun8i_h3_ths_calibrate(struct ths_device *tmdev,
> +				  u16 *caldata, int callen)
> +{
> +	int i;
> +
> +	if (!caldata[0] || callen < 2 * tmdev->chip->sensor_num)
> +		return -EINVAL;
> +
> +	for (i = 0; i < tmdev->chip->sensor_num; i++) {
> +		int offset = (i % 2) << 4;
> +		regmap_update_bits(tmdev->regmap,
> +				   SUN8I_THS_TEMP_CALIB + (4 * (i >> 1)),
> +				   0xfff << offset,
> +				   caldata[i] << offset);
> +	}
> +
> +	return 0;
> +}
> +
> +static int sun50i_h6_ths_calibrate(struct ths_device *tmdev,
> +				   u16 *caldata, int callen)
> +{
> +	struct device *dev = tmdev->dev;
> +	int i, ft_temp;
> +
> +	if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num)
> +		return -EINVAL;
> +
> +	/*
> +	 * efuse layout:
> +	 *
> +	 *	0   11  16	 32
> +	 *	+-------+-------+-------+
> +	 *	|temp|  |sensor0|sensor1|
> +	 *	+-------+-------+-------+
> +	 *
> +	 * The calibration data on the H6 is the ambient temperature and
> +	 * sensor values that are filled during the factory test stage.
> +	 *
> +	 * The unit of stored FT temperature is 0.1 degreee celusis.
> +	 *
> +	 * We need to calculate a delta between measured and caluclated
> +	 * register values and this will become a calibration offset.
> +	 */
> +	ft_temp = (caldata[0] & FT_TEMP_MASK) * 100;
> +	tmdev->cp_ft_flag = (caldata[0] & THS_EFUSE_CP_FT_MASK)
> +		>> THS_EFUSE_CP_FT_BIT;
> +
> +	for (i = 0; i < tmdev->chip->sensor_num; i++) {
> +		int sensor_reg = caldata[i + 1];
> +		int cdata, offset;
> +		int sensor_temp = tmdev->chip->calc_temp(tmdev, i, sensor_reg);
> +
> +		/*
> +		 * Calibration data is CALIBRATE_DEFAULT - (calculated
> +		 * temperature from sensor reading at factory temperature
> +		 * minus actual factory temperature) * 14.88 (scale from
> +		 * temperature to register values)
> +		 */
> +		cdata = CALIBRATE_DEFAULT -
> +			((sensor_temp - ft_temp) * 10 / tmdev->chip->scale);
> +		if (cdata & ~TEMP_CALIB_MASK) {
> +			/*
> +			 * Calibration value more than 12-bit, but calibration
> +			 * register is 12-bit. In this case, ths hardware can
> +			 * still work without calibration, although the data
> +			 * won't be so accurate.
> +			 */
> +			dev_warn(dev, "sensor%d is not calibrated.\n", i);
> +			continue;
> +		}
> +
> +		offset = (i % 2) * 16;
> +		regmap_update_bits(tmdev->regmap,
> +				   SUN50I_H6_THS_TEMP_CALIB + (i / 2 * 4),
> +				   0xfff << offset,
> +				   cdata << offset);
> +	}
> +
> +	return 0;
> +}
> +
> +static int sun8i_ths_calibrate(struct ths_device *tmdev)
> +{
> +	struct nvmem_cell *calcell;
> +	struct device *dev = tmdev->dev;
> +	u16 *caldata;
> +	size_t callen;
> +	int ret = 0;
> +
> +	calcell = devm_nvmem_cell_get(dev, "calibration");
> +	if (IS_ERR(calcell)) {
> +		if (PTR_ERR(calcell) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		/*
> +		 * Even if the external calibration data stored in sid is
> +		 * not accessible, the THS hardware can still work, although
> +		 * the data won't be so accurate.
> +		 *
> +		 * The default value of calibration register is 0x800 for
> +		 * every sensor, and the calibration value is usually 0x7xx
> +		 * or 0x8xx, so they won't be away from the default value
> +		 * for a lot.
> +		 *
> +		 * So here we do not return error if the calibartion data is
> +		 * not available, except the probe needs deferring.
> +		 */
> +		goto out;
> +	}
> +
> +	caldata = nvmem_cell_read(calcell, &callen);
> +	if (IS_ERR(caldata)) {
> +		ret = PTR_ERR(caldata);
> +		goto out;
> +	}
> +
> +	tmdev->chip->calibrate(tmdev, caldata, callen);
> +
> +	kfree(caldata);
> +out:
> +	return ret;
> +}
> +
> +static int sun8i_ths_resource_init(struct ths_device *tmdev)
> +{
> +	struct device *dev = tmdev->dev;
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct resource *mem;

I didn't notice earlier, but this can be dropped ^

> +	void __iomem *base;
> +	int ret;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);

This can be dropped too ^

> +	base = devm_ioremap_resource(dev, mem);

And this can now simply be devm_platform_ioremap_resource(pdev, 0) ^

> +	if (IS_ERR(base))
> +		return PTR_ERR(base);
> +
> +	tmdev->regmap = devm_regmap_init_mmio(dev, base, &config);
> +	if (IS_ERR(tmdev->regmap))
> +		return PTR_ERR(tmdev->regmap);
> +
> +	if (tmdev->chip->has_bus_clk_reset) {
> +		tmdev->reset = devm_reset_control_get(dev, 0);
> +		if (IS_ERR(tmdev->reset))
> +			return PTR_ERR(tmdev->reset);
> +
> +		tmdev->bus_clk = devm_clk_get(&pdev->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(&pdev->dev, "mod");
> +		if (IS_ERR(tmdev->mod_clk))
> +			return PTR_ERR(tmdev->mod_clk);
> +	}
> +
> +	ret = reset_control_deassert(tmdev->reset);
> +	if (ret)
> +		return ret;
> +
> +	ret = clk_prepare_enable(tmdev->bus_clk);
> +	if (ret)
> +		goto assert_reset;
> +
> +	ret = clk_set_rate(tmdev->mod_clk, 24000000);
> +	if (ret)
> +		goto bus_disable;
> +
> +	ret = clk_prepare_enable(tmdev->mod_clk);
> +	if (ret)
> +		goto bus_disable;
> +
> +	ret = sun8i_ths_calibrate(tmdev);
> +	if (ret)
> +		goto mod_disable;
> +
> +	return 0;
> +
> +mod_disable:
> +	clk_disable_unprepare(tmdev->mod_clk);
> +bus_disable:
> +	clk_disable_unprepare(tmdev->bus_clk);
> +assert_reset:
> +	reset_control_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 undocummented 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_register(struct ths_device *tmdev)
> +{
> +	int i;
> +
> +	for (i = 0; i < tmdev->chip->sensor_num; i++) {
> +		tmdev->sensor[i].tmdev = tmdev;
> +		tmdev->sensor[i].id = i;
> +		tmdev->sensor[i].tzd =
> +			devm_thermal_zone_of_sensor_register(tmdev->dev,
> +							     i,
> +							     &tmdev->sensor[i],
> +							     &ths_ops);
> +		if (IS_ERR(tmdev->sensor[i].tzd))
> +			return PTR_ERR(tmdev->sensor[i].tzd);
> +	}
> +
> +	return 0;
> +}
> +
> +static int sun8i_ths_probe(struct platform_device *pdev)
> +{
> +	struct ths_device *tmdev;
> +	struct device *dev = &pdev->dev;
> +	int ret, irq;
> +
> +	tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL);
> +	if (!tmdev)
> +		return -ENOMEM;
> +
> +	tmdev->dev = dev;
> +	tmdev->chip = of_device_get_match_data(&pdev->dev);
> +	if (!tmdev->chip)
> +		return -EINVAL;
> +
> +	platform_set_drvdata(pdev, tmdev);
> +
> +	ret = sun8i_ths_resource_init(tmdev);
> +	if (ret)
> +		return ret;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0)
> +		return irq;
> +
> +	ret = tmdev->chip->init(tmdev);
> +	if (ret)
> +		return ret;
> +
> +	ret = sun8i_ths_register(tmdev);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Avoid entering the interrupt handler, the thermal device is not
> +	 * registered yet, we deffer the registration of the interrupt to
> +	 * the end.
> +	 */
> +	ret = devm_request_threaded_irq(dev, irq, NULL,
> +					sun8i_irq_thread,
> +					IRQF_ONESHOT, "ths", tmdev);
> +	if (ret)
> +		return ret;
> +
> +	return ret;

I guess just return devm_request_threaded_irq(... ^

thank you and regards,
	o.

> +}
> +
> +static int sun8i_ths_remove(struct platform_device *pdev)
> +{
> +	struct ths_device *tmdev = platform_get_drvdata(pdev);
> +
> +	clk_disable_unprepare(tmdev->mod_clk);
> +	clk_disable_unprepare(tmdev->bus_clk);
> +	reset_control_assert(tmdev->reset);
> +
> +	return 0;
> +}
> +
> +static const struct ths_thermal_chip sun8i_a83t_ths = {
> +	.sensor_num = 3,
> +	.scale = 705,
> +	.offset = 191668,
> +	.temp_data_base = SUN8I_THS_TEMP_DATA,
> +	.calibrate = sun8i_h3_ths_calibrate,
> +	.init = sun8i_h3_thermal_init,
> +	.irq_ack = sun8i_h3_irq_ack,
> +	.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,
> +	.calibrate = sun8i_h3_ths_calibrate,
> +	.init = sun8i_h3_thermal_init,
> +	.irq_ack = sun8i_h3_irq_ack,
> +	.calc_temp = sun8i_ths_calc_temp,
> +};
> +
> +static const struct ths_thermal_chip sun8i_r40_ths = {
> +	.sensor_num = 3,
> +	.offset = 251086,
> +	.scale = 1130,
> +	.has_mod_clk = true,
> +	.has_bus_clk_reset = true,
> +	.temp_data_base = SUN8I_THS_TEMP_DATA,
> +	.calibrate = sun8i_h3_ths_calibrate,
> +	.init = sun8i_h3_thermal_init,
> +	.irq_ack = sun8i_h3_irq_ack,
> +	.calc_temp = sun8i_ths_calc_temp,
> +};
> +
> +static const struct ths_thermal_chip sun50i_a64_ths = {
> +	.sensor_num = 3,
> +	.offset = 253890,
> +	.scale = 1170,
> +	.has_mod_clk = true,
> +	.has_bus_clk_reset = true,
> +	.temp_data_base = SUN8I_THS_TEMP_DATA,
> +	.calibrate = sun8i_h3_ths_calibrate,
> +	.init = sun8i_h3_thermal_init,
> +	.irq_ack = sun8i_h3_irq_ack,
> +	.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,
> +	.calibrate = sun8i_h3_ths_calibrate,
> +	.init = sun8i_h3_thermal_init,
> +	.irq_ack = sun8i_h3_irq_ack,
> +	.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,
> +	.calibrate = sun50i_h6_ths_calibrate,
> +	.init = sun50i_h6_thermal_init,
> +	.irq_ack = sun50i_h6_irq_ack,
> +	.calc_temp = sun8i_ths_calc_temp,
> +};
> +
> +static const struct of_device_id of_ths_match[] = {
> +	{ .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths },
> +	{ .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths },
> +	{ .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths },
> +	{ .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths },
> +	{ .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths },
> +	{ .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, of_ths_match);
> +
> +static struct platform_driver ths_driver = {
> +	.probe = sun8i_ths_probe,
> +	.remove = sun8i_ths_remove,
> +	.driver = {
> +		.name = "sun8i-thermal",
> +		.of_match_table = of_ths_match,
> +	},
> +};
> +module_platform_driver(ths_driver);
> +
> +MODULE_DESCRIPTION("Thermal sensor driver for Allwinner SOC");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.24.0
> 

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

* Re: [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-11-27  5:29 ` [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40 Vasily Khoruzhick
  2019-11-27 11:14   ` Ondřej Jirman
@ 2019-11-27 17:27   ` Maxime Ripard
  2019-11-28 16:43   ` Frank Lee
  2 siblings, 0 replies; 31+ messages in thread
From: Maxime Ripard @ 2019-11-27 17:27 UTC (permalink / raw)
  To: Vasily Khoruzhick
  Cc: Yangtao Li, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Chen-Yu Tsai,
	Mauro Carvalho Chehab, David S. Miller, Greg Kroah-Hartman,
	linux-pm, devicetree, linux-arm-kernel, Ondřej Jirman,
	linux-kernel

Hi,

Thanks for sending a new version of this!

On Tue, Nov 26, 2019 at 09:29:29PM -0800, Vasily Khoruzhick wrote:
> From: Yangtao Li <tiny.windzz@gmail.com>
>
> This patch adds the support for allwinner thermal sensor, within
> allwinner SoC. It will register sensors for thermal framework
> and use device tree to bind cooling device.
>
> Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
> Signed-off-by: Ondrej Jirman <megous@megous.com>
> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>

Acked-by: Maxime Ripard <mripard@kernel.org>

Thanks!
Maxime

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

* Re: [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-11-27 11:14   ` Ondřej Jirman
@ 2019-11-27 17:35     ` Maxime Ripard
  2019-11-27 19:44       ` Frank Lee
  0 siblings, 1 reply; 31+ messages in thread
From: Maxime Ripard @ 2019-11-27 17:35 UTC (permalink / raw)
  To: Vasily Khoruzhick, Yangtao Li, Zhang Rui, Eduardo Valentin,
	Daniel Lezcano, Amit Kucheria, Rob Herring, Mark Rutland,
	Chen-Yu Tsai, Mauro Carvalho Chehab, David S. Miller,
	Greg Kroah-Hartman, linux-pm, devicetree, linux-arm-kernel,
	linux-kernel

[-- Attachment #1: Type: text/plain, Size: 612 bytes --]

On Wed, Nov 27, 2019 at 12:14:19PM +0100, Ondřej Jirman wrote:
> > +	/*
> > +	 * Avoid entering the interrupt handler, the thermal device is not
> > +	 * registered yet, we deffer the registration of the interrupt to
> > +	 * the end.
> > +	 */
> > +	ret = devm_request_threaded_irq(dev, irq, NULL,
> > +					sun8i_irq_thread,
> > +					IRQF_ONESHOT, "ths", tmdev);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return ret;
>
> I guess just return devm_request_threaded_irq(... ^

This is harder to extend though, so I'd keep the current construct
(with a return 0 though).

Thanks!
Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 2/7] dt-bindings: thermal: add YAML schema for sun8i-thermal driver bindings
  2019-11-27  5:29 ` [PATCH v6 2/7] dt-bindings: thermal: add YAML schema for sun8i-thermal driver bindings Vasily Khoruzhick
@ 2019-11-27 17:44   ` Maxime Ripard
  2019-11-27 18:07     ` Ondřej Jirman
  2019-11-27 20:23     ` Vasily Khoruzhick
  0 siblings, 2 replies; 31+ messages in thread
From: Maxime Ripard @ 2019-11-27 17:44 UTC (permalink / raw)
  To: Vasily Khoruzhick
  Cc: Yangtao Li, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Chen-Yu Tsai,
	Mauro Carvalho Chehab, David S. Miller, Greg Kroah-Hartman,
	linux-pm, devicetree, linux-arm-kernel, Ondřej Jirman,
	linux-kernel

[-- Attachment #1: Type: text/plain, Size: 3566 bytes --]

Hi,

On Tue, Nov 26, 2019 at 09:29:30PM -0800, Vasily Khoruzhick wrote:
> From: Yangtao Li <tiny.windzz@gmail.com>
>
> sun8i-thermal driver supports thermal sensor in wide range of Allwinner
> SoCs. Add YAML schema for its bindings.
>
> Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> ---
>  .../thermal/allwinner,sun8i-a83t-ths.yaml     | 103 ++++++++++++++++++
>  1 file changed, 103 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
>
> diff --git a/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml b/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> new file mode 100644
> index 000000000000..e622f0a4be90
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> @@ -0,0 +1,103 @@
> +# SPDX-License-Identifier: GPL-2.0
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/thermal/allwinner,sun8i-a83t-ths.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Allwinner SUN8I Thermal Controller Device Tree Bindings
> +
> +maintainers:
> +  - Yangtao Li <tiny.windzz@gmail.com>
> +
> +properties:
> +  compatible:
> +    oneOf:
> +      - const: allwinner,sun8i-a83t-ths
> +      - const: allwinner,sun8i-h3-ths
> +      - const: allwinner,sun8i-r40-ths
> +      - const: allwinner,sun50i-a64-ths
> +      - const: allwinner,sun50i-h5-ths
> +      - const: allwinner,sun50i-h6-ths
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  resets:
> +    maxItems: 1
> +
> +  clocks:
> +    minItems: 1
> +    maxItems: 2
> +
> +  clock-names:
> +    anyOf:
> +      - items:
> +        - const: bus
> +        - const: mod
> +      - items:
> +        - const: bus

This can be:

clock-names:
  minItems: 1
  maxItems: 2
  items:
    - const: bus
    - const: mod

And the length should be checked based on the compatible value, with
something like

if:
  properties:
    compatible:
      contains:
        const: allwinner,sun50i-h6-ths

then:
  properties:
    clocks:
      maxItems: 1

    clock-names:
      maxItems: 1

else:
  properties:
    clocks:
      maxItems: 2

    clock-names:
      maxItems: 2

> +
> +  '#thermal-sensor-cells':
> +    enum: [ 0, 1 ]
> +    description: |
> +      Definition depends on soc version:
> +
> +      For "allwinner,sun8i-h3-ths",
> +      value must be 0.
> +      For all other compatibles
> +      value must be 1.

This should be checked using an if as well.

> +
> +  nvmem-cells:
> +    maxItems: 1
> +    items:
> +      - description: Calibration data for thermal sensors

You can drop the items and just move the description up one level,
under nvmem-cells

> +
> +  nvmem-cell-names:
> +    items:
> +      - const: calibration

Ditto for the const

> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - '#thermal-sensor-cells'

Whether clocks, clock-names and resets are thereshould be check using
an if statement as well.

> +
> +examples:
> +  - |
> +    ths_a83t: ths@1f04000 {

You don't need the label at all, and the node name should be
temperature-sensor according to the DT spec, not ths. This applies to
all you examples.

> +         compatible = "allwinner,sun8i-a83t-ths";
> +         reg = <0x01f04000 0x100>;
> +         interrupts = <0 31 0>;
> +         nvmem-cells = <&ths_calibration>;
> +         nvmem-cell-names = "calibration";
> +         #thermal-sensor-cells = <1>;
> +    };

New line.

Thanks!
Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 2/7] dt-bindings: thermal: add YAML schema for sun8i-thermal driver bindings
  2019-11-27 17:44   ` Maxime Ripard
@ 2019-11-27 18:07     ` Ondřej Jirman
  2019-11-27 18:52       ` Vasily Khoruzhick
  2019-11-27 20:23     ` Vasily Khoruzhick
  1 sibling, 1 reply; 31+ messages in thread
From: Ondřej Jirman @ 2019-11-27 18:07 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Vasily Khoruzhick, Yangtao Li, Zhang Rui, Eduardo Valentin,
	Daniel Lezcano, Amit Kucheria, Rob Herring, Mark Rutland,
	Chen-Yu Tsai, Mauro Carvalho Chehab, David S. Miller,
	Greg Kroah-Hartman, linux-pm, devicetree, linux-arm-kernel,
	linux-kernel

Hi,

On Wed, Nov 27, 2019 at 06:44:34PM +0100, Maxime Ripard wrote:
> Hi,
> 
> On Tue, Nov 26, 2019 at 09:29:30PM -0800, Vasily Khoruzhick wrote:
> > From: Yangtao Li <tiny.windzz@gmail.com>
> >
> > sun8i-thermal driver supports thermal sensor in wide range of Allwinner
> > SoCs. Add YAML schema for its bindings.
> >
> > Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
> > Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> > ---
> >  .../thermal/allwinner,sun8i-a83t-ths.yaml     | 103 ++++++++++++++++++
> >  1 file changed, 103 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml b/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> > new file mode 100644
> > index 000000000000..e622f0a4be90
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> > @@ -0,0 +1,103 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/thermal/allwinner,sun8i-a83t-ths.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Allwinner SUN8I Thermal Controller Device Tree Bindings
> > +
> > +maintainers:
> > +  - Yangtao Li <tiny.windzz@gmail.com>
> > +
> > +properties:
> > +  compatible:
> > +    oneOf:
> > +      - const: allwinner,sun8i-a83t-ths
> > +      - const: allwinner,sun8i-h3-ths
> > +      - const: allwinner,sun8i-r40-ths
> > +      - const: allwinner,sun50i-a64-ths
> > +      - const: allwinner,sun50i-h5-ths
> > +      - const: allwinner,sun50i-h6-ths
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  interrupts:
> > +    maxItems: 1
> > +
> > +  resets:
> > +    maxItems: 1
> > +
> > +  clocks:
> > +    minItems: 1
> > +    maxItems: 2
> > +
> > +  clock-names:
> > +    anyOf:
> > +      - items:
> > +        - const: bus
> > +        - const: mod
> > +      - items:
> > +        - const: bus
> 
> This can be:
> 
> clock-names:
>   minItems: 1

Additionally, minItems should be 0, since A83T doesn't have bus clock/reset. And
then there should be a special case for A83T too with min/maxItems = 0 for both
resets and clocks.

regards,
	o.

>   maxItems: 2
>   items:
>     - const: bus
>     - const: mod
> 
> And the length should be checked based on the compatible value, with
> something like
> 
> if:
>   properties:
>     compatible:
>       contains:
>         const: allwinner,sun50i-h6-ths
> 
> then:
>   properties:
>     clocks:
>       maxItems: 1
> 
>     clock-names:
>       maxItems: 1
> 
> else:
>   properties:
>     clocks:
>       maxItems: 2
> 
>     clock-names:
>       maxItems: 2
> 
> > +
> > +  '#thermal-sensor-cells':
> > +    enum: [ 0, 1 ]
> > +    description: |
> > +      Definition depends on soc version:
> > +
> > +      For "allwinner,sun8i-h3-ths",
> > +      value must be 0.
> > +      For all other compatibles
> > +      value must be 1.
> 
> This should be checked using an if as well.
> 
> > +
> > +  nvmem-cells:
> > +    maxItems: 1
> > +    items:
> > +      - description: Calibration data for thermal sensors
> 
> You can drop the items and just move the description up one level,
> under nvmem-cells
> 
> > +
> > +  nvmem-cell-names:
> > +    items:
> > +      - const: calibration
> 
> Ditto for the const
> 
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupts
> > +  - '#thermal-sensor-cells'
> 
> Whether clocks, clock-names and resets are thereshould be check using
> an if statement as well.
> 
> > +
> > +examples:
> > +  - |
> > +    ths_a83t: ths@1f04000 {
> 
> You don't need the label at all, and the node name should be
> temperature-sensor according to the DT spec, not ths. This applies to
> all you examples.
> 
> > +         compatible = "allwinner,sun8i-a83t-ths";
> > +         reg = <0x01f04000 0x100>;
> > +         interrupts = <0 31 0>;
> > +         nvmem-cells = <&ths_calibration>;
> > +         nvmem-cell-names = "calibration";
> > +         #thermal-sensor-cells = <1>;
> > +    };
> 
> New line.
> 
> Thanks!
> Maxime



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

* Re: [PATCH v6 2/7] dt-bindings: thermal: add YAML schema for sun8i-thermal driver bindings
  2019-11-27 18:07     ` Ondřej Jirman
@ 2019-11-27 18:52       ` Vasily Khoruzhick
  0 siblings, 0 replies; 31+ messages in thread
From: Vasily Khoruzhick @ 2019-11-27 18:52 UTC (permalink / raw)
  To: Maxime Ripard, Vasily Khoruzhick, Yangtao Li, Zhang Rui,
	Eduardo Valentin, Daniel Lezcano, Amit Kucheria, Rob Herring,
	Mark Rutland, Chen-Yu Tsai, Mauro Carvalho Chehab,
	David S. Miller, Greg Kroah-Hartman, Linux PM, devicetree,
	arm-linux, linux-kernel

On Wed, Nov 27, 2019 at 10:07 AM Ondřej Jirman <megous@megous.com> wrote:
>
> Hi,
>
> On Wed, Nov 27, 2019 at 06:44:34PM +0100, Maxime Ripard wrote:
> > Hi,
> >
> > On Tue, Nov 26, 2019 at 09:29:30PM -0800, Vasily Khoruzhick wrote:
> > > From: Yangtao Li <tiny.windzz@gmail.com>
> > >
> > > sun8i-thermal driver supports thermal sensor in wide range of Allwinner
> > > SoCs. Add YAML schema for its bindings.
> > >
> > > Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
> > > Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> > > ---
> > >  .../thermal/allwinner,sun8i-a83t-ths.yaml     | 103 ++++++++++++++++++
> > >  1 file changed, 103 insertions(+)
> > >  create mode 100644 Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> > >
> > > diff --git a/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml b/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> > > new file mode 100644
> > > index 000000000000..e622f0a4be90
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> > > @@ -0,0 +1,103 @@
> > > +# SPDX-License-Identifier: GPL-2.0
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/thermal/allwinner,sun8i-a83t-ths.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Allwinner SUN8I Thermal Controller Device Tree Bindings
> > > +
> > > +maintainers:
> > > +  - Yangtao Li <tiny.windzz@gmail.com>
> > > +
> > > +properties:
> > > +  compatible:
> > > +    oneOf:
> > > +      - const: allwinner,sun8i-a83t-ths
> > > +      - const: allwinner,sun8i-h3-ths
> > > +      - const: allwinner,sun8i-r40-ths
> > > +      - const: allwinner,sun50i-a64-ths
> > > +      - const: allwinner,sun50i-h5-ths
> > > +      - const: allwinner,sun50i-h6-ths
> > > +
> > > +  reg:
> > > +    maxItems: 1
> > > +
> > > +  interrupts:
> > > +    maxItems: 1
> > > +
> > > +  resets:
> > > +    maxItems: 1
> > > +
> > > +  clocks:
> > > +    minItems: 1
> > > +    maxItems: 2
> > > +
> > > +  clock-names:
> > > +    anyOf:
> > > +      - items:
> > > +        - const: bus
> > > +        - const: mod
> > > +      - items:
> > > +        - const: bus
> >
> > This can be:
> >
> > clock-names:
> >   minItems: 1
>
> Additionally, minItems should be 0, since A83T doesn't have bus clock/reset. And
> then there should be a special case for A83T too with min/maxItems = 0 for both
> resets and clocks.

That's why I removed clocks, clock-names and resets from required
properties. If they're present min/maxItems should be 1 and 2
accordingly.

> regards,
>         o.
>
> >   maxItems: 2
> >   items:
> >     - const: bus
> >     - const: mod
> >
> > And the length should be checked based on the compatible value, with
> > something like
> >
> > if:
> >   properties:
> >     compatible:
> >       contains:
> >         const: allwinner,sun50i-h6-ths
> >
> > then:
> >   properties:
> >     clocks:
> >       maxItems: 1
> >
> >     clock-names:
> >       maxItems: 1
> >
> > else:
> >   properties:
> >     clocks:
> >       maxItems: 2
> >
> >     clock-names:
> >       maxItems: 2
> >
> > > +
> > > +  '#thermal-sensor-cells':
> > > +    enum: [ 0, 1 ]
> > > +    description: |
> > > +      Definition depends on soc version:
> > > +
> > > +      For "allwinner,sun8i-h3-ths",
> > > +      value must be 0.
> > > +      For all other compatibles
> > > +      value must be 1.
> >
> > This should be checked using an if as well.
> >
> > > +
> > > +  nvmem-cells:
> > > +    maxItems: 1
> > > +    items:
> > > +      - description: Calibration data for thermal sensors
> >
> > You can drop the items and just move the description up one level,
> > under nvmem-cells
> >
> > > +
> > > +  nvmem-cell-names:
> > > +    items:
> > > +      - const: calibration
> >
> > Ditto for the const
> >
> > > +
> > > +required:
> > > +  - compatible
> > > +  - reg
> > > +  - interrupts
> > > +  - '#thermal-sensor-cells'
> >
> > Whether clocks, clock-names and resets are thereshould be check using
> > an if statement as well.
> >
> > > +
> > > +examples:
> > > +  - |
> > > +    ths_a83t: ths@1f04000 {
> >
> > You don't need the label at all, and the node name should be
> > temperature-sensor according to the DT spec, not ths. This applies to
> > all you examples.
> >
> > > +         compatible = "allwinner,sun8i-a83t-ths";
> > > +         reg = <0x01f04000 0x100>;
> > > +         interrupts = <0 31 0>;
> > > +         nvmem-cells = <&ths_calibration>;
> > > +         nvmem-cell-names = "calibration";
> > > +         #thermal-sensor-cells = <1>;
> > > +    };
> >
> > New line.
> >
> > Thanks!
> > Maxime
>
>

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

* Re: [PATCH v6 4/7] ARM: dts: sun8i-h3: Add thermal sensor and thermal zones
  2019-11-27  5:29 ` [PATCH v6 4/7] ARM: dts: sun8i-h3: " Vasily Khoruzhick
@ 2019-11-27 19:40   ` Samuel Holland
  0 siblings, 0 replies; 31+ messages in thread
From: Samuel Holland @ 2019-11-27 19:40 UTC (permalink / raw)
  To: Vasily Khoruzhick, Yangtao Li, Zhang Rui, Eduardo Valentin,
	Daniel Lezcano, Amit Kucheria, Rob Herring, Mark Rutland,
	Maxime Ripard, Chen-Yu Tsai, Mauro Carvalho Chehab,
	David S. Miller, Greg Kroah-Hartman, linux-pm, devicetree,
	linux-arm-kernel, Ondřej Jirman, linux-kernel

Hi,

On 11/26/19 11:29 PM, Vasily Khoruzhick wrote:
> From: Ondrej Jirman <megous@megous.com>
> 
> There is just one sensor for the CPU.
> 
> Signed-off-by: Ondrej Jirman <megous@megous.com>
> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> ---
>  arch/arm/boot/dts/sun8i-h3.dtsi | 26 ++++++++++++++++++++++++++
>  1 file changed, 26 insertions(+)
> 
> diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
> index e37c30e811d3..42fd0418d678 100644
> --- a/arch/arm/boot/dts/sun8i-h3.dtsi
> +++ b/arch/arm/boot/dts/sun8i-h3.dtsi
> @@ -177,6 +177,26 @@
>  			assigned-clocks = <&ccu CLK_GPU>;
>  			assigned-clock-rates = <384000000>;
>  		};
> +
> +		ths: ths@1c25000 {
> +			compatible = "allwinner,sun8i-h3-ths";
> +			reg = <0x01c25000 0x400>;
> +			interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
> +			resets = <&ccu RST_BUS_THS>;
> +			clocks = <&ccu CLK_BUS_THS>, <&ccu CLK_THS>;
> +			clock-names = "bus", "mod";
> +			nvmem-cells = <&ths_calibration>;
> +			nvmem-cell-names = "calibration";
> +			#thermal-sensor-cells = <0>;
> +		};
> +	};
> +
> +	thermal-zones {
> +		cpu_thermal: cpu-thermal {
> +			polling-delay-passive = <0>;
> +			polling-delay = <0>;
> +			thermal-sensors = <&ths 0>;
> +		};
>  	};
>  };
>  
> @@ -234,4 +254,10 @@
>  
>  &sid {
>  	compatible = "allwinner,sun8i-h3-sid";
> +	#address-cells = <1>;
> +	#size-cells = <1>;
> +
> +	ths_calibration: thermal-sensor-calibration@34 {
> +		reg = <0x34 4>;
> +	};

All of the lines added here are common between the H3 and H5, so they can go in
the shared SID node in sunxi-h3-h5.dtsi.

Cheers,
Samuel

>  };
> 


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

* Re: [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-11-27 17:35     ` Maxime Ripard
@ 2019-11-27 19:44       ` Frank Lee
  2019-11-27 19:48         ` Vasily Khoruzhick
  0 siblings, 1 reply; 31+ messages in thread
From: Frank Lee @ 2019-11-27 19:44 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Vasily Khoruzhick, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Chen-Yu Tsai,
	Mauro Carvalho Chehab, David S. Miller, Greg Kroah-Hartman,
	Linux PM, devicetree, Linux ARM, Linux Kernel Mailing List

Hello Vasily,

Thank you very much for your work on this.
This looks good to me.

By the way, I would like to ask comments about adding the following code.


diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
index c0ed60782b11..579dde5e0701 100644
--- a/drivers/thermal/sun8i_thermal.c
+++ b/drivers/thermal/sun8i_thermal.c
@@ -629,11 +629,63 @@ static const struct of_device_id of_ths_match[] = {
 };
 MODULE_DEVICE_TABLE(of, of_ths_match);

+static int __maybe_unused sun8i_thermal_suspend(struct device *dev)
+{
+ struct ths_device *tmdev; = dev_get_drvdata(dev);
+
+ clk_disable(tmdev->mod_clk);
+ clk_disable(tmdev->bus_clk);
+
+ reset_control_assert(tmdev->reset);
+
+ return 0;
+}
+
+static int __maybe_unused sun8i_thermal_resume(struct device *dev)
+{
+ struct ths_device *tmdev; = dev_get_drvdata(dev);
+ int error;
+
+ error = reset_control_deassert(tmdev->reset);
+ if (error)
+ return error;
+
+ error = clk_enable(tmdev->bus_clk);
+ if (error)
+ goto assert_reset;
+
+ clk_set_rate(tmdev->mod_clk, 24000000);
+ error = clk_enable(tmdev->mod_clk);
+ if (error)
+ goto bus_disable;
+
+ sun8i_ths_calibrate(tmdev);
+
+ ret = tmdev->chip->init(tmdev);
+ if (ret)
+ goto mod_disable;
+
+ return 0;
+
+mod_disable:
+ clk_disable(tmdev->mod_clk);
+bus_disable:
+ clk_disable(tmdev->bus_clk);
+assert_reset:
+ reset_control_assert(tmdev->reset);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(sun8i_thermal_pm_ops,
+ sun8i_thermal_suspend, sun8i_thermal_resume);
+
 static struct platform_driver ths_driver = {
  .probe = sun8i_ths_probe,
  .remove = sun8i_ths_remove,
  .driver = {
  .name = "sun8i-thermal",
+ .pm = &sun8i_thermal_pm_ops,
  .of_match_table = of_ths_match,
  },
 };

Yangtao

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

* Re: [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-11-27 19:44       ` Frank Lee
@ 2019-11-27 19:48         ` Vasily Khoruzhick
  2019-11-27 22:24           ` Ondřej Jirman
  2019-11-28  7:42           ` Maxime Ripard
  0 siblings, 2 replies; 31+ messages in thread
From: Vasily Khoruzhick @ 2019-11-27 19:48 UTC (permalink / raw)
  To: Frank Lee
  Cc: Maxime Ripard, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Chen-Yu Tsai,
	Mauro Carvalho Chehab, David S. Miller, Greg Kroah-Hartman,
	Linux PM, devicetree, Linux ARM, Linux Kernel Mailing List

On Wed, Nov 27, 2019 at 11:44 AM Frank Lee <tiny.windzz@gmail.com> wrote:
>
> Hello Vasily,
>
> Thank you very much for your work on this.
> This looks good to me.

Thanks!

> By the way, I would like to ask comments about adding the following code.

Can we add it as follow up patch? I don't think that I have a device
with working suspend to test it and I'm hesitant to add any code that
I can't test.

>
> diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
> index c0ed60782b11..579dde5e0701 100644
> --- a/drivers/thermal/sun8i_thermal.c
> +++ b/drivers/thermal/sun8i_thermal.c
> @@ -629,11 +629,63 @@ static const struct of_device_id of_ths_match[] = {
>  };
>  MODULE_DEVICE_TABLE(of, of_ths_match);
>
> +static int __maybe_unused sun8i_thermal_suspend(struct device *dev)
> +{
> + struct ths_device *tmdev; = dev_get_drvdata(dev);
> +
> + clk_disable(tmdev->mod_clk);
> + clk_disable(tmdev->bus_clk);
> +
> + reset_control_assert(tmdev->reset);
> +
> + return 0;
> +}
> +
> +static int __maybe_unused sun8i_thermal_resume(struct device *dev)
> +{
> + struct ths_device *tmdev; = dev_get_drvdata(dev);
> + int error;
> +
> + error = reset_control_deassert(tmdev->reset);
> + if (error)
> + return error;
> +
> + error = clk_enable(tmdev->bus_clk);
> + if (error)
> + goto assert_reset;
> +
> + clk_set_rate(tmdev->mod_clk, 24000000);
> + error = clk_enable(tmdev->mod_clk);
> + if (error)
> + goto bus_disable;
> +
> + sun8i_ths_calibrate(tmdev);
> +
> + ret = tmdev->chip->init(tmdev);
> + if (ret)
> + goto mod_disable;
> +
> + return 0;
> +
> +mod_disable:
> + clk_disable(tmdev->mod_clk);
> +bus_disable:
> + clk_disable(tmdev->bus_clk);
> +assert_reset:
> + reset_control_assert(tmdev->reset);
> +
> + return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(sun8i_thermal_pm_ops,
> + sun8i_thermal_suspend, sun8i_thermal_resume);
> +
>  static struct platform_driver ths_driver = {
>   .probe = sun8i_ths_probe,
>   .remove = sun8i_ths_remove,
>   .driver = {
>   .name = "sun8i-thermal",
> + .pm = &sun8i_thermal_pm_ops,
>   .of_match_table = of_ths_match,
>   },
>  };
>
> Yangtao

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

* Re: [PATCH v6 2/7] dt-bindings: thermal: add YAML schema for sun8i-thermal driver bindings
  2019-11-27 17:44   ` Maxime Ripard
  2019-11-27 18:07     ` Ondřej Jirman
@ 2019-11-27 20:23     ` Vasily Khoruzhick
  2019-11-28  7:43       ` Maxime Ripard
  1 sibling, 1 reply; 31+ messages in thread
From: Vasily Khoruzhick @ 2019-11-27 20:23 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Yangtao Li, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Chen-Yu Tsai,
	Mauro Carvalho Chehab, David S. Miller, Greg Kroah-Hartman,
	Linux PM, devicetree, arm-linux, Ondřej Jirman,
	linux-kernel

On Wed, Nov 27, 2019 at 9:44 AM Maxime Ripard <mripard@kernel.org> wrote:
>
> Hi,
>
> On Tue, Nov 26, 2019 at 09:29:30PM -0800, Vasily Khoruzhick wrote:
> > From: Yangtao Li <tiny.windzz@gmail.com>
> >
> > sun8i-thermal driver supports thermal sensor in wide range of Allwinner
> > SoCs. Add YAML schema for its bindings.
> >
> > Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
> > Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> > ---
> >  .../thermal/allwinner,sun8i-a83t-ths.yaml     | 103 ++++++++++++++++++
> >  1 file changed, 103 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml b/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> > new file mode 100644
> > index 000000000000..e622f0a4be90
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> > @@ -0,0 +1,103 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/thermal/allwinner,sun8i-a83t-ths.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Allwinner SUN8I Thermal Controller Device Tree Bindings
> > +
> > +maintainers:
> > +  - Yangtao Li <tiny.windzz@gmail.com>
> > +
> > +properties:
> > +  compatible:
> > +    oneOf:
> > +      - const: allwinner,sun8i-a83t-ths
> > +      - const: allwinner,sun8i-h3-ths
> > +      - const: allwinner,sun8i-r40-ths
> > +      - const: allwinner,sun50i-a64-ths
> > +      - const: allwinner,sun50i-h5-ths
> > +      - const: allwinner,sun50i-h6-ths
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  interrupts:
> > +    maxItems: 1
> > +
> > +  resets:
> > +    maxItems: 1
> > +
> > +  clocks:
> > +    minItems: 1
> > +    maxItems: 2
> > +
> > +  clock-names:
> > +    anyOf:
> > +      - items:
> > +        - const: bus
> > +        - const: mod
> > +      - items:
> > +        - const: bus
>
> This can be:
>
> clock-names:
>   minItems: 1
>   maxItems: 2
>   items:
>     - const: bus
>     - const: mod
>
> And the length should be checked based on the compatible value, with
> something like
>
> if:
>   properties:
>     compatible:
>       contains:
>         const: allwinner,sun50i-h6-ths
>
> then:
>   properties:
>     clocks:
>       maxItems: 1
>
>     clock-names:
>       maxItems: 1
>
> else:
>   properties:
>     clocks:
>       maxItems: 2
>
>     clock-names:
>       maxItems: 2

OK, will do

> > +
> > +  '#thermal-sensor-cells':
> > +    enum: [ 0, 1 ]
> > +    description: |
> > +      Definition depends on soc version:
> > +
> > +      For "allwinner,sun8i-h3-ths",
> > +      value must be 0.
> > +      For all other compatibles
> > +      value must be 1.
>
> This should be checked using an if as well.

Will do.

> > +
> > +  nvmem-cells:
> > +    maxItems: 1
> > +    items:
> > +      - description: Calibration data for thermal sensors
>
> You can drop the items and just move the description up one level,
> under nvmem-cells

Will do.

> > +
> > +  nvmem-cell-names:
> > +    items:
> > +      - const: calibration
>
> Ditto for the const

Sorry, I don't quite get it. What exactly do you want me to do with
this one? nvmem-cell-names must be "calibration"

> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupts
> > +  - '#thermal-sensor-cells'
>
> Whether clocks, clock-names and resets are thereshould be check using
> an if statement as well.

Will do

> > +
> > +examples:
> > +  - |
> > +    ths_a83t: ths@1f04000 {
>
> You don't need the label at all, and the node name should be
> temperature-sensor according to the DT spec, not ths. This applies to
> all you examples.

OK

> > +         compatible = "allwinner,sun8i-a83t-ths";
> > +         reg = <0x01f04000 0x100>;
> > +         interrupts = <0 31 0>;
> > +         nvmem-cells = <&ths_calibration>;
> > +         nvmem-cell-names = "calibration";
> > +         #thermal-sensor-cells = <1>;
> > +    };
>
> New line.

OK

>
> Thanks!
> Maxime

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

* Re: [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-11-27 19:48         ` Vasily Khoruzhick
@ 2019-11-27 22:24           ` Ondřej Jirman
  2019-11-28 15:16             ` Frank Lee
  2019-11-28  7:42           ` Maxime Ripard
  1 sibling, 1 reply; 31+ messages in thread
From: Ondřej Jirman @ 2019-11-27 22:24 UTC (permalink / raw)
  To: Vasily Khoruzhick
  Cc: Frank Lee, Mark Rutland, devicetree, Amit Kucheria, Linux PM,
	Greg Kroah-Hartman, Daniel Lezcano, Linux Kernel Mailing List,
	Maxime Ripard, Eduardo Valentin, Chen-Yu Tsai, Rob Herring,
	Mauro Carvalho Chehab, Zhang Rui, David S. Miller, Linux ARM

On Wed, Nov 27, 2019 at 11:48:32AM -0800, Vasily Khoruzhick wrote:
> On Wed, Nov 27, 2019 at 11:44 AM Frank Lee <tiny.windzz@gmail.com> wrote:
> >
> > Hello Vasily,
> >
> > Thank you very much for your work on this.
> > This looks good to me.
> 
> Thanks!
> 
> > By the way, I would like to ask comments about adding the following code.
> 
> Can we add it as follow up patch? I don't think that I have a device
> with working suspend to test it and I'm hesitant to add any code that
> I can't test.

I have, but it doesn't use any of the clocks and resets, so it wouldn't
test this fully, and basicaly doesn't need re-calibration at all, probably.

So that may be one feedback. On a83t, I'd made these callbacks a no-op.

regards,
	o.

> >
> > diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
> > index c0ed60782b11..579dde5e0701 100644
> > --- a/drivers/thermal/sun8i_thermal.c
> > +++ b/drivers/thermal/sun8i_thermal.c
> > @@ -629,11 +629,63 @@ static const struct of_device_id of_ths_match[] = {
> >  };
> >  MODULE_DEVICE_TABLE(of, of_ths_match);
> >
> > +static int __maybe_unused sun8i_thermal_suspend(struct device *dev)
> > +{
> > + struct ths_device *tmdev; = dev_get_drvdata(dev);
> > +
> > + clk_disable(tmdev->mod_clk);
> > + clk_disable(tmdev->bus_clk);
> > +
> > + reset_control_assert(tmdev->reset);
> > +
> > + return 0;
> > +}
> > +
> > +static int __maybe_unused sun8i_thermal_resume(struct device *dev)
> > +{
> > + struct ths_device *tmdev; = dev_get_drvdata(dev);
> > + int error;
> > +
> > + error = reset_control_deassert(tmdev->reset);
> > + if (error)
> > + return error;
> > +
> > + error = clk_enable(tmdev->bus_clk);
> > + if (error)
> > + goto assert_reset;
> > +
> > + clk_set_rate(tmdev->mod_clk, 24000000);
> > + error = clk_enable(tmdev->mod_clk);
> > + if (error)
> > + goto bus_disable;
> > +
> > + sun8i_ths_calibrate(tmdev);
> > +
> > + ret = tmdev->chip->init(tmdev);
> > + if (ret)
> > + goto mod_disable;
> > +
> > + return 0;
> > +
> > +mod_disable:
> > + clk_disable(tmdev->mod_clk);
> > +bus_disable:
> > + clk_disable(tmdev->bus_clk);
> > +assert_reset:
> > + reset_control_assert(tmdev->reset);
> > +
> > + return 0;
> > +}
> > +
> > +static SIMPLE_DEV_PM_OPS(sun8i_thermal_pm_ops,
> > + sun8i_thermal_suspend, sun8i_thermal_resume);
> > +
> >  static struct platform_driver ths_driver = {
> >   .probe = sun8i_ths_probe,
> >   .remove = sun8i_ths_remove,
> >   .driver = {
> >   .name = "sun8i-thermal",
> > + .pm = &sun8i_thermal_pm_ops,
> >   .of_match_table = of_ths_match,
> >   },
> >  };
> >
> > Yangtao
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-11-27 19:48         ` Vasily Khoruzhick
  2019-11-27 22:24           ` Ondřej Jirman
@ 2019-11-28  7:42           ` Maxime Ripard
  1 sibling, 0 replies; 31+ messages in thread
From: Maxime Ripard @ 2019-11-28  7:42 UTC (permalink / raw)
  To: Vasily Khoruzhick
  Cc: Frank Lee, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Chen-Yu Tsai,
	Mauro Carvalho Chehab, David S. Miller, Greg Kroah-Hartman,
	Linux PM, devicetree, Linux ARM, Linux Kernel Mailing List

[-- Attachment #1: Type: text/plain, Size: 582 bytes --]

On Wed, Nov 27, 2019 at 11:48:32AM -0800, Vasily Khoruzhick wrote:
> On Wed, Nov 27, 2019 at 11:44 AM Frank Lee <tiny.windzz@gmail.com> wrote:
> >
> > Hello Vasily,
> >
> > Thank you very much for your work on this.
> > This looks good to me.
>
> Thanks!
>
> > By the way, I would like to ask comments about adding the following code.
>
> Can we add it as follow up patch? I don't think that I have a device
> with working suspend to test it and I'm hesitant to add any code that
> I can't test.

Yeah, this should be a followup patch, otherwise this will never get
merged.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 2/7] dt-bindings: thermal: add YAML schema for sun8i-thermal driver bindings
  2019-11-27 20:23     ` Vasily Khoruzhick
@ 2019-11-28  7:43       ` Maxime Ripard
  2019-11-28 14:35         ` Frank Lee
  0 siblings, 1 reply; 31+ messages in thread
From: Maxime Ripard @ 2019-11-28  7:43 UTC (permalink / raw)
  To: Vasily Khoruzhick
  Cc: Yangtao Li, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Chen-Yu Tsai,
	Mauro Carvalho Chehab, David S. Miller, Greg Kroah-Hartman,
	Linux PM, devicetree, arm-linux, Ondřej Jirman,
	linux-kernel

[-- Attachment #1: Type: text/plain, Size: 480 bytes --]

On Wed, Nov 27, 2019 at 12:23:53PM -0800, Vasily Khoruzhick wrote:
> On Wed, Nov 27, 2019 at 9:44 AM Maxime Ripard <mripard@kernel.org> wrote:
> > > +
> > > +  nvmem-cell-names:
> > > +    items:
> > > +      - const: calibration
> >
> > Ditto for the const
>
> Sorry, I don't quite get it. What exactly do you want me to do with
> this one? nvmem-cell-names must be "calibration"

You don't need the items here either, this can be

nvmem-cell-names:
  const: calibration

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 2/7] dt-bindings: thermal: add YAML schema for sun8i-thermal driver bindings
  2019-11-28  7:43       ` Maxime Ripard
@ 2019-11-28 14:35         ` Frank Lee
  2019-11-28 14:45           ` Frank Lee
  0 siblings, 1 reply; 31+ messages in thread
From: Frank Lee @ 2019-11-28 14:35 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Vasily Khoruzhick, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Chen-Yu Tsai,
	Mauro Carvalho Chehab, David S. Miller, Greg Kroah-Hartman,
	Linux PM, devicetree, arm-linux, Ondřej Jirman,
	linux-kernel

[-- Attachment #1: Type: text/plain, Size: 44 bytes --]

Hi,

How about this?

Best regards,
Yangtao

[-- Attachment #2: 0001-dt-bindings-thermal-add-YAML-schema-for-sun8i-therma.patch --]
[-- Type: text/x-patch, Size: 4232 bytes --]

From 38dd0e4162b698a3007d4eee771e34810d3a8f9a Mon Sep 17 00:00:00 2001
From: Yangtao Li <tiny.windzz@gmail.com>
Date: Thu, 28 Nov 2019 22:31:28 +0800
Subject: [PATCH] dt-bindings: thermal: add YAML schema for sun8i-thermal
 driver bindings

sun8i-thermal driver supports thermal sensor in wide range of Allwinner
SoCs. Add YAML schema for its bindings.

Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
---
 .../bindings/thermal/sun8i-thermal.yaml       | 160 ++++++++++++++++++
 1 file changed, 160 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml

diff --git a/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml b/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
new file mode 100644
index 000000000000..1be32f09fcc8
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
@@ -0,0 +1,160 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/sun8i-thermal.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner SUN8I Thermal Controller Device Tree Bindings
+
+maintainers:
+  - Yangtao Li <tiny.windzz@gmail.com>
+
+description: |-
+  This describes the device tree binding for the Allwinner thermal
+  controller which measures the on-SoC temperatures.
+
+properties:
+  compatible:
+    enum:
+      - allwinner,sun8i-a83t-ths
+      - allwinner,sun8i-h3-ths
+      - allwinner,sun8i-r40-ths
+      - allwinner,sun50i-a64-ths
+      - allwinner,sun50i-h5-ths
+      - allwinner,sun50i-h6-ths
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
+  clocks:
+    minItems: 1
+    maxItems: 2
+    items:
+      - description: ths bus clock
+      - description: ths mod clock
+
+  clock-names:
+    minItems: 1
+    maxItems: 2
+    items:
+      - const: bus
+      - const: mod
+
+  "#thermal-sensor-cells":
+    enum: [ 0, 1 ]
+
+  nvmem-cells:
+    description: ths calibrate data
+
+  nvmem-cell-names:
+    const: calibration
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - "#thermal-sensor-cells"
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - allwinner,sun8i-h3-ths
+              - allwinner,sun8i-r40-ths
+              - allwinner,sun50i-a64-ths
+              - allwinner,sun50i-h5-ths
+              - allwinner,sun50i-h6-ths
+
+    then:
+      properties:
+        resets:
+          minItems: 1
+          maxItems: 1
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: allwinner,sun50i-h6-ths
+
+    then:
+      properties:
+        clocks:
+          minItems: 1
+          maxItems: 1
+
+        clock-names:
+          minItems: 1
+          maxItems: 1
+
+    else:
+      if:
+        properties:
+          compatible:
+            contains:
+              enum:
+                - allwinner,sun8i-h3-ths
+                - allwinner,sun8i-r40-ths
+                - allwinner,sun50i-a64-ths
+                - allwinner,sun50i-h5-ths
+
+      then:
+        properties:
+          clocks:
+            minItems: 2
+            maxItems: 2
+
+          clock-names:
+            minItems: 2
+            maxItems: 2
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: allwinner,sun8i-h3-ths
+
+    then:
+      properties:
+        "#thermal-sensor-cells":
+          const: 0
+
+    else:
+      properties:
+        "#thermal-sensor-cells":
+          const: 1
+
+additionalProperties: false
+
+examples:
+  - |
+    ths: ths@5070400 {
+        compatible = "allwinner,sun50i-h6-ths";
+        reg = <0x05070400 0x100>;
+        clocks = <&ccu 89>;
+        clock-names = "bus";
+        resets = <&ccu 36>;
+        interrupts = <0 15 4>;
+        nvmem-cells = <&tsen_calib>;
+        nvmem-cell-names = "calibration";
+        #thermal-sensor-cells = <1>;
+    };
+
+    sid: sid@3006000 {
+        compatible = "allwinner,sun50i-h6-sid";
+        reg = <0x03006000 0x400>;
+        #address-cells = <1>;
+        #size-cells = <1>;
+        tsen_calib: calib@14 {
+                reg = <0x14 6>;
+        };
+    };
+...
-- 
2.17.1


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

* Re: [PATCH v6 2/7] dt-bindings: thermal: add YAML schema for sun8i-thermal driver bindings
  2019-11-28 14:35         ` Frank Lee
@ 2019-11-28 14:45           ` Frank Lee
  2019-11-28 16:24             ` Vasily Khoruzhick
  0 siblings, 1 reply; 31+ messages in thread
From: Frank Lee @ 2019-11-28 14:45 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Vasily Khoruzhick, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Chen-Yu Tsai,
	Mauro Carvalho Chehab, David S. Miller, Greg Kroah-Hartman,
	Linux PM, devicetree, arm-linux, Ondřej Jirman,
	linux-kernel

diff --git a/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
b/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
new file mode 100644
index 000000000000..1be32f09fcc8
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
@@ -0,0 +1,160 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/sun8i-thermal.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner SUN8I Thermal Controller Device Tree Bindings
+
+maintainers:
+  - Yangtao Li <tiny.windzz@gmail.com>
+
+description: |-
+  This describes the device tree binding for the Allwinner thermal
+  controller which measures the on-SoC temperatures.
+
+properties:
+  compatible:
+    enum:
+      - allwinner,sun8i-a83t-ths
+      - allwinner,sun8i-h3-ths
+      - allwinner,sun8i-r40-ths
+      - allwinner,sun50i-a64-ths
+      - allwinner,sun50i-h5-ths
+      - allwinner,sun50i-h6-ths
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
+  clocks:
+    minItems: 1
+    maxItems: 2
+    items:
+      - description: ths bus clock
+      - description: ths mod clock
+
+  clock-names:
+    minItems: 1
+    maxItems: 2
+    items:
+      - const: bus
+      - const: mod
+
+  "#thermal-sensor-cells":
+    enum: [ 0, 1 ]
+
+  nvmem-cells:
+    description: ths calibrate data
+
+  nvmem-cell-names:
+    const: calibration
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - "#thermal-sensor-cells"
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - allwinner,sun8i-h3-ths
+              - allwinner,sun8i-r40-ths
+              - allwinner,sun50i-a64-ths
+              - allwinner,sun50i-h5-ths
+              - allwinner,sun50i-h6-ths
+
+    then:
+      properties:
+        resets:
+          minItems: 1
+          maxItems: 1
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: allwinner,sun50i-h6-ths
+
+    then:
+      properties:
+        clocks:
+          minItems: 1
+          maxItems: 1
+
+        clock-names:
+          minItems: 1
+          maxItems: 1
+
+    else:
+      if:
+        properties:
+          compatible:
+            contains:
+              enum:
+                - allwinner,sun8i-h3-ths
+                - allwinner,sun8i-r40-ths
+                - allwinner,sun50i-a64-ths
+                - allwinner,sun50i-h5-ths
+
+      then:
+        properties:
+          clocks:
+            minItems: 2
+            maxItems: 2
+
+          clock-names:
+            minItems: 2
+            maxItems: 2
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: allwinner,sun8i-h3-ths
+
+    then:
+      properties:
+        "#thermal-sensor-cells":
+          const: 0
+
+    else:
+      properties:
+        "#thermal-sensor-cells":
+          const: 1
+
+additionalProperties: false
+
+examples:
+  - |
+    ths: ths@5070400 {
+        compatible = "allwinner,sun50i-h6-ths";
+        reg = <0x05070400 0x100>;
+        clocks = <&ccu 89>;
+        clock-names = "bus";
+        resets = <&ccu 36>;
+        interrupts = <0 15 4>;
+        nvmem-cells = <&tsen_calib>;
+        nvmem-cell-names = "calibration";
+        #thermal-sensor-cells = <1>;
+    };
+
+    sid: sid@3006000 {
+        compatible = "allwinner,sun50i-h6-sid";
+        reg = <0x03006000 0x400>;
+        #address-cells = <1>;
+        #size-cells = <1>;
+        tsen_calib: calib@14 {
+                reg = <0x14 6>;
+        };
+    };
+...

On Thu, Nov 28, 2019 at 10:35 PM Frank Lee <tiny.windzz@gmail.com> wrote:
>
> Hi,
>
> How about this?
>
> Best regards,
> Yangtao

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

* Re: [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-11-27 22:24           ` Ondřej Jirman
@ 2019-11-28 15:16             ` Frank Lee
  2019-11-29  0:53               ` Ondřej Jirman
  0 siblings, 1 reply; 31+ messages in thread
From: Frank Lee @ 2019-11-28 15:16 UTC (permalink / raw)
  To: Vasily Khoruzhick, Frank Lee, Mark Rutland, devicetree,
	Amit Kucheria, Linux PM, Greg Kroah-Hartman, Daniel Lezcano,
	Linux Kernel Mailing List, Maxime Ripard, Eduardo Valentin,
	Chen-Yu Tsai, Rob Herring, Mauro Carvalho Chehab, Zhang Rui,
	David S. Miller, Linux ARM

On Thu, Nov 28, 2019 at 6:24 AM Ondřej Jirman <megous@megous.com> wrote:
>
> On Wed, Nov 27, 2019 at 11:48:32AM -0800, Vasily Khoruzhick wrote:
> > On Wed, Nov 27, 2019 at 11:44 AM Frank Lee <tiny.windzz@gmail.com> wrote:
> > >
> > > Hello Vasily,
> > >
> > > Thank you very much for your work on this.
> > > This looks good to me.
> >
> > Thanks!
> >
> > > By the way, I would like to ask comments about adding the following code.
> >
> > Can we add it as follow up patch? I don't think that I have a device
> > with working suspend to test it and I'm hesitant to add any code that
> > I can't test.
>
> I have, but it doesn't use any of the clocks and resets, so it wouldn't
> test this fully, and basicaly doesn't need re-calibration at all, probably.
>
> So that may be one feedback. On a83t, I'd made these callbacks a no-op.

This is just that the mainline code does not yet have the S2RAM code
implementation of these SOCs.
Each module has its own suspend function and resume function as part
of the system suspend function.
When the system is in S2RAM, the entire SOC will be completely powered
off, and each module
needs to save and restore its own state.

Yangtao

>
> regards,
>         o.
>
> > >
> > > diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
> > > index c0ed60782b11..579dde5e0701 100644
> > > --- a/drivers/thermal/sun8i_thermal.c
> > > +++ b/drivers/thermal/sun8i_thermal.c
> > > @@ -629,11 +629,63 @@ static const struct of_device_id of_ths_match[] = {
> > >  };
> > >  MODULE_DEVICE_TABLE(of, of_ths_match);
> > >
> > > +static int __maybe_unused sun8i_thermal_suspend(struct device *dev)
> > > +{
> > > + struct ths_device *tmdev; = dev_get_drvdata(dev);
> > > +
> > > + clk_disable(tmdev->mod_clk);
> > > + clk_disable(tmdev->bus_clk);
> > > +
> > > + reset_control_assert(tmdev->reset);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int __maybe_unused sun8i_thermal_resume(struct device *dev)
> > > +{
> > > + struct ths_device *tmdev; = dev_get_drvdata(dev);
> > > + int error;
> > > +
> > > + error = reset_control_deassert(tmdev->reset);
> > > + if (error)
> > > + return error;
> > > +
> > > + error = clk_enable(tmdev->bus_clk);
> > > + if (error)
> > > + goto assert_reset;
> > > +
> > > + clk_set_rate(tmdev->mod_clk, 24000000);
> > > + error = clk_enable(tmdev->mod_clk);
> > > + if (error)
> > > + goto bus_disable;
> > > +
> > > + sun8i_ths_calibrate(tmdev);
> > > +
> > > + ret = tmdev->chip->init(tmdev);
> > > + if (ret)
> > > + goto mod_disable;
> > > +
> > > + return 0;
> > > +
> > > +mod_disable:
> > > + clk_disable(tmdev->mod_clk);
> > > +bus_disable:
> > > + clk_disable(tmdev->bus_clk);
> > > +assert_reset:
> > > + reset_control_assert(tmdev->reset);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static SIMPLE_DEV_PM_OPS(sun8i_thermal_pm_ops,
> > > + sun8i_thermal_suspend, sun8i_thermal_resume);
> > > +
> > >  static struct platform_driver ths_driver = {
> > >   .probe = sun8i_ths_probe,
> > >   .remove = sun8i_ths_remove,
> > >   .driver = {
> > >   .name = "sun8i-thermal",
> > > + .pm = &sun8i_thermal_pm_ops,
> > >   .of_match_table = of_ths_match,
> > >   },
> > >  };
> > >
> > > Yangtao
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > linux-arm-kernel@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 2/7] dt-bindings: thermal: add YAML schema for sun8i-thermal driver bindings
  2019-11-28 14:45           ` Frank Lee
@ 2019-11-28 16:24             ` Vasily Khoruzhick
  0 siblings, 0 replies; 31+ messages in thread
From: Vasily Khoruzhick @ 2019-11-28 16:24 UTC (permalink / raw)
  To: Frank Lee
  Cc: Maxime Ripard, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Chen-Yu Tsai,
	Mauro Carvalho Chehab, David S. Miller, Greg Kroah-Hartman,
	Linux PM, devicetree, arm-linux, Ondřej Jirman,
	linux-kernel

On Thu, Nov 28, 2019 at 6:45 AM Frank Lee <tiny.windzz@gmail.com> wrote:

Hey Yangtao,

I've already addressed all the comments and fixed patchset is sitting
in https://github.com/anarsoul/linux-2.6/tree/ths-5.4

I'll rebase and submit new version after merge window is closed (in ~1.5 weeks?)

Regards,
Vasily

> diff --git a/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
> b/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
> new file mode 100644
> index 000000000000..1be32f09fcc8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
> @@ -0,0 +1,160 @@
> +# SPDX-License-Identifier: GPL-2.0
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/thermal/sun8i-thermal.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Allwinner SUN8I Thermal Controller Device Tree Bindings
> +
> +maintainers:
> +  - Yangtao Li <tiny.windzz@gmail.com>
> +
> +description: |-
> +  This describes the device tree binding for the Allwinner thermal
> +  controller which measures the on-SoC temperatures.
> +
> +properties:
> +  compatible:
> +    enum:
> +      - allwinner,sun8i-a83t-ths
> +      - allwinner,sun8i-h3-ths
> +      - allwinner,sun8i-r40-ths
> +      - allwinner,sun50i-a64-ths
> +      - allwinner,sun50i-h5-ths
> +      - allwinner,sun50i-h6-ths
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  resets:
> +    maxItems: 1
> +
> +  clocks:
> +    minItems: 1
> +    maxItems: 2
> +    items:
> +      - description: ths bus clock
> +      - description: ths mod clock
> +
> +  clock-names:
> +    minItems: 1
> +    maxItems: 2
> +    items:
> +      - const: bus
> +      - const: mod
> +
> +  "#thermal-sensor-cells":
> +    enum: [ 0, 1 ]
> +
> +  nvmem-cells:
> +    description: ths calibrate data
> +
> +  nvmem-cell-names:
> +    const: calibration
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - "#thermal-sensor-cells"
> +
> +allOf:
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            enum:
> +              - allwinner,sun8i-h3-ths
> +              - allwinner,sun8i-r40-ths
> +              - allwinner,sun50i-a64-ths
> +              - allwinner,sun50i-h5-ths
> +              - allwinner,sun50i-h6-ths
> +
> +    then:
> +      properties:
> +        resets:
> +          minItems: 1
> +          maxItems: 1
> +
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: allwinner,sun50i-h6-ths
> +
> +    then:
> +      properties:
> +        clocks:
> +          minItems: 1
> +          maxItems: 1
> +
> +        clock-names:
> +          minItems: 1
> +          maxItems: 1
> +
> +    else:
> +      if:
> +        properties:
> +          compatible:
> +            contains:
> +              enum:
> +                - allwinner,sun8i-h3-ths
> +                - allwinner,sun8i-r40-ths
> +                - allwinner,sun50i-a64-ths
> +                - allwinner,sun50i-h5-ths
> +
> +      then:
> +        properties:
> +          clocks:
> +            minItems: 2
> +            maxItems: 2
> +
> +          clock-names:
> +            minItems: 2
> +            maxItems: 2
> +
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: allwinner,sun8i-h3-ths
> +
> +    then:
> +      properties:
> +        "#thermal-sensor-cells":
> +          const: 0
> +
> +    else:
> +      properties:
> +        "#thermal-sensor-cells":
> +          const: 1
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    ths: ths@5070400 {
> +        compatible = "allwinner,sun50i-h6-ths";
> +        reg = <0x05070400 0x100>;
> +        clocks = <&ccu 89>;
> +        clock-names = "bus";
> +        resets = <&ccu 36>;
> +        interrupts = <0 15 4>;
> +        nvmem-cells = <&tsen_calib>;
> +        nvmem-cell-names = "calibration";
> +        #thermal-sensor-cells = <1>;
> +    };
> +
> +    sid: sid@3006000 {
> +        compatible = "allwinner,sun50i-h6-sid";
> +        reg = <0x03006000 0x400>;
> +        #address-cells = <1>;
> +        #size-cells = <1>;
> +        tsen_calib: calib@14 {
> +                reg = <0x14 6>;
> +        };
> +    };
> +...
>
> On Thu, Nov 28, 2019 at 10:35 PM Frank Lee <tiny.windzz@gmail.com> wrote:
> >
> > Hi,
> >
> > How about this?
> >
> > Best regards,
> > Yangtao

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

* Re: [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-11-27  5:29 ` [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40 Vasily Khoruzhick
  2019-11-27 11:14   ` Ondřej Jirman
  2019-11-27 17:27   ` Maxime Ripard
@ 2019-11-28 16:43   ` Frank Lee
  2019-11-28 17:06     ` Ondřej Jirman
  2 siblings, 1 reply; 31+ messages in thread
From: Frank Lee @ 2019-11-28 16:43 UTC (permalink / raw)
  To: Vasily Khoruzhick
  Cc: Zhang Rui, Eduardo Valentin, Daniel Lezcano, Amit Kucheria,
	Rob Herring, Mark Rutland, Maxime Ripard, Chen-Yu Tsai,
	Mauro Carvalho Chehab, David S. Miller, Greg Kroah-Hartman,
	Linux PM, devicetree, Linux ARM, Ondřej Jirman,
	Linux Kernel Mailing List

HI,

I took a closer look at it, and I had some questions about these places.

On Wed, Nov 27, 2019 at 1:30 PM Vasily Khoruzhick <anarsoul@gmail.com> wrote:
>
> From: Yangtao Li <tiny.windzz@gmail.com>
>
> This patch adds the support for allwinner thermal sensor, within
> allwinner SoC. It will register sensors for thermal framework
> and use device tree to bind cooling device.
>
> Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
> Signed-off-by: Ondrej Jirman <megous@megous.com>
> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> ---
>  MAINTAINERS                     |   7 +
>  drivers/thermal/Kconfig         |  14 +
>  drivers/thermal/Makefile        |   1 +
>  drivers/thermal/sun8i_thermal.c | 643 ++++++++++++++++++++++++++++++++
>  4 files changed, 665 insertions(+)
>  create mode 100644 drivers/thermal/sun8i_thermal.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 66cc549ac327..da34f3f2e80b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -688,6 +688,13 @@ L: linux-crypto@vger.kernel.org
>  S:     Maintained
>  F:     drivers/crypto/allwinner/
>
> +ALLWINNER THERMAL DRIVER
> +M:     Yangtao Li <tiny.windzz@gmail.com>
> +L:     linux-pm@vger.kernel.org
> +S:     Maintained
> +F:     Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> +F:     drivers/thermal/sun8i_thermal.c
> +
>  ALLWINNER VPU DRIVER
>  M:     Maxime Ripard <mripard@kernel.org>
>  M:     Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index 001a21abcc28..0b0422e89adb 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -262,6 +262,20 @@ config SPEAR_THERMAL
>           Enable this to plug the SPEAr thermal sensor driver into the Linux
>           thermal framework.
>
> +config SUN8I_THERMAL
> +       tristate "Allwinner sun8i thermal driver"
> +       depends on ARCH_SUNXI || COMPILE_TEST
> +       depends on HAS_IOMEM
> +       depends on NVMEM
> +       depends on OF
> +       depends on RESET_CONTROLLER
> +       help
> +         Support for the sun8i thermal sensor driver into the Linux thermal
> +         framework.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called sun8i-thermal.
> +
>  config ROCKCHIP_THERMAL
>         tristate "Rockchip thermal driver"
>         depends on ARCH_ROCKCHIP || COMPILE_TEST
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 74a37c7f847a..fa6f8b206281 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -31,6 +31,7 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
>  obj-y                          += broadcom/
>  obj-$(CONFIG_THERMAL_MMIO)             += thermal_mmio.o
>  obj-$(CONFIG_SPEAR_THERMAL)    += spear_thermal.o
> +obj-$(CONFIG_SUN8I_THERMAL)     += sun8i_thermal.o
>  obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
>  obj-$(CONFIG_RCAR_THERMAL)     += rcar_thermal.o
>  obj-$(CONFIG_RCAR_GEN3_THERMAL)        += rcar_gen3_thermal.o
> diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
> new file mode 100644
> index 000000000000..e86b64f51196
> --- /dev/null
> +++ b/drivers/thermal/sun8i_thermal.c
> @@ -0,0 +1,643 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Thermal sensor driver for Allwinner SOC
> + * Copyright (C) 2019 Yangtao Li
> + *
> + * Based on the work of Icenowy Zheng <icenowy@aosc.io>
> + * Based on the work of Ondrej Jirman <megous@megous.com>
> + * Based on the work of Josef Gajdusek <atx@atx.name>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +#include <linux/thermal.h>
> +
> +#define MAX_SENSOR_NUM 4
> +
> +#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)
> +
> +/* millidegree celsius */
> +#define THS_EFUSE_CP_FT_MASK                   0x3000
> +#define THS_EFUSE_CP_FT_BIT                    12
> +#define THS_CALIBRATION_IN_FT                  1
> +
> +struct ths_device;
> +
> +struct tsensor {
> +       struct ths_device               *tmdev;
> +       struct thermal_zone_device      *tzd;
> +       int                             id;
> +};
> +
> +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             (*calibrate)(struct ths_device *tmdev,
> +                                    u16 *caldata, int callen);
> +       int             (*init)(struct ths_device *tmdev);
> +       int             (*irq_ack)(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 device                           *dev;
> +       struct regmap                           *regmap;
> +       struct reset_control                    *reset;
> +       struct clk                              *bus_clk;
> +       struct clk                              *mod_clk;
> +       struct tsensor                          sensor[MAX_SENSOR_NUM];
> +       u32                                     cp_ft_flag;
> +};
> +
> +/* 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(void *data, int *temp)
> +{
> +       struct tsensor *s = data;
> +       struct ths_device *tmdev = s->tmdev;
> +       int val = 0;
> +
> +       regmap_read(tmdev->regmap, tmdev->chip->temp_data_base +
> +                   0x4 * s->id, &val);
> +
> +       /* ths have no data yet */
> +       if (!val)
> +               return -EAGAIN;
> +
> +       *temp = tmdev->chip->calc_temp(tmdev, s->id, 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 const struct thermal_zone_of_device_ops ths_ops = {
> +       .get_temp = sun8i_ths_get_temp,
> +};
> +
> +static const struct regmap_config config = {
> +       .reg_bits = 32,
> +       .val_bits = 32,
> +       .reg_stride = 4,
> +       .fast_io = true,
> +       .max_register = 0xfc,
> +};
> +
> +static int sun8i_h3_irq_ack(struct ths_device *tmdev)
> +{
> +       int i, state, ret = 0;
> +
> +       regmap_read(tmdev->regmap, SUN8I_THS_IS, &state);
> +
> +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> +               if (state & SUN8I_THS_DATA_IRQ_STS(i)) {
> +                       regmap_write(tmdev->regmap, SUN8I_THS_IS,
> +                                    SUN8I_THS_DATA_IRQ_STS(i));
> +                       ret |= BIT(i);
> +               }
> +       }
> +
> +       return ret;
> +}
> +
> +static int sun50i_h6_irq_ack(struct ths_device *tmdev)
> +{
> +       int i, state, ret = 0;
> +
> +       regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state);
> +
> +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> +               if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
> +                       regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS,
> +                                    SUN50I_H6_THS_DATA_IRQ_STS(i));
> +                       ret |= BIT(i);
> +               }
> +       }
> +
> +       return ret;
> +}
> +
> +static irqreturn_t sun8i_irq_thread(int irq, void *data)
> +{
> +       struct ths_device *tmdev = data;
> +       int i, state;
> +
> +       state = tmdev->chip->irq_ack(tmdev);
> +
> +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> +               if (state & BIT(i))
> +                       thermal_zone_device_update(tmdev->sensor[i].tzd,
> +                                                  THERMAL_EVENT_UNSPECIFIED);
> +       }
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int sun8i_h3_ths_calibrate(struct ths_device *tmdev,
> +                                 u16 *caldata, int callen)
> +{
> +       int i;
> +
> +       if (!caldata[0] || callen < 2 * tmdev->chip->sensor_num)
> +               return -EINVAL;
> +
> +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> +               int offset = (i % 2) << 4;
> +
> +               regmap_update_bits(tmdev->regmap,
> +                                  SUN8I_THS_TEMP_CALIB + (4 * (i >> 1)),
> +                                  0xfff << offset,
> +                                  caldata[i] << offset);
> +       }
> +
> +       return 0;
> +}
> +
> +static int sun50i_h6_ths_calibrate(struct ths_device *tmdev,
> +                                  u16 *caldata, int callen)
> +{
> +       struct device *dev = tmdev->dev;
> +       int i, ft_temp;
> +
> +       if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num)
> +               return -EINVAL;
> +
> +       /*
> +        * efuse layout:
> +        *
> +        *      0   11  16       32
> +        *      +-------+-------+-------+
> +        *      |temp|  |sensor0|sensor1|
> +        *      +-------+-------+-------+
> +        *
> +        * The calibration data on the H6 is the ambient temperature and
> +        * sensor values that are filled during the factory test stage.
> +        *
> +        * The unit of stored FT temperature is 0.1 degreee celusis.
> +        *
> +        * We need to calculate a delta between measured and caluclated
> +        * register values and this will become a calibration offset.
> +        */
> +       ft_temp = (caldata[0] & FT_TEMP_MASK) * 100;
> +       tmdev->cp_ft_flag = (caldata[0] & THS_EFUSE_CP_FT_MASK)
> +               >> THS_EFUSE_CP_FT_BIT;

Here got an unused data "cp_ft_flag",
which is used in the calculation of the temperature function according
to the source code.

https://github.com/orangepi-xunlong/OrangePiH6_kernel/blob/master/drivers/thermal/sunxi_thermal/sunxi_ths_core.c#L392

> +
> +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> +               int sensor_reg = caldata[i + 1];
> +               int cdata, offset;
> +               int sensor_temp = tmdev->chip->calc_temp(tmdev, i, sensor_reg);
> +
> +               /*
> +                * Calibration data is CALIBRATE_DEFAULT - (calculated
> +                * temperature from sensor reading at factory temperature
> +                * minus actual factory temperature) * 14.88 (scale from
> +                * temperature to register values)
> +                */
> +               cdata = CALIBRATE_DEFAULT -
> +                       ((sensor_temp - ft_temp) * 10 / tmdev->chip->scale);

Why change the formula here.

https://github.com/orangepi-xunlong/OrangePiH6_kernel/blob/master/drivers/thermal/sunxi_thermal/sunxi_ths_core.c#L339

> +               if (cdata & ~TEMP_CALIB_MASK) {
> +                       /*
> +                        * Calibration value more than 12-bit, but calibration
> +                        * register is 12-bit. In this case, ths hardware can
> +                        * still work without calibration, although the data
> +                        * won't be so accurate.
> +                        */
> +                       dev_warn(dev, "sensor%d is not calibrated.\n", i);
> +                       continue;
> +               }
> +
> +               offset = (i % 2) * 16;
> +               regmap_update_bits(tmdev->regmap,
> +                                  SUN50I_H6_THS_TEMP_CALIB + (i / 2 * 4),
> +                                  0xfff << offset,
> +                                  cdata << offset);
> +       }
> +
> +       return 0;
> +}
> +
> +static int sun8i_ths_calibrate(struct ths_device *tmdev)
> +{
> +       struct nvmem_cell *calcell;
> +       struct device *dev = tmdev->dev;
> +       u16 *caldata;
> +       size_t callen;
> +       int ret = 0;
> +
> +       calcell = devm_nvmem_cell_get(dev, "calibration");
> +       if (IS_ERR(calcell)) {
> +               if (PTR_ERR(calcell) == -EPROBE_DEFER)
> +                       return -EPROBE_DEFER;
> +               /*
> +                * Even if the external calibration data stored in sid is
> +                * not accessible, the THS hardware can still work, although
> +                * the data won't be so accurate.
> +                *
> +                * The default value of calibration register is 0x800 for
> +                * every sensor, and the calibration value is usually 0x7xx
> +                * or 0x8xx, so they won't be away from the default value
> +                * for a lot.
> +                *
> +                * So here we do not return error if the calibartion data is
> +                * not available, except the probe needs deferring.
> +                */
> +               goto out;
> +       }
> +
> +       caldata = nvmem_cell_read(calcell, &callen);
> +       if (IS_ERR(caldata)) {
> +               ret = PTR_ERR(caldata);
> +               goto out;
> +       }
> +
> +       tmdev->chip->calibrate(tmdev, caldata, callen);
> +
> +       kfree(caldata);
> +out:
> +       return ret;
> +}
> +
> +static int sun8i_ths_resource_init(struct ths_device *tmdev)
> +{
> +       struct device *dev = tmdev->dev;
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct resource *mem;
> +       void __iomem *base;
> +       int ret;
> +
> +       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       base = devm_ioremap_resource(dev, mem);
> +       if (IS_ERR(base))
> +               return PTR_ERR(base);
> +
> +       tmdev->regmap = devm_regmap_init_mmio(dev, base, &config);
> +       if (IS_ERR(tmdev->regmap))
> +               return PTR_ERR(tmdev->regmap);
> +
> +       if (tmdev->chip->has_bus_clk_reset) {
> +               tmdev->reset = devm_reset_control_get(dev, 0);
> +               if (IS_ERR(tmdev->reset))
> +                       return PTR_ERR(tmdev->reset);
> +
> +               tmdev->bus_clk = devm_clk_get(&pdev->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(&pdev->dev, "mod");
> +               if (IS_ERR(tmdev->mod_clk))
> +                       return PTR_ERR(tmdev->mod_clk);
> +       }
> +
> +       ret = reset_control_deassert(tmdev->reset);
> +       if (ret)
> +               return ret;
> +
> +       ret = clk_prepare_enable(tmdev->bus_clk);
> +       if (ret)
> +               goto assert_reset;
> +
> +       ret = clk_set_rate(tmdev->mod_clk, 24000000);
> +       if (ret)
> +               goto bus_disable;
> +
> +       ret = clk_prepare_enable(tmdev->mod_clk);
> +       if (ret)
> +               goto bus_disable;
> +
> +       ret = sun8i_ths_calibrate(tmdev);
> +       if (ret)
> +               goto mod_disable;
> +
> +       return 0;
> +
> +mod_disable:
> +       clk_disable_unprepare(tmdev->mod_clk);
> +bus_disable:
> +       clk_disable_unprepare(tmdev->bus_clk);
> +assert_reset:
> +       reset_control_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 undocummented 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_register(struct ths_device *tmdev)
> +{
> +       int i;
> +
> +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> +               tmdev->sensor[i].tmdev = tmdev;
> +               tmdev->sensor[i].id = i;
> +               tmdev->sensor[i].tzd =
> +                       devm_thermal_zone_of_sensor_register(tmdev->dev,
> +                                                            i,
> +                                                            &tmdev->sensor[i],
> +                                                            &ths_ops);
> +               if (IS_ERR(tmdev->sensor[i].tzd))
> +                       return PTR_ERR(tmdev->sensor[i].tzd);
> +       }
> +
> +       return 0;
> +}
> +
> +static int sun8i_ths_probe(struct platform_device *pdev)
> +{
> +       struct ths_device *tmdev;
> +       struct device *dev = &pdev->dev;
> +       int ret, irq;
> +
> +       tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL);
> +       if (!tmdev)
> +               return -ENOMEM;
> +
> +       tmdev->dev = dev;
> +       tmdev->chip = of_device_get_match_data(&pdev->dev);
> +       if (!tmdev->chip)
> +               return -EINVAL;
> +
> +       platform_set_drvdata(pdev, tmdev);
> +
> +       ret = sun8i_ths_resource_init(tmdev);
> +       if (ret)
> +               return ret;
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0)
> +               return irq;
> +
> +       ret = tmdev->chip->init(tmdev);
> +       if (ret)
> +               return ret;
> +
> +       ret = sun8i_ths_register(tmdev);
> +       if (ret)
> +               return ret;
> +
> +       /*
> +        * Avoid entering the interrupt handler, the thermal device is not
> +        * registered yet, we deffer the registration of the interrupt to
> +        * the end.
> +        */
> +       ret = devm_request_threaded_irq(dev, irq, NULL,
> +                                       sun8i_irq_thread,
> +                                       IRQF_ONESHOT, "ths", tmdev);
> +       if (ret)
> +               return ret;
> +
> +       return ret;
> +}
> +
> +static int sun8i_ths_remove(struct platform_device *pdev)
> +{
> +       struct ths_device *tmdev = platform_get_drvdata(pdev);
> +
> +       clk_disable_unprepare(tmdev->mod_clk);
> +       clk_disable_unprepare(tmdev->bus_clk);
> +       reset_control_assert(tmdev->reset);
> +
> +       return 0;
> +}
> +
> +static const struct ths_thermal_chip sun8i_a83t_ths = {
> +       .sensor_num = 3,
> +       .scale = 705,
> +       .offset = 191668,
> +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> +       .calibrate = sun8i_h3_ths_calibrate,
> +       .init = sun8i_h3_thermal_init,
> +       .irq_ack = sun8i_h3_irq_ack,
> +       .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,
> +       .calibrate = sun8i_h3_ths_calibrate,
> +       .init = sun8i_h3_thermal_init,
> +       .irq_ack = sun8i_h3_irq_ack,
> +       .calc_temp = sun8i_ths_calc_temp,
> +};
> +
> +static const struct ths_thermal_chip sun8i_r40_ths = {
> +       .sensor_num = 3,
> +       .offset = 251086,
> +       .scale = 1130,
> +       .has_mod_clk = true,
> +       .has_bus_clk_reset = true,
> +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> +       .calibrate = sun8i_h3_ths_calibrate,
> +       .init = sun8i_h3_thermal_init,
> +       .irq_ack = sun8i_h3_irq_ack,
> +       .calc_temp = sun8i_ths_calc_temp,
> +};
> +
> +static const struct ths_thermal_chip sun50i_a64_ths = {
> +       .sensor_num = 3,
> +       .offset = 253890,
> +       .scale = 1170,
> +       .has_mod_clk = true,
> +       .has_bus_clk_reset = true,
> +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> +       .calibrate = sun8i_h3_ths_calibrate,
> +       .init = sun8i_h3_thermal_init,
> +       .irq_ack = sun8i_h3_irq_ack,
> +       .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,
> +       .calibrate = sun8i_h3_ths_calibrate,
> +       .init = sun8i_h3_thermal_init,
> +       .irq_ack = sun8i_h3_irq_ack,
> +       .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,
> +       .calibrate = sun50i_h6_ths_calibrate,
> +       .init = sun50i_h6_thermal_init,
> +       .irq_ack = sun50i_h6_irq_ack,
> +       .calc_temp = sun8i_ths_calc_temp,
> +};
> +
> +static const struct of_device_id of_ths_match[] = {
> +       { .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths },
> +       { .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths },
> +       { .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths },
> +       { .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths },
> +       { .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths },
> +       { .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths },
> +       { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, of_ths_match);
> +
> +static struct platform_driver ths_driver = {
> +       .probe = sun8i_ths_probe,
> +       .remove = sun8i_ths_remove,
> +       .driver = {
> +               .name = "sun8i-thermal",
> +               .of_match_table = of_ths_match,
> +       },
> +};
> +module_platform_driver(ths_driver);
> +
> +MODULE_DESCRIPTION("Thermal sensor driver for Allwinner SOC");
> +MODULE_LICENSE("GPL v2");
> --
> 2.24.0
>

Thx,
Yangtao

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

* Re: [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-11-28 16:43   ` Frank Lee
@ 2019-11-28 17:06     ` Ondřej Jirman
  2019-11-29 11:46       ` Frank Lee
  0 siblings, 1 reply; 31+ messages in thread
From: Ondřej Jirman @ 2019-11-28 17:06 UTC (permalink / raw)
  To: Frank Lee
  Cc: Vasily Khoruzhick, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Maxime Ripard,
	Chen-Yu Tsai, Mauro Carvalho Chehab, David S. Miller,
	Greg Kroah-Hartman, Linux PM, devicetree, Linux ARM,
	Linux Kernel Mailing List

On Fri, Nov 29, 2019 at 12:43:02AM +0800, Frank Lee wrote:
> HI,
> 
> I took a closer look at it, and I had some questions about these places.
> 
> On Wed, Nov 27, 2019 at 1:30 PM Vasily Khoruzhick <anarsoul@gmail.com> wrote:
> >
> > From: Yangtao Li <tiny.windzz@gmail.com>
> >
> > This patch adds the support for allwinner thermal sensor, within
> > allwinner SoC. It will register sensors for thermal framework
> > and use device tree to bind cooling device.
> >
> > Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
> > Signed-off-by: Ondrej Jirman <megous@megous.com>
> > Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> > ---
> >  MAINTAINERS                     |   7 +
> >  drivers/thermal/Kconfig         |  14 +
> >  drivers/thermal/Makefile        |   1 +
> >  drivers/thermal/sun8i_thermal.c | 643 ++++++++++++++++++++++++++++++++
> >  4 files changed, 665 insertions(+)
> >  create mode 100644 drivers/thermal/sun8i_thermal.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 66cc549ac327..da34f3f2e80b 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -688,6 +688,13 @@ L: linux-crypto@vger.kernel.org
> >  S:     Maintained
> >  F:     drivers/crypto/allwinner/
> >
> > +ALLWINNER THERMAL DRIVER
> > +M:     Yangtao Li <tiny.windzz@gmail.com>
> > +L:     linux-pm@vger.kernel.org
> > +S:     Maintained
> > +F:     Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> > +F:     drivers/thermal/sun8i_thermal.c
> > +
> >  ALLWINNER VPU DRIVER
> >  M:     Maxime Ripard <mripard@kernel.org>
> >  M:     Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> > index 001a21abcc28..0b0422e89adb 100644
> > --- a/drivers/thermal/Kconfig
> > +++ b/drivers/thermal/Kconfig
> > @@ -262,6 +262,20 @@ config SPEAR_THERMAL
> >           Enable this to plug the SPEAr thermal sensor driver into the Linux
> >           thermal framework.
> >
> > +config SUN8I_THERMAL
> > +       tristate "Allwinner sun8i thermal driver"
> > +       depends on ARCH_SUNXI || COMPILE_TEST
> > +       depends on HAS_IOMEM
> > +       depends on NVMEM
> > +       depends on OF
> > +       depends on RESET_CONTROLLER
> > +       help
> > +         Support for the sun8i thermal sensor driver into the Linux thermal
> > +         framework.
> > +
> > +         To compile this driver as a module, choose M here: the
> > +         module will be called sun8i-thermal.
> > +
> >  config ROCKCHIP_THERMAL
> >         tristate "Rockchip thermal driver"
> >         depends on ARCH_ROCKCHIP || COMPILE_TEST
> > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> > index 74a37c7f847a..fa6f8b206281 100644
> > --- a/drivers/thermal/Makefile
> > +++ b/drivers/thermal/Makefile
> > @@ -31,6 +31,7 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
> >  obj-y                          += broadcom/
> >  obj-$(CONFIG_THERMAL_MMIO)             += thermal_mmio.o
> >  obj-$(CONFIG_SPEAR_THERMAL)    += spear_thermal.o
> > +obj-$(CONFIG_SUN8I_THERMAL)     += sun8i_thermal.o
> >  obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
> >  obj-$(CONFIG_RCAR_THERMAL)     += rcar_thermal.o
> >  obj-$(CONFIG_RCAR_GEN3_THERMAL)        += rcar_gen3_thermal.o
> > diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
> > new file mode 100644
> > index 000000000000..e86b64f51196
> > --- /dev/null
> > +++ b/drivers/thermal/sun8i_thermal.c
> > @@ -0,0 +1,643 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Thermal sensor driver for Allwinner SOC
> > + * Copyright (C) 2019 Yangtao Li
> > + *
> > + * Based on the work of Icenowy Zheng <icenowy@aosc.io>
> > + * Based on the work of Ondrej Jirman <megous@megous.com>
> > + * Based on the work of Josef Gajdusek <atx@atx.name>
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/device.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/module.h>
> > +#include <linux/nvmem-consumer.h>
> > +#include <linux/of_device.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +#include <linux/reset.h>
> > +#include <linux/slab.h>
> > +#include <linux/thermal.h>
> > +
> > +#define MAX_SENSOR_NUM 4
> > +
> > +#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)
> > +
> > +/* millidegree celsius */
> > +#define THS_EFUSE_CP_FT_MASK                   0x3000
> > +#define THS_EFUSE_CP_FT_BIT                    12
> > +#define THS_CALIBRATION_IN_FT                  1
> > +
> > +struct ths_device;
> > +
> > +struct tsensor {
> > +       struct ths_device               *tmdev;
> > +       struct thermal_zone_device      *tzd;
> > +       int                             id;
> > +};
> > +
> > +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             (*calibrate)(struct ths_device *tmdev,
> > +                                    u16 *caldata, int callen);
> > +       int             (*init)(struct ths_device *tmdev);
> > +       int             (*irq_ack)(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 device                           *dev;
> > +       struct regmap                           *regmap;
> > +       struct reset_control                    *reset;
> > +       struct clk                              *bus_clk;
> > +       struct clk                              *mod_clk;
> > +       struct tsensor                          sensor[MAX_SENSOR_NUM];
> > +       u32                                     cp_ft_flag;
> > +};
> > +
> > +/* 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(void *data, int *temp)
> > +{
> > +       struct tsensor *s = data;
> > +       struct ths_device *tmdev = s->tmdev;
> > +       int val = 0;
> > +
> > +       regmap_read(tmdev->regmap, tmdev->chip->temp_data_base +
> > +                   0x4 * s->id, &val);
> > +
> > +       /* ths have no data yet */
> > +       if (!val)
> > +               return -EAGAIN;
> > +
> > +       *temp = tmdev->chip->calc_temp(tmdev, s->id, 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 const struct thermal_zone_of_device_ops ths_ops = {
> > +       .get_temp = sun8i_ths_get_temp,
> > +};
> > +
> > +static const struct regmap_config config = {
> > +       .reg_bits = 32,
> > +       .val_bits = 32,
> > +       .reg_stride = 4,
> > +       .fast_io = true,
> > +       .max_register = 0xfc,
> > +};
> > +
> > +static int sun8i_h3_irq_ack(struct ths_device *tmdev)
> > +{
> > +       int i, state, ret = 0;
> > +
> > +       regmap_read(tmdev->regmap, SUN8I_THS_IS, &state);
> > +
> > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > +               if (state & SUN8I_THS_DATA_IRQ_STS(i)) {
> > +                       regmap_write(tmdev->regmap, SUN8I_THS_IS,
> > +                                    SUN8I_THS_DATA_IRQ_STS(i));
> > +                       ret |= BIT(i);
> > +               }
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +static int sun50i_h6_irq_ack(struct ths_device *tmdev)
> > +{
> > +       int i, state, ret = 0;
> > +
> > +       regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state);
> > +
> > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > +               if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
> > +                       regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS,
> > +                                    SUN50I_H6_THS_DATA_IRQ_STS(i));
> > +                       ret |= BIT(i);
> > +               }
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +static irqreturn_t sun8i_irq_thread(int irq, void *data)
> > +{
> > +       struct ths_device *tmdev = data;
> > +       int i, state;
> > +
> > +       state = tmdev->chip->irq_ack(tmdev);
> > +
> > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > +               if (state & BIT(i))
> > +                       thermal_zone_device_update(tmdev->sensor[i].tzd,
> > +                                                  THERMAL_EVENT_UNSPECIFIED);
> > +       }
> > +
> > +       return IRQ_HANDLED;
> > +}
> > +
> > +static int sun8i_h3_ths_calibrate(struct ths_device *tmdev,
> > +                                 u16 *caldata, int callen)
> > +{
> > +       int i;
> > +
> > +       if (!caldata[0] || callen < 2 * tmdev->chip->sensor_num)
> > +               return -EINVAL;
> > +
> > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > +               int offset = (i % 2) << 4;
> > +
> > +               regmap_update_bits(tmdev->regmap,
> > +                                  SUN8I_THS_TEMP_CALIB + (4 * (i >> 1)),
> > +                                  0xfff << offset,
> > +                                  caldata[i] << offset);
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int sun50i_h6_ths_calibrate(struct ths_device *tmdev,
> > +                                  u16 *caldata, int callen)
> > +{
> > +       struct device *dev = tmdev->dev;
> > +       int i, ft_temp;
> > +
> > +       if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num)
> > +               return -EINVAL;
> > +
> > +       /*
> > +        * efuse layout:
> > +        *
> > +        *      0   11  16       32
> > +        *      +-------+-------+-------+
> > +        *      |temp|  |sensor0|sensor1|
> > +        *      +-------+-------+-------+
> > +        *
> > +        * The calibration data on the H6 is the ambient temperature and
> > +        * sensor values that are filled during the factory test stage.
> > +        *
> > +        * The unit of stored FT temperature is 0.1 degreee celusis.
> > +        *
> > +        * We need to calculate a delta between measured and caluclated
> > +        * register values and this will become a calibration offset.
> > +        */
> > +       ft_temp = (caldata[0] & FT_TEMP_MASK) * 100;
> > +       tmdev->cp_ft_flag = (caldata[0] & THS_EFUSE_CP_FT_MASK)
> > +               >> THS_EFUSE_CP_FT_BIT;
> 
> Here got an unused data "cp_ft_flag",
> which is used in the calculation of the temperature function according
> to the source code.
> 
> https://github.com/orangepi-xunlong/OrangePiH6_kernel/blob/master/drivers/thermal/sunxi_thermal/sunxi_ths_core.c#L392

It should be used to enable the addition of ft_deviation in
sun8i_ths_get_temp(), I guess. Probably an ommission. I guess most of H6
chips will have this set anyway.

> > +
> > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > +               int sensor_reg = caldata[i + 1];
> > +               int cdata, offset;
> > +               int sensor_temp = tmdev->chip->calc_temp(tmdev, i, sensor_reg);
> > +
> > +               /*
> > +                * Calibration data is CALIBRATE_DEFAULT - (calculated
> > +                * temperature from sensor reading at factory temperature
> > +                * minus actual factory temperature) * 14.88 (scale from
> > +                * temperature to register values)
> > +                */
> > +               cdata = CALIBRATE_DEFAULT -
> > +                       ((sensor_temp - ft_temp) * 10 / tmdev->chip->scale);
> 
> Why change the formula here.
> 
> https://github.com/orangepi-xunlong/OrangePiH6_kernel/blob/master/drivers/thermal/sunxi_thermal/sunxi_ths_core.c#L339

It looks the same to me as before. Here's the original patch:

https://megous.com/git/linux/commit/?h=ths-5.4&id=cae28a7dfa6fc79ba37e31d4dff281947247e822

It's basicaly to avoid magic values TEMP_TO_REG and use what's defined
in the chip struct.

regards,
	o.

> > +               if (cdata & ~TEMP_CALIB_MASK) {
> > +                       /*
> > +                        * Calibration value more than 12-bit, but calibration
> > +                        * register is 12-bit. In this case, ths hardware can
> > +                        * still work without calibration, although the data
> > +                        * won't be so accurate.
> > +                        */
> > +                       dev_warn(dev, "sensor%d is not calibrated.\n", i);
> > +                       continue;
> > +               }
> > +
> > +               offset = (i % 2) * 16;
> > +               regmap_update_bits(tmdev->regmap,
> > +                                  SUN50I_H6_THS_TEMP_CALIB + (i / 2 * 4),
> > +                                  0xfff << offset,
> > +                                  cdata << offset);
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int sun8i_ths_calibrate(struct ths_device *tmdev)
> > +{
> > +       struct nvmem_cell *calcell;
> > +       struct device *dev = tmdev->dev;
> > +       u16 *caldata;
> > +       size_t callen;
> > +       int ret = 0;
> > +
> > +       calcell = devm_nvmem_cell_get(dev, "calibration");
> > +       if (IS_ERR(calcell)) {
> > +               if (PTR_ERR(calcell) == -EPROBE_DEFER)
> > +                       return -EPROBE_DEFER;
> > +               /*
> > +                * Even if the external calibration data stored in sid is
> > +                * not accessible, the THS hardware can still work, although
> > +                * the data won't be so accurate.
> > +                *
> > +                * The default value of calibration register is 0x800 for
> > +                * every sensor, and the calibration value is usually 0x7xx
> > +                * or 0x8xx, so they won't be away from the default value
> > +                * for a lot.
> > +                *
> > +                * So here we do not return error if the calibartion data is
> > +                * not available, except the probe needs deferring.
> > +                */
> > +               goto out;
> > +       }
> > +
> > +       caldata = nvmem_cell_read(calcell, &callen);
> > +       if (IS_ERR(caldata)) {
> > +               ret = PTR_ERR(caldata);
> > +               goto out;
> > +       }
> > +
> > +       tmdev->chip->calibrate(tmdev, caldata, callen);
> > +
> > +       kfree(caldata);
> > +out:
> > +       return ret;
> > +}
> > +
> > +static int sun8i_ths_resource_init(struct ths_device *tmdev)
> > +{
> > +       struct device *dev = tmdev->dev;
> > +       struct platform_device *pdev = to_platform_device(dev);
> > +       struct resource *mem;
> > +       void __iomem *base;
> > +       int ret;
> > +
> > +       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       base = devm_ioremap_resource(dev, mem);
> > +       if (IS_ERR(base))
> > +               return PTR_ERR(base);
> > +
> > +       tmdev->regmap = devm_regmap_init_mmio(dev, base, &config);
> > +       if (IS_ERR(tmdev->regmap))
> > +               return PTR_ERR(tmdev->regmap);
> > +
> > +       if (tmdev->chip->has_bus_clk_reset) {
> > +               tmdev->reset = devm_reset_control_get(dev, 0);
> > +               if (IS_ERR(tmdev->reset))
> > +                       return PTR_ERR(tmdev->reset);
> > +
> > +               tmdev->bus_clk = devm_clk_get(&pdev->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(&pdev->dev, "mod");
> > +               if (IS_ERR(tmdev->mod_clk))
> > +                       return PTR_ERR(tmdev->mod_clk);
> > +       }
> > +
> > +       ret = reset_control_deassert(tmdev->reset);
> > +       if (ret)
> > +               return ret;
> > +
> > +       ret = clk_prepare_enable(tmdev->bus_clk);
> > +       if (ret)
> > +               goto assert_reset;
> > +
> > +       ret = clk_set_rate(tmdev->mod_clk, 24000000);
> > +       if (ret)
> > +               goto bus_disable;
> > +
> > +       ret = clk_prepare_enable(tmdev->mod_clk);
> > +       if (ret)
> > +               goto bus_disable;
> > +
> > +       ret = sun8i_ths_calibrate(tmdev);
> > +       if (ret)
> > +               goto mod_disable;
> > +
> > +       return 0;
> > +
> > +mod_disable:
> > +       clk_disable_unprepare(tmdev->mod_clk);
> > +bus_disable:
> > +       clk_disable_unprepare(tmdev->bus_clk);
> > +assert_reset:
> > +       reset_control_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 undocummented 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_register(struct ths_device *tmdev)
> > +{
> > +       int i;
> > +
> > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > +               tmdev->sensor[i].tmdev = tmdev;
> > +               tmdev->sensor[i].id = i;
> > +               tmdev->sensor[i].tzd =
> > +                       devm_thermal_zone_of_sensor_register(tmdev->dev,
> > +                                                            i,
> > +                                                            &tmdev->sensor[i],
> > +                                                            &ths_ops);
> > +               if (IS_ERR(tmdev->sensor[i].tzd))
> > +                       return PTR_ERR(tmdev->sensor[i].tzd);
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int sun8i_ths_probe(struct platform_device *pdev)
> > +{
> > +       struct ths_device *tmdev;
> > +       struct device *dev = &pdev->dev;
> > +       int ret, irq;
> > +
> > +       tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL);
> > +       if (!tmdev)
> > +               return -ENOMEM;
> > +
> > +       tmdev->dev = dev;
> > +       tmdev->chip = of_device_get_match_data(&pdev->dev);
> > +       if (!tmdev->chip)
> > +               return -EINVAL;
> > +
> > +       platform_set_drvdata(pdev, tmdev);
> > +
> > +       ret = sun8i_ths_resource_init(tmdev);
> > +       if (ret)
> > +               return ret;
> > +
> > +       irq = platform_get_irq(pdev, 0);
> > +       if (irq < 0)
> > +               return irq;
> > +
> > +       ret = tmdev->chip->init(tmdev);
> > +       if (ret)
> > +               return ret;
> > +
> > +       ret = sun8i_ths_register(tmdev);
> > +       if (ret)
> > +               return ret;
> > +
> > +       /*
> > +        * Avoid entering the interrupt handler, the thermal device is not
> > +        * registered yet, we deffer the registration of the interrupt to
> > +        * the end.
> > +        */
> > +       ret = devm_request_threaded_irq(dev, irq, NULL,
> > +                                       sun8i_irq_thread,
> > +                                       IRQF_ONESHOT, "ths", tmdev);
> > +       if (ret)
> > +               return ret;
> > +
> > +       return ret;
> > +}
> > +
> > +static int sun8i_ths_remove(struct platform_device *pdev)
> > +{
> > +       struct ths_device *tmdev = platform_get_drvdata(pdev);
> > +
> > +       clk_disable_unprepare(tmdev->mod_clk);
> > +       clk_disable_unprepare(tmdev->bus_clk);
> > +       reset_control_assert(tmdev->reset);
> > +
> > +       return 0;
> > +}
> > +
> > +static const struct ths_thermal_chip sun8i_a83t_ths = {
> > +       .sensor_num = 3,
> > +       .scale = 705,
> > +       .offset = 191668,
> > +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> > +       .calibrate = sun8i_h3_ths_calibrate,
> > +       .init = sun8i_h3_thermal_init,
> > +       .irq_ack = sun8i_h3_irq_ack,
> > +       .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,
> > +       .calibrate = sun8i_h3_ths_calibrate,
> > +       .init = sun8i_h3_thermal_init,
> > +       .irq_ack = sun8i_h3_irq_ack,
> > +       .calc_temp = sun8i_ths_calc_temp,
> > +};
> > +
> > +static const struct ths_thermal_chip sun8i_r40_ths = {
> > +       .sensor_num = 3,
> > +       .offset = 251086,
> > +       .scale = 1130,
> > +       .has_mod_clk = true,
> > +       .has_bus_clk_reset = true,
> > +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> > +       .calibrate = sun8i_h3_ths_calibrate,
> > +       .init = sun8i_h3_thermal_init,
> > +       .irq_ack = sun8i_h3_irq_ack,
> > +       .calc_temp = sun8i_ths_calc_temp,
> > +};
> > +
> > +static const struct ths_thermal_chip sun50i_a64_ths = {
> > +       .sensor_num = 3,
> > +       .offset = 253890,
> > +       .scale = 1170,
> > +       .has_mod_clk = true,
> > +       .has_bus_clk_reset = true,
> > +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> > +       .calibrate = sun8i_h3_ths_calibrate,
> > +       .init = sun8i_h3_thermal_init,
> > +       .irq_ack = sun8i_h3_irq_ack,
> > +       .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,
> > +       .calibrate = sun8i_h3_ths_calibrate,
> > +       .init = sun8i_h3_thermal_init,
> > +       .irq_ack = sun8i_h3_irq_ack,
> > +       .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,
> > +       .calibrate = sun50i_h6_ths_calibrate,
> > +       .init = sun50i_h6_thermal_init,
> > +       .irq_ack = sun50i_h6_irq_ack,
> > +       .calc_temp = sun8i_ths_calc_temp,
> > +};
> > +
> > +static const struct of_device_id of_ths_match[] = {
> > +       { .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths },
> > +       { .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths },
> > +       { .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths },
> > +       { .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths },
> > +       { .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths },
> > +       { .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths },
> > +       { /* sentinel */ },
> > +};
> > +MODULE_DEVICE_TABLE(of, of_ths_match);
> > +
> > +static struct platform_driver ths_driver = {
> > +       .probe = sun8i_ths_probe,
> > +       .remove = sun8i_ths_remove,
> > +       .driver = {
> > +               .name = "sun8i-thermal",
> > +               .of_match_table = of_ths_match,
> > +       },
> > +};
> > +module_platform_driver(ths_driver);
> > +
> > +MODULE_DESCRIPTION("Thermal sensor driver for Allwinner SOC");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 2.24.0
> >
> 
> Thx,
> Yangtao

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

* Re: [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-11-28 15:16             ` Frank Lee
@ 2019-11-29  0:53               ` Ondřej Jirman
  0 siblings, 0 replies; 31+ messages in thread
From: Ondřej Jirman @ 2019-11-29  0:53 UTC (permalink / raw)
  To: Frank Lee
  Cc: Vasily Khoruzhick, Mark Rutland, devicetree, Amit Kucheria,
	Linux PM, Greg Kroah-Hartman, Daniel Lezcano,
	Linux Kernel Mailing List, Maxime Ripard, Eduardo Valentin,
	Chen-Yu Tsai, Rob Herring, Mauro Carvalho Chehab, Zhang Rui,
	David S. Miller, Linux ARM

On Thu, Nov 28, 2019 at 11:16:05PM +0800, Frank Lee wrote:
> On Thu, Nov 28, 2019 at 6:24 AM Ondřej Jirman <megous@megous.com> wrote:
> >
> > On Wed, Nov 27, 2019 at 11:48:32AM -0800, Vasily Khoruzhick wrote:
> > > On Wed, Nov 27, 2019 at 11:44 AM Frank Lee <tiny.windzz@gmail.com> wrote:
> > > >
> > > > Hello Vasily,
> > > >
> > > > Thank you very much for your work on this.
> > > > This looks good to me.
> > >
> > > Thanks!
> > >
> > > > By the way, I would like to ask comments about adding the following code.
> > >
> > > Can we add it as follow up patch? I don't think that I have a device
> > > with working suspend to test it and I'm hesitant to add any code that
> > > I can't test.
> >
> > I have, but it doesn't use any of the clocks and resets, so it wouldn't
> > test this fully, and basicaly doesn't need re-calibration at all, probably.
> >
> > So that may be one feedback. On a83t, I'd made these callbacks a no-op.
> 
> This is just that the mainline code does not yet have the S2RAM code
> implementation of these SOCs.
> Each module has its own suspend function and resume function as part
> of the system suspend function.
> When the system is in S2RAM, the entire SOC will be completely powered
> off, and each module
> needs to save and restore its own state.

I know, but on A83T THS (named R_TH in the datasheet) is in the RTC power
domain, so it will not be powered down, even if you power down rest of the SoC.

I guess you can make it not run by other means, but it will not need restoring
any state, since it will not lose any during suspend.

regards,
	o.

> Yangtao
> 
> >
> > regards,
> >         o.
> >
> > > >
> > > > diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
> > > > index c0ed60782b11..579dde5e0701 100644
> > > > --- a/drivers/thermal/sun8i_thermal.c
> > > > +++ b/drivers/thermal/sun8i_thermal.c
> > > > @@ -629,11 +629,63 @@ static const struct of_device_id of_ths_match[] = {
> > > >  };
> > > >  MODULE_DEVICE_TABLE(of, of_ths_match);
> > > >
> > > > +static int __maybe_unused sun8i_thermal_suspend(struct device *dev)
> > > > +{
> > > > + struct ths_device *tmdev; = dev_get_drvdata(dev);
> > > > +
> > > > + clk_disable(tmdev->mod_clk);
> > > > + clk_disable(tmdev->bus_clk);
> > > > +
> > > > + reset_control_assert(tmdev->reset);
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int __maybe_unused sun8i_thermal_resume(struct device *dev)
> > > > +{
> > > > + struct ths_device *tmdev; = dev_get_drvdata(dev);
> > > > + int error;
> > > > +
> > > > + error = reset_control_deassert(tmdev->reset);
> > > > + if (error)
> > > > + return error;
> > > > +
> > > > + error = clk_enable(tmdev->bus_clk);
> > > > + if (error)
> > > > + goto assert_reset;
> > > > +
> > > > + clk_set_rate(tmdev->mod_clk, 24000000);
> > > > + error = clk_enable(tmdev->mod_clk);
> > > > + if (error)
> > > > + goto bus_disable;
> > > > +
> > > > + sun8i_ths_calibrate(tmdev);
> > > > +
> > > > + ret = tmdev->chip->init(tmdev);
> > > > + if (ret)
> > > > + goto mod_disable;
> > > > +
> > > > + return 0;
> > > > +
> > > > +mod_disable:
> > > > + clk_disable(tmdev->mod_clk);
> > > > +bus_disable:
> > > > + clk_disable(tmdev->bus_clk);
> > > > +assert_reset:
> > > > + reset_control_assert(tmdev->reset);
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static SIMPLE_DEV_PM_OPS(sun8i_thermal_pm_ops,
> > > > + sun8i_thermal_suspend, sun8i_thermal_resume);
> > > > +
> > > >  static struct platform_driver ths_driver = {
> > > >   .probe = sun8i_ths_probe,
> > > >   .remove = sun8i_ths_remove,
> > > >   .driver = {
> > > >   .name = "sun8i-thermal",
> > > > + .pm = &sun8i_thermal_pm_ops,
> > > >   .of_match_table = of_ths_match,
> > > >   },
> > > >  };
> > > >
> > > > Yangtao
> > >
> > > _______________________________________________
> > > linux-arm-kernel mailing list
> > > linux-arm-kernel@lists.infradead.org
> > > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-11-28 17:06     ` Ondřej Jirman
@ 2019-11-29 11:46       ` Frank Lee
  2019-12-02  1:58         ` Ondřej Jirman
  0 siblings, 1 reply; 31+ messages in thread
From: Frank Lee @ 2019-11-29 11:46 UTC (permalink / raw)
  To: Frank Lee, Vasily Khoruzhick, Zhang Rui, Eduardo Valentin,
	Daniel Lezcano, Amit Kucheria, Rob Herring, Mark Rutland,
	Maxime Ripard, Chen-Yu Tsai, Mauro Carvalho Chehab,
	David S. Miller, Greg Kroah-Hartman, Linux PM, devicetree,
	Linux ARM, Linux Kernel Mailing List

On Fri, Nov 29, 2019 at 1:06 AM Ondřej Jirman <megous@megous.com> wrote:
>
> On Fri, Nov 29, 2019 at 12:43:02AM +0800, Frank Lee wrote:
> > HI,
> >
> > I took a closer look at it, and I had some questions about these places.
> >
> > On Wed, Nov 27, 2019 at 1:30 PM Vasily Khoruzhick <anarsoul@gmail.com> wrote:
> > >
> > > From: Yangtao Li <tiny.windzz@gmail.com>
> > >
> > > This patch adds the support for allwinner thermal sensor, within
> > > allwinner SoC. It will register sensors for thermal framework
> > > and use device tree to bind cooling device.
> > >
> > > Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
> > > Signed-off-by: Ondrej Jirman <megous@megous.com>
> > > Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> > > ---
> > >  MAINTAINERS                     |   7 +
> > >  drivers/thermal/Kconfig         |  14 +
> > >  drivers/thermal/Makefile        |   1 +
> > >  drivers/thermal/sun8i_thermal.c | 643 ++++++++++++++++++++++++++++++++
> > >  4 files changed, 665 insertions(+)
> > >  create mode 100644 drivers/thermal/sun8i_thermal.c
> > >
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index 66cc549ac327..da34f3f2e80b 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -688,6 +688,13 @@ L: linux-crypto@vger.kernel.org
> > >  S:     Maintained
> > >  F:     drivers/crypto/allwinner/
> > >
> > > +ALLWINNER THERMAL DRIVER
> > > +M:     Yangtao Li <tiny.windzz@gmail.com>
> > > +L:     linux-pm@vger.kernel.org
> > > +S:     Maintained
> > > +F:     Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> > > +F:     drivers/thermal/sun8i_thermal.c
> > > +
> > >  ALLWINNER VPU DRIVER
> > >  M:     Maxime Ripard <mripard@kernel.org>
> > >  M:     Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> > > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> > > index 001a21abcc28..0b0422e89adb 100644
> > > --- a/drivers/thermal/Kconfig
> > > +++ b/drivers/thermal/Kconfig
> > > @@ -262,6 +262,20 @@ config SPEAR_THERMAL
> > >           Enable this to plug the SPEAr thermal sensor driver into the Linux
> > >           thermal framework.
> > >
> > > +config SUN8I_THERMAL
> > > +       tristate "Allwinner sun8i thermal driver"
> > > +       depends on ARCH_SUNXI || COMPILE_TEST
> > > +       depends on HAS_IOMEM
> > > +       depends on NVMEM
> > > +       depends on OF
> > > +       depends on RESET_CONTROLLER
> > > +       help
> > > +         Support for the sun8i thermal sensor driver into the Linux thermal
> > > +         framework.
> > > +
> > > +         To compile this driver as a module, choose M here: the
> > > +         module will be called sun8i-thermal.
> > > +
> > >  config ROCKCHIP_THERMAL
> > >         tristate "Rockchip thermal driver"
> > >         depends on ARCH_ROCKCHIP || COMPILE_TEST
> > > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> > > index 74a37c7f847a..fa6f8b206281 100644
> > > --- a/drivers/thermal/Makefile
> > > +++ b/drivers/thermal/Makefile
> > > @@ -31,6 +31,7 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
> > >  obj-y                          += broadcom/
> > >  obj-$(CONFIG_THERMAL_MMIO)             += thermal_mmio.o
> > >  obj-$(CONFIG_SPEAR_THERMAL)    += spear_thermal.o
> > > +obj-$(CONFIG_SUN8I_THERMAL)     += sun8i_thermal.o
> > >  obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
> > >  obj-$(CONFIG_RCAR_THERMAL)     += rcar_thermal.o
> > >  obj-$(CONFIG_RCAR_GEN3_THERMAL)        += rcar_gen3_thermal.o
> > > diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
> > > new file mode 100644
> > > index 000000000000..e86b64f51196
> > > --- /dev/null
> > > +++ b/drivers/thermal/sun8i_thermal.c
> > > @@ -0,0 +1,643 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Thermal sensor driver for Allwinner SOC
> > > + * Copyright (C) 2019 Yangtao Li
> > > + *
> > > + * Based on the work of Icenowy Zheng <icenowy@aosc.io>
> > > + * Based on the work of Ondrej Jirman <megous@megous.com>
> > > + * Based on the work of Josef Gajdusek <atx@atx.name>
> > > + */
> > > +
> > > +#include <linux/clk.h>
> > > +#include <linux/device.h>
> > > +#include <linux/interrupt.h>
> > > +#include <linux/module.h>
> > > +#include <linux/nvmem-consumer.h>
> > > +#include <linux/of_device.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/regmap.h>
> > > +#include <linux/reset.h>
> > > +#include <linux/slab.h>
> > > +#include <linux/thermal.h>
> > > +
> > > +#define MAX_SENSOR_NUM 4
> > > +
> > > +#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)
> > > +
> > > +/* millidegree celsius */
> > > +#define THS_EFUSE_CP_FT_MASK                   0x3000
> > > +#define THS_EFUSE_CP_FT_BIT                    12
> > > +#define THS_CALIBRATION_IN_FT                  1
> > > +
> > > +struct ths_device;
> > > +
> > > +struct tsensor {
> > > +       struct ths_device               *tmdev;
> > > +       struct thermal_zone_device      *tzd;
> > > +       int                             id;
> > > +};
> > > +
> > > +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             (*calibrate)(struct ths_device *tmdev,
> > > +                                    u16 *caldata, int callen);
> > > +       int             (*init)(struct ths_device *tmdev);
> > > +       int             (*irq_ack)(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 device                           *dev;
> > > +       struct regmap                           *regmap;
> > > +       struct reset_control                    *reset;
> > > +       struct clk                              *bus_clk;
> > > +       struct clk                              *mod_clk;
> > > +       struct tsensor                          sensor[MAX_SENSOR_NUM];
> > > +       u32                                     cp_ft_flag;
> > > +};
> > > +
> > > +/* 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(void *data, int *temp)
> > > +{
> > > +       struct tsensor *s = data;
> > > +       struct ths_device *tmdev = s->tmdev;
> > > +       int val = 0;
> > > +
> > > +       regmap_read(tmdev->regmap, tmdev->chip->temp_data_base +
> > > +                   0x4 * s->id, &val);
> > > +
> > > +       /* ths have no data yet */
> > > +       if (!val)
> > > +               return -EAGAIN;
> > > +
> > > +       *temp = tmdev->chip->calc_temp(tmdev, s->id, 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 const struct thermal_zone_of_device_ops ths_ops = {
> > > +       .get_temp = sun8i_ths_get_temp,
> > > +};
> > > +
> > > +static const struct regmap_config config = {
> > > +       .reg_bits = 32,
> > > +       .val_bits = 32,
> > > +       .reg_stride = 4,
> > > +       .fast_io = true,
> > > +       .max_register = 0xfc,
> > > +};
> > > +
> > > +static int sun8i_h3_irq_ack(struct ths_device *tmdev)
> > > +{
> > > +       int i, state, ret = 0;
> > > +
> > > +       regmap_read(tmdev->regmap, SUN8I_THS_IS, &state);
> > > +
> > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > +               if (state & SUN8I_THS_DATA_IRQ_STS(i)) {
> > > +                       regmap_write(tmdev->regmap, SUN8I_THS_IS,
> > > +                                    SUN8I_THS_DATA_IRQ_STS(i));
> > > +                       ret |= BIT(i);
> > > +               }
> > > +       }
> > > +
> > > +       return ret;
> > > +}
> > > +
> > > +static int sun50i_h6_irq_ack(struct ths_device *tmdev)
> > > +{
> > > +       int i, state, ret = 0;
> > > +
> > > +       regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state);
> > > +
> > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > +               if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
> > > +                       regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS,
> > > +                                    SUN50I_H6_THS_DATA_IRQ_STS(i));
> > > +                       ret |= BIT(i);
> > > +               }
> > > +       }
> > > +
> > > +       return ret;
> > > +}
> > > +
> > > +static irqreturn_t sun8i_irq_thread(int irq, void *data)
> > > +{
> > > +       struct ths_device *tmdev = data;
> > > +       int i, state;
> > > +
> > > +       state = tmdev->chip->irq_ack(tmdev);
> > > +
> > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > +               if (state & BIT(i))
> > > +                       thermal_zone_device_update(tmdev->sensor[i].tzd,
> > > +                                                  THERMAL_EVENT_UNSPECIFIED);
> > > +       }
> > > +
> > > +       return IRQ_HANDLED;
> > > +}
> > > +
> > > +static int sun8i_h3_ths_calibrate(struct ths_device *tmdev,
> > > +                                 u16 *caldata, int callen)
> > > +{
> > > +       int i;
> > > +
> > > +       if (!caldata[0] || callen < 2 * tmdev->chip->sensor_num)
> > > +               return -EINVAL;
> > > +
> > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > +               int offset = (i % 2) << 4;
> > > +
> > > +               regmap_update_bits(tmdev->regmap,
> > > +                                  SUN8I_THS_TEMP_CALIB + (4 * (i >> 1)),
> > > +                                  0xfff << offset,
> > > +                                  caldata[i] << offset);
> > > +       }
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +static int sun50i_h6_ths_calibrate(struct ths_device *tmdev,
> > > +                                  u16 *caldata, int callen)
> > > +{
> > > +       struct device *dev = tmdev->dev;
> > > +       int i, ft_temp;
> > > +
> > > +       if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num)
> > > +               return -EINVAL;
> > > +
> > > +       /*
> > > +        * efuse layout:
> > > +        *
> > > +        *      0   11  16       32
> > > +        *      +-------+-------+-------+
> > > +        *      |temp|  |sensor0|sensor1|
> > > +        *      +-------+-------+-------+
> > > +        *
> > > +        * The calibration data on the H6 is the ambient temperature and
> > > +        * sensor values that are filled during the factory test stage.
> > > +        *
> > > +        * The unit of stored FT temperature is 0.1 degreee celusis.
> > > +        *
> > > +        * We need to calculate a delta between measured and caluclated
> > > +        * register values and this will become a calibration offset.
> > > +        */
> > > +       ft_temp = (caldata[0] & FT_TEMP_MASK) * 100;
> > > +       tmdev->cp_ft_flag = (caldata[0] & THS_EFUSE_CP_FT_MASK)
> > > +               >> THS_EFUSE_CP_FT_BIT;
> >
> > Here got an unused data "cp_ft_flag",
> > which is used in the calculation of the temperature function according
> > to the source code.
> >
> > https://github.com/orangepi-xunlong/OrangePiH6_kernel/blob/master/drivers/thermal/sunxi_thermal/sunxi_ths_core.c#L392
>
> It should be used to enable the addition of ft_deviation in
> sun8i_ths_get_temp(), I guess. Probably an ommission. I guess most of H6
> chips will have this set anyway.
>
> > > +
> > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > +               int sensor_reg = caldata[i + 1];
> > > +               int cdata, offset;
> > > +               int sensor_temp = tmdev->chip->calc_temp(tmdev, i, sensor_reg);
> > > +
> > > +               /*
> > > +                * Calibration data is CALIBRATE_DEFAULT - (calculated
> > > +                * temperature from sensor reading at factory temperature
> > > +                * minus actual factory temperature) * 14.88 (scale from
> > > +                * temperature to register values)
> > > +                */
> > > +               cdata = CALIBRATE_DEFAULT -
> > > +                       ((sensor_temp - ft_temp) * 10 / tmdev->chip->scale);
> >
> > Why change the formula here.
> >
> > https://github.com/orangepi-xunlong/OrangePiH6_kernel/blob/master/drivers/thermal/sunxi_thermal/sunxi_ths_core.c#L339
>

HI,

I suggest keeping you as it was before this patch, just change this
place to this.

cdata = CALIBRATE_DEFAULT - ((ft_temp * 100 - sensor_temp) /
tmdev->chip->scale);

Yangtao

> It looks the same to me as before. Here's the original patch:
>
> https://megous.com/git/linux/commit/?h=ths-5.4&id=cae28a7dfa6fc79ba37e31d4dff281947247e822
>
> It's basicaly to avoid magic values TEMP_TO_REG and use what's defined
> in the chip struct.
>
> regards,
>         o.
>
> > > +               if (cdata & ~TEMP_CALIB_MASK) {
> > > +                       /*
> > > +                        * Calibration value more than 12-bit, but calibration
> > > +                        * register is 12-bit. In this case, ths hardware can
> > > +                        * still work without calibration, although the data
> > > +                        * won't be so accurate.
> > > +                        */
> > > +                       dev_warn(dev, "sensor%d is not calibrated.\n", i);
> > > +                       continue;
> > > +               }
> > > +
> > > +               offset = (i % 2) * 16;
> > > +               regmap_update_bits(tmdev->regmap,
> > > +                                  SUN50I_H6_THS_TEMP_CALIB + (i / 2 * 4),
> > > +                                  0xfff << offset,
> > > +                                  cdata << offset);
> > > +       }
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +static int sun8i_ths_calibrate(struct ths_device *tmdev)
> > > +{
> > > +       struct nvmem_cell *calcell;
> > > +       struct device *dev = tmdev->dev;
> > > +       u16 *caldata;
> > > +       size_t callen;
> > > +       int ret = 0;
> > > +
> > > +       calcell = devm_nvmem_cell_get(dev, "calibration");
> > > +       if (IS_ERR(calcell)) {
> > > +               if (PTR_ERR(calcell) == -EPROBE_DEFER)
> > > +                       return -EPROBE_DEFER;
> > > +               /*
> > > +                * Even if the external calibration data stored in sid is
> > > +                * not accessible, the THS hardware can still work, although
> > > +                * the data won't be so accurate.
> > > +                *
> > > +                * The default value of calibration register is 0x800 for
> > > +                * every sensor, and the calibration value is usually 0x7xx
> > > +                * or 0x8xx, so they won't be away from the default value
> > > +                * for a lot.
> > > +                *
> > > +                * So here we do not return error if the calibartion data is
> > > +                * not available, except the probe needs deferring.
> > > +                */
> > > +               goto out;
> > > +       }
> > > +
> > > +       caldata = nvmem_cell_read(calcell, &callen);
> > > +       if (IS_ERR(caldata)) {
> > > +               ret = PTR_ERR(caldata);
> > > +               goto out;
> > > +       }
> > > +
> > > +       tmdev->chip->calibrate(tmdev, caldata, callen);
> > > +
> > > +       kfree(caldata);
> > > +out:
> > > +       return ret;
> > > +}
> > > +
> > > +static int sun8i_ths_resource_init(struct ths_device *tmdev)
> > > +{
> > > +       struct device *dev = tmdev->dev;
> > > +       struct platform_device *pdev = to_platform_device(dev);
> > > +       struct resource *mem;
> > > +       void __iomem *base;
> > > +       int ret;
> > > +
> > > +       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > > +       base = devm_ioremap_resource(dev, mem);
> > > +       if (IS_ERR(base))
> > > +               return PTR_ERR(base);
> > > +
> > > +       tmdev->regmap = devm_regmap_init_mmio(dev, base, &config);
> > > +       if (IS_ERR(tmdev->regmap))
> > > +               return PTR_ERR(tmdev->regmap);
> > > +
> > > +       if (tmdev->chip->has_bus_clk_reset) {
> > > +               tmdev->reset = devm_reset_control_get(dev, 0);
> > > +               if (IS_ERR(tmdev->reset))
> > > +                       return PTR_ERR(tmdev->reset);
> > > +
> > > +               tmdev->bus_clk = devm_clk_get(&pdev->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(&pdev->dev, "mod");
> > > +               if (IS_ERR(tmdev->mod_clk))
> > > +                       return PTR_ERR(tmdev->mod_clk);
> > > +       }
> > > +
> > > +       ret = reset_control_deassert(tmdev->reset);
> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       ret = clk_prepare_enable(tmdev->bus_clk);
> > > +       if (ret)
> > > +               goto assert_reset;
> > > +
> > > +       ret = clk_set_rate(tmdev->mod_clk, 24000000);
> > > +       if (ret)
> > > +               goto bus_disable;
> > > +
> > > +       ret = clk_prepare_enable(tmdev->mod_clk);
> > > +       if (ret)
> > > +               goto bus_disable;
> > > +
> > > +       ret = sun8i_ths_calibrate(tmdev);
> > > +       if (ret)
> > > +               goto mod_disable;
> > > +
> > > +       return 0;
> > > +
> > > +mod_disable:
> > > +       clk_disable_unprepare(tmdev->mod_clk);
> > > +bus_disable:
> > > +       clk_disable_unprepare(tmdev->bus_clk);
> > > +assert_reset:
> > > +       reset_control_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 undocummented 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_register(struct ths_device *tmdev)
> > > +{
> > > +       int i;
> > > +
> > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > +               tmdev->sensor[i].tmdev = tmdev;
> > > +               tmdev->sensor[i].id = i;
> > > +               tmdev->sensor[i].tzd =
> > > +                       devm_thermal_zone_of_sensor_register(tmdev->dev,
> > > +                                                            i,
> > > +                                                            &tmdev->sensor[i],
> > > +                                                            &ths_ops);
> > > +               if (IS_ERR(tmdev->sensor[i].tzd))
> > > +                       return PTR_ERR(tmdev->sensor[i].tzd);
> > > +       }
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +static int sun8i_ths_probe(struct platform_device *pdev)
> > > +{
> > > +       struct ths_device *tmdev;
> > > +       struct device *dev = &pdev->dev;
> > > +       int ret, irq;
> > > +
> > > +       tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL);
> > > +       if (!tmdev)
> > > +               return -ENOMEM;
> > > +
> > > +       tmdev->dev = dev;
> > > +       tmdev->chip = of_device_get_match_data(&pdev->dev);
> > > +       if (!tmdev->chip)
> > > +               return -EINVAL;
> > > +
> > > +       platform_set_drvdata(pdev, tmdev);
> > > +
> > > +       ret = sun8i_ths_resource_init(tmdev);
> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       irq = platform_get_irq(pdev, 0);
> > > +       if (irq < 0)
> > > +               return irq;
> > > +
> > > +       ret = tmdev->chip->init(tmdev);
> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       ret = sun8i_ths_register(tmdev);
> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       /*
> > > +        * Avoid entering the interrupt handler, the thermal device is not
> > > +        * registered yet, we deffer the registration of the interrupt to
> > > +        * the end.
> > > +        */
> > > +       ret = devm_request_threaded_irq(dev, irq, NULL,
> > > +                                       sun8i_irq_thread,
> > > +                                       IRQF_ONESHOT, "ths", tmdev);
> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       return ret;
> > > +}
> > > +
> > > +static int sun8i_ths_remove(struct platform_device *pdev)
> > > +{
> > > +       struct ths_device *tmdev = platform_get_drvdata(pdev);
> > > +
> > > +       clk_disable_unprepare(tmdev->mod_clk);
> > > +       clk_disable_unprepare(tmdev->bus_clk);
> > > +       reset_control_assert(tmdev->reset);
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +static const struct ths_thermal_chip sun8i_a83t_ths = {
> > > +       .sensor_num = 3,
> > > +       .scale = 705,
> > > +       .offset = 191668,
> > > +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > +       .init = sun8i_h3_thermal_init,
> > > +       .irq_ack = sun8i_h3_irq_ack,
> > > +       .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,
> > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > +       .init = sun8i_h3_thermal_init,
> > > +       .irq_ack = sun8i_h3_irq_ack,
> > > +       .calc_temp = sun8i_ths_calc_temp,
> > > +};
> > > +
> > > +static const struct ths_thermal_chip sun8i_r40_ths = {
> > > +       .sensor_num = 3,
> > > +       .offset = 251086,
> > > +       .scale = 1130,
> > > +       .has_mod_clk = true,
> > > +       .has_bus_clk_reset = true,
> > > +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > +       .init = sun8i_h3_thermal_init,
> > > +       .irq_ack = sun8i_h3_irq_ack,
> > > +       .calc_temp = sun8i_ths_calc_temp,
> > > +};
> > > +
> > > +static const struct ths_thermal_chip sun50i_a64_ths = {
> > > +       .sensor_num = 3,
> > > +       .offset = 253890,
> > > +       .scale = 1170,
> > > +       .has_mod_clk = true,
> > > +       .has_bus_clk_reset = true,
> > > +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > +       .init = sun8i_h3_thermal_init,
> > > +       .irq_ack = sun8i_h3_irq_ack,
> > > +       .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,
> > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > +       .init = sun8i_h3_thermal_init,
> > > +       .irq_ack = sun8i_h3_irq_ack,
> > > +       .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,
> > > +       .calibrate = sun50i_h6_ths_calibrate,
> > > +       .init = sun50i_h6_thermal_init,
> > > +       .irq_ack = sun50i_h6_irq_ack,
> > > +       .calc_temp = sun8i_ths_calc_temp,
> > > +};
> > > +
> > > +static const struct of_device_id of_ths_match[] = {
> > > +       { .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths },
> > > +       { .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths },
> > > +       { .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths },
> > > +       { .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths },
> > > +       { .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths },
> > > +       { .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths },
> > > +       { /* sentinel */ },
> > > +};
> > > +MODULE_DEVICE_TABLE(of, of_ths_match);
> > > +
> > > +static struct platform_driver ths_driver = {
> > > +       .probe = sun8i_ths_probe,
> > > +       .remove = sun8i_ths_remove,
> > > +       .driver = {
> > > +               .name = "sun8i-thermal",
> > > +               .of_match_table = of_ths_match,
> > > +       },
> > > +};
> > > +module_platform_driver(ths_driver);
> > > +
> > > +MODULE_DESCRIPTION("Thermal sensor driver for Allwinner SOC");
> > > +MODULE_LICENSE("GPL v2");
> > > --
> > > 2.24.0
> > >
> >
> > Thx,
> > Yangtao

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

* Re: [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-11-29 11:46       ` Frank Lee
@ 2019-12-02  1:58         ` Ondřej Jirman
  2019-12-09 18:04           ` Frank Lee
  0 siblings, 1 reply; 31+ messages in thread
From: Ondřej Jirman @ 2019-12-02  1:58 UTC (permalink / raw)
  To: Frank Lee
  Cc: Vasily Khoruzhick, Zhang Rui, Eduardo Valentin, Daniel Lezcano,
	Amit Kucheria, Rob Herring, Mark Rutland, Maxime Ripard,
	Chen-Yu Tsai, Mauro Carvalho Chehab, David S. Miller,
	Greg Kroah-Hartman, Linux PM, devicetree, Linux ARM,
	Linux Kernel Mailing List

On Fri, Nov 29, 2019 at 07:46:58PM +0800, Frank Lee wrote:
> On Fri, Nov 29, 2019 at 1:06 AM Ondřej Jirman <megous@megous.com> wrote:
> >
> > On Fri, Nov 29, 2019 at 12:43:02AM +0800, Frank Lee wrote:
> > > HI,
> > >
> > > I took a closer look at it, and I had some questions about these places.
> > >
> > > On Wed, Nov 27, 2019 at 1:30 PM Vasily Khoruzhick <anarsoul@gmail.com> wrote:
> > > >
> > > > From: Yangtao Li <tiny.windzz@gmail.com>
> > > >
> > > > This patch adds the support for allwinner thermal sensor, within
> > > > allwinner SoC. It will register sensors for thermal framework
> > > > and use device tree to bind cooling device.
> > > >
> > > > Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
> > > > Signed-off-by: Ondrej Jirman <megous@megous.com>
> > > > Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> > > > ---
> > > >  MAINTAINERS                     |   7 +
> > > >  drivers/thermal/Kconfig         |  14 +
> > > >  drivers/thermal/Makefile        |   1 +
> > > >  drivers/thermal/sun8i_thermal.c | 643 ++++++++++++++++++++++++++++++++
> > > >  4 files changed, 665 insertions(+)
> > > >  create mode 100644 drivers/thermal/sun8i_thermal.c
> > > >
> > > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > > index 66cc549ac327..da34f3f2e80b 100644
> > > > --- a/MAINTAINERS
> > > > +++ b/MAINTAINERS
> > > > @@ -688,6 +688,13 @@ L: linux-crypto@vger.kernel.org
> > > >  S:     Maintained
> > > >  F:     drivers/crypto/allwinner/
> > > >
> > > > +ALLWINNER THERMAL DRIVER
> > > > +M:     Yangtao Li <tiny.windzz@gmail.com>
> > > > +L:     linux-pm@vger.kernel.org
> > > > +S:     Maintained
> > > > +F:     Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> > > > +F:     drivers/thermal/sun8i_thermal.c
> > > > +
> > > >  ALLWINNER VPU DRIVER
> > > >  M:     Maxime Ripard <mripard@kernel.org>
> > > >  M:     Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> > > > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> > > > index 001a21abcc28..0b0422e89adb 100644
> > > > --- a/drivers/thermal/Kconfig
> > > > +++ b/drivers/thermal/Kconfig
> > > > @@ -262,6 +262,20 @@ config SPEAR_THERMAL
> > > >           Enable this to plug the SPEAr thermal sensor driver into the Linux
> > > >           thermal framework.
> > > >
> > > > +config SUN8I_THERMAL
> > > > +       tristate "Allwinner sun8i thermal driver"
> > > > +       depends on ARCH_SUNXI || COMPILE_TEST
> > > > +       depends on HAS_IOMEM
> > > > +       depends on NVMEM
> > > > +       depends on OF
> > > > +       depends on RESET_CONTROLLER
> > > > +       help
> > > > +         Support for the sun8i thermal sensor driver into the Linux thermal
> > > > +         framework.
> > > > +
> > > > +         To compile this driver as a module, choose M here: the
> > > > +         module will be called sun8i-thermal.
> > > > +
> > > >  config ROCKCHIP_THERMAL
> > > >         tristate "Rockchip thermal driver"
> > > >         depends on ARCH_ROCKCHIP || COMPILE_TEST
> > > > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> > > > index 74a37c7f847a..fa6f8b206281 100644
> > > > --- a/drivers/thermal/Makefile
> > > > +++ b/drivers/thermal/Makefile
> > > > @@ -31,6 +31,7 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
> > > >  obj-y                          += broadcom/
> > > >  obj-$(CONFIG_THERMAL_MMIO)             += thermal_mmio.o
> > > >  obj-$(CONFIG_SPEAR_THERMAL)    += spear_thermal.o
> > > > +obj-$(CONFIG_SUN8I_THERMAL)     += sun8i_thermal.o
> > > >  obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
> > > >  obj-$(CONFIG_RCAR_THERMAL)     += rcar_thermal.o
> > > >  obj-$(CONFIG_RCAR_GEN3_THERMAL)        += rcar_gen3_thermal.o
> > > > diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
> > > > new file mode 100644
> > > > index 000000000000..e86b64f51196
> > > > --- /dev/null
> > > > +++ b/drivers/thermal/sun8i_thermal.c
> > > > @@ -0,0 +1,643 @@
> > > > +// SPDX-License-Identifier: GPL-2.0
> > > > +/*
> > > > + * Thermal sensor driver for Allwinner SOC
> > > > + * Copyright (C) 2019 Yangtao Li
> > > > + *
> > > > + * Based on the work of Icenowy Zheng <icenowy@aosc.io>
> > > > + * Based on the work of Ondrej Jirman <megous@megous.com>
> > > > + * Based on the work of Josef Gajdusek <atx@atx.name>
> > > > + */
> > > > +
> > > > +#include <linux/clk.h>
> > > > +#include <linux/device.h>
> > > > +#include <linux/interrupt.h>
> > > > +#include <linux/module.h>
> > > > +#include <linux/nvmem-consumer.h>
> > > > +#include <linux/of_device.h>
> > > > +#include <linux/platform_device.h>
> > > > +#include <linux/regmap.h>
> > > > +#include <linux/reset.h>
> > > > +#include <linux/slab.h>
> > > > +#include <linux/thermal.h>
> > > > +
> > > > +#define MAX_SENSOR_NUM 4
> > > > +
> > > > +#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)
> > > > +
> > > > +/* millidegree celsius */
> > > > +#define THS_EFUSE_CP_FT_MASK                   0x3000
> > > > +#define THS_EFUSE_CP_FT_BIT                    12
> > > > +#define THS_CALIBRATION_IN_FT                  1
> > > > +
> > > > +struct ths_device;
> > > > +
> > > > +struct tsensor {
> > > > +       struct ths_device               *tmdev;
> > > > +       struct thermal_zone_device      *tzd;
> > > > +       int                             id;
> > > > +};
> > > > +
> > > > +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             (*calibrate)(struct ths_device *tmdev,
> > > > +                                    u16 *caldata, int callen);
> > > > +       int             (*init)(struct ths_device *tmdev);
> > > > +       int             (*irq_ack)(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 device                           *dev;
> > > > +       struct regmap                           *regmap;
> > > > +       struct reset_control                    *reset;
> > > > +       struct clk                              *bus_clk;
> > > > +       struct clk                              *mod_clk;
> > > > +       struct tsensor                          sensor[MAX_SENSOR_NUM];
> > > > +       u32                                     cp_ft_flag;
> > > > +};
> > > > +
> > > > +/* 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(void *data, int *temp)
> > > > +{
> > > > +       struct tsensor *s = data;
> > > > +       struct ths_device *tmdev = s->tmdev;
> > > > +       int val = 0;
> > > > +
> > > > +       regmap_read(tmdev->regmap, tmdev->chip->temp_data_base +
> > > > +                   0x4 * s->id, &val);
> > > > +
> > > > +       /* ths have no data yet */
> > > > +       if (!val)
> > > > +               return -EAGAIN;
> > > > +
> > > > +       *temp = tmdev->chip->calc_temp(tmdev, s->id, 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 const struct thermal_zone_of_device_ops ths_ops = {
> > > > +       .get_temp = sun8i_ths_get_temp,
> > > > +};
> > > > +
> > > > +static const struct regmap_config config = {
> > > > +       .reg_bits = 32,
> > > > +       .val_bits = 32,
> > > > +       .reg_stride = 4,
> > > > +       .fast_io = true,
> > > > +       .max_register = 0xfc,
> > > > +};
> > > > +
> > > > +static int sun8i_h3_irq_ack(struct ths_device *tmdev)
> > > > +{
> > > > +       int i, state, ret = 0;
> > > > +
> > > > +       regmap_read(tmdev->regmap, SUN8I_THS_IS, &state);
> > > > +
> > > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > > +               if (state & SUN8I_THS_DATA_IRQ_STS(i)) {
> > > > +                       regmap_write(tmdev->regmap, SUN8I_THS_IS,
> > > > +                                    SUN8I_THS_DATA_IRQ_STS(i));
> > > > +                       ret |= BIT(i);
> > > > +               }
> > > > +       }
> > > > +
> > > > +       return ret;
> > > > +}
> > > > +
> > > > +static int sun50i_h6_irq_ack(struct ths_device *tmdev)
> > > > +{
> > > > +       int i, state, ret = 0;
> > > > +
> > > > +       regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state);
> > > > +
> > > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > > +               if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
> > > > +                       regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS,
> > > > +                                    SUN50I_H6_THS_DATA_IRQ_STS(i));
> > > > +                       ret |= BIT(i);
> > > > +               }
> > > > +       }
> > > > +
> > > > +       return ret;
> > > > +}
> > > > +
> > > > +static irqreturn_t sun8i_irq_thread(int irq, void *data)
> > > > +{
> > > > +       struct ths_device *tmdev = data;
> > > > +       int i, state;
> > > > +
> > > > +       state = tmdev->chip->irq_ack(tmdev);
> > > > +
> > > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > > +               if (state & BIT(i))
> > > > +                       thermal_zone_device_update(tmdev->sensor[i].tzd,
> > > > +                                                  THERMAL_EVENT_UNSPECIFIED);
> > > > +       }
> > > > +
> > > > +       return IRQ_HANDLED;
> > > > +}
> > > > +
> > > > +static int sun8i_h3_ths_calibrate(struct ths_device *tmdev,
> > > > +                                 u16 *caldata, int callen)
> > > > +{
> > > > +       int i;
> > > > +
> > > > +       if (!caldata[0] || callen < 2 * tmdev->chip->sensor_num)
> > > > +               return -EINVAL;
> > > > +
> > > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > > +               int offset = (i % 2) << 4;
> > > > +
> > > > +               regmap_update_bits(tmdev->regmap,
> > > > +                                  SUN8I_THS_TEMP_CALIB + (4 * (i >> 1)),
> > > > +                                  0xfff << offset,
> > > > +                                  caldata[i] << offset);
> > > > +       }
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static int sun50i_h6_ths_calibrate(struct ths_device *tmdev,
> > > > +                                  u16 *caldata, int callen)
> > > > +{
> > > > +       struct device *dev = tmdev->dev;
> > > > +       int i, ft_temp;
> > > > +
> > > > +       if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num)
> > > > +               return -EINVAL;
> > > > +
> > > > +       /*
> > > > +        * efuse layout:
> > > > +        *
> > > > +        *      0   11  16       32
> > > > +        *      +-------+-------+-------+
> > > > +        *      |temp|  |sensor0|sensor1|
> > > > +        *      +-------+-------+-------+
> > > > +        *
> > > > +        * The calibration data on the H6 is the ambient temperature and
> > > > +        * sensor values that are filled during the factory test stage.
> > > > +        *
> > > > +        * The unit of stored FT temperature is 0.1 degreee celusis.
> > > > +        *
> > > > +        * We need to calculate a delta between measured and caluclated
> > > > +        * register values and this will become a calibration offset.
> > > > +        */
> > > > +       ft_temp = (caldata[0] & FT_TEMP_MASK) * 100;
> > > > +       tmdev->cp_ft_flag = (caldata[0] & THS_EFUSE_CP_FT_MASK)
> > > > +               >> THS_EFUSE_CP_FT_BIT;
> > >
> > > Here got an unused data "cp_ft_flag",
> > > which is used in the calculation of the temperature function according
> > > to the source code.
> > >
> > > https://github.com/orangepi-xunlong/OrangePiH6_kernel/blob/master/drivers/thermal/sunxi_thermal/sunxi_ths_core.c#L392
> >
> > It should be used to enable the addition of ft_deviation in
> > sun8i_ths_get_temp(), I guess. Probably an ommission. I guess most of H6
> > chips will have this set anyway.
> >
> > > > +
> > > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > > +               int sensor_reg = caldata[i + 1];
> > > > +               int cdata, offset;
> > > > +               int sensor_temp = tmdev->chip->calc_temp(tmdev, i, sensor_reg);
> > > > +
> > > > +               /*
> > > > +                * Calibration data is CALIBRATE_DEFAULT - (calculated
> > > > +                * temperature from sensor reading at factory temperature
> > > > +                * minus actual factory temperature) * 14.88 (scale from
> > > > +                * temperature to register values)
> > > > +                */
> > > > +               cdata = CALIBRATE_DEFAULT -
> > > > +                       ((sensor_temp - ft_temp) * 10 / tmdev->chip->scale);
> > >
> > > Why change the formula here.
> > >
> > > https://github.com/orangepi-xunlong/OrangePiH6_kernel/blob/master/drivers/thermal/sunxi_thermal/sunxi_ths_core.c#L339
> >
> 
> HI,
> 
> I suggest keeping you as it was before this patch, just change this
> place to this.
> 
> cdata = CALIBRATE_DEFAULT - ((ft_temp * 100 - sensor_temp) /
> tmdev->chip->scale);

Hi,

any reason for your suggestion? I also changed scale to be 10* specifically to
allow it to be more precise when doing integer math, so that it more easily
supports A83T's value. H6's scale value is also needlessly rounded too much
without this change. With your suggestion, I'd have to round A83T's scale from
70.5 to either 70 or 71, but why?

regards,
	o.

> Yangtao
> 
> > It looks the same to me as before. Here's the original patch:
> >
> > https://megous.com/git/linux/commit/?h=ths-5.4&id=cae28a7dfa6fc79ba37e31d4dff281947247e822
> >
> > It's basicaly to avoid magic values TEMP_TO_REG and use what's defined
> > in the chip struct.
> >
> > regards,
> >         o.
> >
> > > > +               if (cdata & ~TEMP_CALIB_MASK) {
> > > > +                       /*
> > > > +                        * Calibration value more than 12-bit, but calibration
> > > > +                        * register is 12-bit. In this case, ths hardware can
> > > > +                        * still work without calibration, although the data
> > > > +                        * won't be so accurate.
> > > > +                        */
> > > > +                       dev_warn(dev, "sensor%d is not calibrated.\n", i);
> > > > +                       continue;
> > > > +               }
> > > > +
> > > > +               offset = (i % 2) * 16;
> > > > +               regmap_update_bits(tmdev->regmap,
> > > > +                                  SUN50I_H6_THS_TEMP_CALIB + (i / 2 * 4),
> > > > +                                  0xfff << offset,
> > > > +                                  cdata << offset);
> > > > +       }
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static int sun8i_ths_calibrate(struct ths_device *tmdev)
> > > > +{
> > > > +       struct nvmem_cell *calcell;
> > > > +       struct device *dev = tmdev->dev;
> > > > +       u16 *caldata;
> > > > +       size_t callen;
> > > > +       int ret = 0;
> > > > +
> > > > +       calcell = devm_nvmem_cell_get(dev, "calibration");
> > > > +       if (IS_ERR(calcell)) {
> > > > +               if (PTR_ERR(calcell) == -EPROBE_DEFER)
> > > > +                       return -EPROBE_DEFER;
> > > > +               /*
> > > > +                * Even if the external calibration data stored in sid is
> > > > +                * not accessible, the THS hardware can still work, although
> > > > +                * the data won't be so accurate.
> > > > +                *
> > > > +                * The default value of calibration register is 0x800 for
> > > > +                * every sensor, and the calibration value is usually 0x7xx
> > > > +                * or 0x8xx, so they won't be away from the default value
> > > > +                * for a lot.
> > > > +                *
> > > > +                * So here we do not return error if the calibartion data is
> > > > +                * not available, except the probe needs deferring.
> > > > +                */
> > > > +               goto out;
> > > > +       }
> > > > +
> > > > +       caldata = nvmem_cell_read(calcell, &callen);
> > > > +       if (IS_ERR(caldata)) {
> > > > +               ret = PTR_ERR(caldata);
> > > > +               goto out;
> > > > +       }
> > > > +
> > > > +       tmdev->chip->calibrate(tmdev, caldata, callen);
> > > > +
> > > > +       kfree(caldata);
> > > > +out:
> > > > +       return ret;
> > > > +}
> > > > +
> > > > +static int sun8i_ths_resource_init(struct ths_device *tmdev)
> > > > +{
> > > > +       struct device *dev = tmdev->dev;
> > > > +       struct platform_device *pdev = to_platform_device(dev);
> > > > +       struct resource *mem;
> > > > +       void __iomem *base;
> > > > +       int ret;
> > > > +
> > > > +       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > > > +       base = devm_ioremap_resource(dev, mem);
> > > > +       if (IS_ERR(base))
> > > > +               return PTR_ERR(base);
> > > > +
> > > > +       tmdev->regmap = devm_regmap_init_mmio(dev, base, &config);
> > > > +       if (IS_ERR(tmdev->regmap))
> > > > +               return PTR_ERR(tmdev->regmap);
> > > > +
> > > > +       if (tmdev->chip->has_bus_clk_reset) {
> > > > +               tmdev->reset = devm_reset_control_get(dev, 0);
> > > > +               if (IS_ERR(tmdev->reset))
> > > > +                       return PTR_ERR(tmdev->reset);
> > > > +
> > > > +               tmdev->bus_clk = devm_clk_get(&pdev->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(&pdev->dev, "mod");
> > > > +               if (IS_ERR(tmdev->mod_clk))
> > > > +                       return PTR_ERR(tmdev->mod_clk);
> > > > +       }
> > > > +
> > > > +       ret = reset_control_deassert(tmdev->reset);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       ret = clk_prepare_enable(tmdev->bus_clk);
> > > > +       if (ret)
> > > > +               goto assert_reset;
> > > > +
> > > > +       ret = clk_set_rate(tmdev->mod_clk, 24000000);
> > > > +       if (ret)
> > > > +               goto bus_disable;
> > > > +
> > > > +       ret = clk_prepare_enable(tmdev->mod_clk);
> > > > +       if (ret)
> > > > +               goto bus_disable;
> > > > +
> > > > +       ret = sun8i_ths_calibrate(tmdev);
> > > > +       if (ret)
> > > > +               goto mod_disable;
> > > > +
> > > > +       return 0;
> > > > +
> > > > +mod_disable:
> > > > +       clk_disable_unprepare(tmdev->mod_clk);
> > > > +bus_disable:
> > > > +       clk_disable_unprepare(tmdev->bus_clk);
> > > > +assert_reset:
> > > > +       reset_control_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 undocummented 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_register(struct ths_device *tmdev)
> > > > +{
> > > > +       int i;
> > > > +
> > > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > > +               tmdev->sensor[i].tmdev = tmdev;
> > > > +               tmdev->sensor[i].id = i;
> > > > +               tmdev->sensor[i].tzd =
> > > > +                       devm_thermal_zone_of_sensor_register(tmdev->dev,
> > > > +                                                            i,
> > > > +                                                            &tmdev->sensor[i],
> > > > +                                                            &ths_ops);
> > > > +               if (IS_ERR(tmdev->sensor[i].tzd))
> > > > +                       return PTR_ERR(tmdev->sensor[i].tzd);
> > > > +       }
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static int sun8i_ths_probe(struct platform_device *pdev)
> > > > +{
> > > > +       struct ths_device *tmdev;
> > > > +       struct device *dev = &pdev->dev;
> > > > +       int ret, irq;
> > > > +
> > > > +       tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL);
> > > > +       if (!tmdev)
> > > > +               return -ENOMEM;
> > > > +
> > > > +       tmdev->dev = dev;
> > > > +       tmdev->chip = of_device_get_match_data(&pdev->dev);
> > > > +       if (!tmdev->chip)
> > > > +               return -EINVAL;
> > > > +
> > > > +       platform_set_drvdata(pdev, tmdev);
> > > > +
> > > > +       ret = sun8i_ths_resource_init(tmdev);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       irq = platform_get_irq(pdev, 0);
> > > > +       if (irq < 0)
> > > > +               return irq;
> > > > +
> > > > +       ret = tmdev->chip->init(tmdev);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       ret = sun8i_ths_register(tmdev);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       /*
> > > > +        * Avoid entering the interrupt handler, the thermal device is not
> > > > +        * registered yet, we deffer the registration of the interrupt to
> > > > +        * the end.
> > > > +        */
> > > > +       ret = devm_request_threaded_irq(dev, irq, NULL,
> > > > +                                       sun8i_irq_thread,
> > > > +                                       IRQF_ONESHOT, "ths", tmdev);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       return ret;
> > > > +}
> > > > +
> > > > +static int sun8i_ths_remove(struct platform_device *pdev)
> > > > +{
> > > > +       struct ths_device *tmdev = platform_get_drvdata(pdev);
> > > > +
> > > > +       clk_disable_unprepare(tmdev->mod_clk);
> > > > +       clk_disable_unprepare(tmdev->bus_clk);
> > > > +       reset_control_assert(tmdev->reset);
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static const struct ths_thermal_chip sun8i_a83t_ths = {
> > > > +       .sensor_num = 3,
> > > > +       .scale = 705,
> > > > +       .offset = 191668,
> > > > +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> > > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > > +       .init = sun8i_h3_thermal_init,
> > > > +       .irq_ack = sun8i_h3_irq_ack,
> > > > +       .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,
> > > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > > +       .init = sun8i_h3_thermal_init,
> > > > +       .irq_ack = sun8i_h3_irq_ack,
> > > > +       .calc_temp = sun8i_ths_calc_temp,
> > > > +};
> > > > +
> > > > +static const struct ths_thermal_chip sun8i_r40_ths = {
> > > > +       .sensor_num = 3,
> > > > +       .offset = 251086,
> > > > +       .scale = 1130,
> > > > +       .has_mod_clk = true,
> > > > +       .has_bus_clk_reset = true,
> > > > +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> > > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > > +       .init = sun8i_h3_thermal_init,
> > > > +       .irq_ack = sun8i_h3_irq_ack,
> > > > +       .calc_temp = sun8i_ths_calc_temp,
> > > > +};
> > > > +
> > > > +static const struct ths_thermal_chip sun50i_a64_ths = {
> > > > +       .sensor_num = 3,
> > > > +       .offset = 253890,
> > > > +       .scale = 1170,
> > > > +       .has_mod_clk = true,
> > > > +       .has_bus_clk_reset = true,
> > > > +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> > > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > > +       .init = sun8i_h3_thermal_init,
> > > > +       .irq_ack = sun8i_h3_irq_ack,
> > > > +       .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,
> > > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > > +       .init = sun8i_h3_thermal_init,
> > > > +       .irq_ack = sun8i_h3_irq_ack,
> > > > +       .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,
> > > > +       .calibrate = sun50i_h6_ths_calibrate,
> > > > +       .init = sun50i_h6_thermal_init,
> > > > +       .irq_ack = sun50i_h6_irq_ack,
> > > > +       .calc_temp = sun8i_ths_calc_temp,
> > > > +};
> > > > +
> > > > +static const struct of_device_id of_ths_match[] = {
> > > > +       { .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths },
> > > > +       { .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths },
> > > > +       { .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths },
> > > > +       { .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths },
> > > > +       { .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths },
> > > > +       { .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths },
> > > > +       { /* sentinel */ },
> > > > +};
> > > > +MODULE_DEVICE_TABLE(of, of_ths_match);
> > > > +
> > > > +static struct platform_driver ths_driver = {
> > > > +       .probe = sun8i_ths_probe,
> > > > +       .remove = sun8i_ths_remove,
> > > > +       .driver = {
> > > > +               .name = "sun8i-thermal",
> > > > +               .of_match_table = of_ths_match,
> > > > +       },
> > > > +};
> > > > +module_platform_driver(ths_driver);
> > > > +
> > > > +MODULE_DESCRIPTION("Thermal sensor driver for Allwinner SOC");
> > > > +MODULE_LICENSE("GPL v2");
> > > > --
> > > > 2.24.0
> > > >
> > >
> > > Thx,
> > > Yangtao
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40
  2019-12-02  1:58         ` Ondřej Jirman
@ 2019-12-09 18:04           ` Frank Lee
  0 siblings, 0 replies; 31+ messages in thread
From: Frank Lee @ 2019-12-09 18:04 UTC (permalink / raw)
  To: Frank Lee, Vasily Khoruzhick, Zhang Rui, Eduardo Valentin,
	Daniel Lezcano, Amit Kucheria, Rob Herring, Mark Rutland,
	Maxime Ripard, Chen-Yu Tsai, Mauro Carvalho Chehab,
	David S. Miller, Greg Kroah-Hartman, Linux PM, devicetree,
	Linux ARM, Linux Kernel Mailing List

On Mon, Dec 2, 2019 at 9:58 AM Ondřej Jirman <megous@megous.com> wrote:
>
> On Fri, Nov 29, 2019 at 07:46:58PM +0800, Frank Lee wrote:
> > On Fri, Nov 29, 2019 at 1:06 AM Ondřej Jirman <megous@megous.com> wrote:
> > >
> > > On Fri, Nov 29, 2019 at 12:43:02AM +0800, Frank Lee wrote:
> > > > HI,
> > > >
> > > > I took a closer look at it, and I had some questions about these places.
> > > >
> > > > On Wed, Nov 27, 2019 at 1:30 PM Vasily Khoruzhick <anarsoul@gmail.com> wrote:
> > > > >
> > > > > From: Yangtao Li <tiny.windzz@gmail.com>
> > > > >
> > > > > This patch adds the support for allwinner thermal sensor, within
> > > > > allwinner SoC. It will register sensors for thermal framework
> > > > > and use device tree to bind cooling device.
> > > > >
> > > > > Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
> > > > > Signed-off-by: Ondrej Jirman <megous@megous.com>
> > > > > Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> > > > > ---
> > > > >  MAINTAINERS                     |   7 +
> > > > >  drivers/thermal/Kconfig         |  14 +
> > > > >  drivers/thermal/Makefile        |   1 +
> > > > >  drivers/thermal/sun8i_thermal.c | 643 ++++++++++++++++++++++++++++++++
> > > > >  4 files changed, 665 insertions(+)
> > > > >  create mode 100644 drivers/thermal/sun8i_thermal.c
> > > > >
> > > > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > > > index 66cc549ac327..da34f3f2e80b 100644
> > > > > --- a/MAINTAINERS
> > > > > +++ b/MAINTAINERS
> > > > > @@ -688,6 +688,13 @@ L: linux-crypto@vger.kernel.org
> > > > >  S:     Maintained
> > > > >  F:     drivers/crypto/allwinner/
> > > > >
> > > > > +ALLWINNER THERMAL DRIVER
> > > > > +M:     Yangtao Li <tiny.windzz@gmail.com>
> > > > > +L:     linux-pm@vger.kernel.org
> > > > > +S:     Maintained
> > > > > +F:     Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
> > > > > +F:     drivers/thermal/sun8i_thermal.c
> > > > > +
> > > > >  ALLWINNER VPU DRIVER
> > > > >  M:     Maxime Ripard <mripard@kernel.org>
> > > > >  M:     Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> > > > > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> > > > > index 001a21abcc28..0b0422e89adb 100644
> > > > > --- a/drivers/thermal/Kconfig
> > > > > +++ b/drivers/thermal/Kconfig
> > > > > @@ -262,6 +262,20 @@ config SPEAR_THERMAL
> > > > >           Enable this to plug the SPEAr thermal sensor driver into the Linux
> > > > >           thermal framework.
> > > > >
> > > > > +config SUN8I_THERMAL
> > > > > +       tristate "Allwinner sun8i thermal driver"
> > > > > +       depends on ARCH_SUNXI || COMPILE_TEST
> > > > > +       depends on HAS_IOMEM
> > > > > +       depends on NVMEM
> > > > > +       depends on OF
> > > > > +       depends on RESET_CONTROLLER
> > > > > +       help
> > > > > +         Support for the sun8i thermal sensor driver into the Linux thermal
> > > > > +         framework.
> > > > > +
> > > > > +         To compile this driver as a module, choose M here: the
> > > > > +         module will be called sun8i-thermal.
> > > > > +
> > > > >  config ROCKCHIP_THERMAL
> > > > >         tristate "Rockchip thermal driver"
> > > > >         depends on ARCH_ROCKCHIP || COMPILE_TEST
> > > > > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> > > > > index 74a37c7f847a..fa6f8b206281 100644
> > > > > --- a/drivers/thermal/Makefile
> > > > > +++ b/drivers/thermal/Makefile
> > > > > @@ -31,6 +31,7 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
> > > > >  obj-y                          += broadcom/
> > > > >  obj-$(CONFIG_THERMAL_MMIO)             += thermal_mmio.o
> > > > >  obj-$(CONFIG_SPEAR_THERMAL)    += spear_thermal.o
> > > > > +obj-$(CONFIG_SUN8I_THERMAL)     += sun8i_thermal.o
> > > > >  obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
> > > > >  obj-$(CONFIG_RCAR_THERMAL)     += rcar_thermal.o
> > > > >  obj-$(CONFIG_RCAR_GEN3_THERMAL)        += rcar_gen3_thermal.o
> > > > > diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
> > > > > new file mode 100644
> > > > > index 000000000000..e86b64f51196
> > > > > --- /dev/null
> > > > > +++ b/drivers/thermal/sun8i_thermal.c
> > > > > @@ -0,0 +1,643 @@
> > > > > +// SPDX-License-Identifier: GPL-2.0
> > > > > +/*
> > > > > + * Thermal sensor driver for Allwinner SOC
> > > > > + * Copyright (C) 2019 Yangtao Li
> > > > > + *
> > > > > + * Based on the work of Icenowy Zheng <icenowy@aosc.io>
> > > > > + * Based on the work of Ondrej Jirman <megous@megous.com>
> > > > > + * Based on the work of Josef Gajdusek <atx@atx.name>
> > > > > + */
> > > > > +
> > > > > +#include <linux/clk.h>
> > > > > +#include <linux/device.h>
> > > > > +#include <linux/interrupt.h>
> > > > > +#include <linux/module.h>
> > > > > +#include <linux/nvmem-consumer.h>
> > > > > +#include <linux/of_device.h>
> > > > > +#include <linux/platform_device.h>
> > > > > +#include <linux/regmap.h>
> > > > > +#include <linux/reset.h>
> > > > > +#include <linux/slab.h>
> > > > > +#include <linux/thermal.h>
> > > > > +
> > > > > +#define MAX_SENSOR_NUM 4
> > > > > +
> > > > > +#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)
> > > > > +
> > > > > +/* millidegree celsius */
> > > > > +#define THS_EFUSE_CP_FT_MASK                   0x3000
> > > > > +#define THS_EFUSE_CP_FT_BIT                    12
> > > > > +#define THS_CALIBRATION_IN_FT                  1
> > > > > +
> > > > > +struct ths_device;
> > > > > +
> > > > > +struct tsensor {
> > > > > +       struct ths_device               *tmdev;
> > > > > +       struct thermal_zone_device      *tzd;
> > > > > +       int                             id;
> > > > > +};
> > > > > +
> > > > > +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             (*calibrate)(struct ths_device *tmdev,
> > > > > +                                    u16 *caldata, int callen);
> > > > > +       int             (*init)(struct ths_device *tmdev);
> > > > > +       int             (*irq_ack)(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 device                           *dev;
> > > > > +       struct regmap                           *regmap;
> > > > > +       struct reset_control                    *reset;
> > > > > +       struct clk                              *bus_clk;
> > > > > +       struct clk                              *mod_clk;
> > > > > +       struct tsensor                          sensor[MAX_SENSOR_NUM];
> > > > > +       u32                                     cp_ft_flag;
> > > > > +};
> > > > > +
> > > > > +/* 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(void *data, int *temp)
> > > > > +{
> > > > > +       struct tsensor *s = data;
> > > > > +       struct ths_device *tmdev = s->tmdev;
> > > > > +       int val = 0;
> > > > > +
> > > > > +       regmap_read(tmdev->regmap, tmdev->chip->temp_data_base +
> > > > > +                   0x4 * s->id, &val);
> > > > > +
> > > > > +       /* ths have no data yet */
> > > > > +       if (!val)
> > > > > +               return -EAGAIN;
> > > > > +
> > > > > +       *temp = tmdev->chip->calc_temp(tmdev, s->id, 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 const struct thermal_zone_of_device_ops ths_ops = {
> > > > > +       .get_temp = sun8i_ths_get_temp,
> > > > > +};
> > > > > +
> > > > > +static const struct regmap_config config = {
> > > > > +       .reg_bits = 32,
> > > > > +       .val_bits = 32,
> > > > > +       .reg_stride = 4,
> > > > > +       .fast_io = true,
> > > > > +       .max_register = 0xfc,
> > > > > +};
> > > > > +
> > > > > +static int sun8i_h3_irq_ack(struct ths_device *tmdev)
> > > > > +{
> > > > > +       int i, state, ret = 0;
> > > > > +
> > > > > +       regmap_read(tmdev->regmap, SUN8I_THS_IS, &state);
> > > > > +
> > > > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > > > +               if (state & SUN8I_THS_DATA_IRQ_STS(i)) {
> > > > > +                       regmap_write(tmdev->regmap, SUN8I_THS_IS,
> > > > > +                                    SUN8I_THS_DATA_IRQ_STS(i));
> > > > > +                       ret |= BIT(i);
> > > > > +               }
> > > > > +       }
> > > > > +
> > > > > +       return ret;
> > > > > +}
> > > > > +
> > > > > +static int sun50i_h6_irq_ack(struct ths_device *tmdev)
> > > > > +{
> > > > > +       int i, state, ret = 0;
> > > > > +
> > > > > +       regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state);
> > > > > +
> > > > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > > > +               if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
> > > > > +                       regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS,
> > > > > +                                    SUN50I_H6_THS_DATA_IRQ_STS(i));
> > > > > +                       ret |= BIT(i);
> > > > > +               }
> > > > > +       }
> > > > > +
> > > > > +       return ret;
> > > > > +}
> > > > > +
> > > > > +static irqreturn_t sun8i_irq_thread(int irq, void *data)
> > > > > +{
> > > > > +       struct ths_device *tmdev = data;
> > > > > +       int i, state;
> > > > > +
> > > > > +       state = tmdev->chip->irq_ack(tmdev);
> > > > > +
> > > > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > > > +               if (state & BIT(i))
> > > > > +                       thermal_zone_device_update(tmdev->sensor[i].tzd,
> > > > > +                                                  THERMAL_EVENT_UNSPECIFIED);
> > > > > +       }
> > > > > +
> > > > > +       return IRQ_HANDLED;
> > > > > +}
> > > > > +
> > > > > +static int sun8i_h3_ths_calibrate(struct ths_device *tmdev,
> > > > > +                                 u16 *caldata, int callen)
> > > > > +{
> > > > > +       int i;
> > > > > +
> > > > > +       if (!caldata[0] || callen < 2 * tmdev->chip->sensor_num)
> > > > > +               return -EINVAL;
> > > > > +
> > > > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > > > +               int offset = (i % 2) << 4;
> > > > > +
> > > > > +               regmap_update_bits(tmdev->regmap,
> > > > > +                                  SUN8I_THS_TEMP_CALIB + (4 * (i >> 1)),
> > > > > +                                  0xfff << offset,
> > > > > +                                  caldata[i] << offset);
> > > > > +       }
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static int sun50i_h6_ths_calibrate(struct ths_device *tmdev,
> > > > > +                                  u16 *caldata, int callen)
> > > > > +{
> > > > > +       struct device *dev = tmdev->dev;
> > > > > +       int i, ft_temp;
> > > > > +
> > > > > +       if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num)
> > > > > +               return -EINVAL;
> > > > > +
> > > > > +       /*
> > > > > +        * efuse layout:
> > > > > +        *
> > > > > +        *      0   11  16       32
> > > > > +        *      +-------+-------+-------+
> > > > > +        *      |temp|  |sensor0|sensor1|
> > > > > +        *      +-------+-------+-------+
> > > > > +        *
> > > > > +        * The calibration data on the H6 is the ambient temperature and
> > > > > +        * sensor values that are filled during the factory test stage.
> > > > > +        *
> > > > > +        * The unit of stored FT temperature is 0.1 degreee celusis.
> > > > > +        *
> > > > > +        * We need to calculate a delta between measured and caluclated
> > > > > +        * register values and this will become a calibration offset.
> > > > > +        */
> > > > > +       ft_temp = (caldata[0] & FT_TEMP_MASK) * 100;
> > > > > +       tmdev->cp_ft_flag = (caldata[0] & THS_EFUSE_CP_FT_MASK)
> > > > > +               >> THS_EFUSE_CP_FT_BIT;
> > > >
> > > > Here got an unused data "cp_ft_flag",
> > > > which is used in the calculation of the temperature function according
> > > > to the source code.
> > > >
> > > > https://github.com/orangepi-xunlong/OrangePiH6_kernel/blob/master/drivers/thermal/sunxi_thermal/sunxi_ths_core.c#L392
> > >
> > > It should be used to enable the addition of ft_deviation in
> > > sun8i_ths_get_temp(), I guess. Probably an ommission. I guess most of H6
> > > chips will have this set anyway.
> > >
> > > > > +
> > > > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > > > +               int sensor_reg = caldata[i + 1];
> > > > > +               int cdata, offset;
> > > > > +               int sensor_temp = tmdev->chip->calc_temp(tmdev, i, sensor_reg);
> > > > > +
> > > > > +               /*
> > > > > +                * Calibration data is CALIBRATE_DEFAULT - (calculated
> > > > > +                * temperature from sensor reading at factory temperature
> > > > > +                * minus actual factory temperature) * 14.88 (scale from
> > > > > +                * temperature to register values)
> > > > > +                */
> > > > > +               cdata = CALIBRATE_DEFAULT -
> > > > > +                       ((sensor_temp - ft_temp) * 10 / tmdev->chip->scale);
> > > >
> > > > Why change the formula here.
> > > >
> > > > https://github.com/orangepi-xunlong/OrangePiH6_kernel/blob/master/drivers/thermal/sunxi_thermal/sunxi_ths_core.c#L339
> > >
> >
> > HI,
> >
> > I suggest keeping you as it was before this patch, just change this
> > place to this.
> >
> > cdata = CALIBRATE_DEFAULT - ((ft_temp * 100 - sensor_temp) /
> > tmdev->chip->scale);
>
> Hi,
>
> any reason for your suggestion? I also changed scale to be 10* specifically to
> allow it to be more precise when doing integer math, so that it more easily
> supports A83T's value. H6's scale value is also needlessly rounded too much
> without this change. With your suggestion, I'd have to round A83T's scale from
> 70.5 to either 70 or 71, but why?

The calculation result of this formula, due to the difference of the
chip, the temperature
will not be very accurate, with some deviations(May be a few degrees from actual
temperature). I'm not too keen to keep it exactly the same as the
original formula.
At this time rounding to 71, 70 may not cause the calculated
temperature difference too much.

Yours,
Yangtao

>
> regards,
>         o.
>
> > Yangtao
> >
> > > It looks the same to me as before. Here's the original patch:
> > >
> > > https://megous.com/git/linux/commit/?h=ths-5.4&id=cae28a7dfa6fc79ba37e31d4dff281947247e822
> > >
> > > It's basicaly to avoid magic values TEMP_TO_REG and use what's defined
> > > in the chip struct.
> > >
> > > regards,
> > >         o.
> > >
> > > > > +               if (cdata & ~TEMP_CALIB_MASK) {
> > > > > +                       /*
> > > > > +                        * Calibration value more than 12-bit, but calibration
> > > > > +                        * register is 12-bit. In this case, ths hardware can
> > > > > +                        * still work without calibration, although the data
> > > > > +                        * won't be so accurate.
> > > > > +                        */
> > > > > +                       dev_warn(dev, "sensor%d is not calibrated.\n", i);
> > > > > +                       continue;
> > > > > +               }
> > > > > +
> > > > > +               offset = (i % 2) * 16;
> > > > > +               regmap_update_bits(tmdev->regmap,
> > > > > +                                  SUN50I_H6_THS_TEMP_CALIB + (i / 2 * 4),
> > > > > +                                  0xfff << offset,
> > > > > +                                  cdata << offset);
> > > > > +       }
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static int sun8i_ths_calibrate(struct ths_device *tmdev)
> > > > > +{
> > > > > +       struct nvmem_cell *calcell;
> > > > > +       struct device *dev = tmdev->dev;
> > > > > +       u16 *caldata;
> > > > > +       size_t callen;
> > > > > +       int ret = 0;
> > > > > +
> > > > > +       calcell = devm_nvmem_cell_get(dev, "calibration");
> > > > > +       if (IS_ERR(calcell)) {
> > > > > +               if (PTR_ERR(calcell) == -EPROBE_DEFER)
> > > > > +                       return -EPROBE_DEFER;
> > > > > +               /*
> > > > > +                * Even if the external calibration data stored in sid is
> > > > > +                * not accessible, the THS hardware can still work, although
> > > > > +                * the data won't be so accurate.
> > > > > +                *
> > > > > +                * The default value of calibration register is 0x800 for
> > > > > +                * every sensor, and the calibration value is usually 0x7xx
> > > > > +                * or 0x8xx, so they won't be away from the default value
> > > > > +                * for a lot.
> > > > > +                *
> > > > > +                * So here we do not return error if the calibartion data is
> > > > > +                * not available, except the probe needs deferring.
> > > > > +                */
> > > > > +               goto out;
> > > > > +       }
> > > > > +
> > > > > +       caldata = nvmem_cell_read(calcell, &callen);
> > > > > +       if (IS_ERR(caldata)) {
> > > > > +               ret = PTR_ERR(caldata);
> > > > > +               goto out;
> > > > > +       }
> > > > > +
> > > > > +       tmdev->chip->calibrate(tmdev, caldata, callen);
> > > > > +
> > > > > +       kfree(caldata);
> > > > > +out:
> > > > > +       return ret;
> > > > > +}
> > > > > +
> > > > > +static int sun8i_ths_resource_init(struct ths_device *tmdev)
> > > > > +{
> > > > > +       struct device *dev = tmdev->dev;
> > > > > +       struct platform_device *pdev = to_platform_device(dev);
> > > > > +       struct resource *mem;
> > > > > +       void __iomem *base;
> > > > > +       int ret;
> > > > > +
> > > > > +       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > > > > +       base = devm_ioremap_resource(dev, mem);
> > > > > +       if (IS_ERR(base))
> > > > > +               return PTR_ERR(base);
> > > > > +
> > > > > +       tmdev->regmap = devm_regmap_init_mmio(dev, base, &config);
> > > > > +       if (IS_ERR(tmdev->regmap))
> > > > > +               return PTR_ERR(tmdev->regmap);
> > > > > +
> > > > > +       if (tmdev->chip->has_bus_clk_reset) {
> > > > > +               tmdev->reset = devm_reset_control_get(dev, 0);
> > > > > +               if (IS_ERR(tmdev->reset))
> > > > > +                       return PTR_ERR(tmdev->reset);
> > > > > +
> > > > > +               tmdev->bus_clk = devm_clk_get(&pdev->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(&pdev->dev, "mod");
> > > > > +               if (IS_ERR(tmdev->mod_clk))
> > > > > +                       return PTR_ERR(tmdev->mod_clk);
> > > > > +       }
> > > > > +
> > > > > +       ret = reset_control_deassert(tmdev->reset);
> > > > > +       if (ret)
> > > > > +               return ret;
> > > > > +
> > > > > +       ret = clk_prepare_enable(tmdev->bus_clk);
> > > > > +       if (ret)
> > > > > +               goto assert_reset;
> > > > > +
> > > > > +       ret = clk_set_rate(tmdev->mod_clk, 24000000);
> > > > > +       if (ret)
> > > > > +               goto bus_disable;
> > > > > +
> > > > > +       ret = clk_prepare_enable(tmdev->mod_clk);
> > > > > +       if (ret)
> > > > > +               goto bus_disable;
> > > > > +
> > > > > +       ret = sun8i_ths_calibrate(tmdev);
> > > > > +       if (ret)
> > > > > +               goto mod_disable;
> > > > > +
> > > > > +       return 0;
> > > > > +
> > > > > +mod_disable:
> > > > > +       clk_disable_unprepare(tmdev->mod_clk);
> > > > > +bus_disable:
> > > > > +       clk_disable_unprepare(tmdev->bus_clk);
> > > > > +assert_reset:
> > > > > +       reset_control_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 undocummented 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_register(struct ths_device *tmdev)
> > > > > +{
> > > > > +       int i;
> > > > > +
> > > > > +       for (i = 0; i < tmdev->chip->sensor_num; i++) {
> > > > > +               tmdev->sensor[i].tmdev = tmdev;
> > > > > +               tmdev->sensor[i].id = i;
> > > > > +               tmdev->sensor[i].tzd =
> > > > > +                       devm_thermal_zone_of_sensor_register(tmdev->dev,
> > > > > +                                                            i,
> > > > > +                                                            &tmdev->sensor[i],
> > > > > +                                                            &ths_ops);
> > > > > +               if (IS_ERR(tmdev->sensor[i].tzd))
> > > > > +                       return PTR_ERR(tmdev->sensor[i].tzd);
> > > > > +       }
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static int sun8i_ths_probe(struct platform_device *pdev)
> > > > > +{
> > > > > +       struct ths_device *tmdev;
> > > > > +       struct device *dev = &pdev->dev;
> > > > > +       int ret, irq;
> > > > > +
> > > > > +       tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL);
> > > > > +       if (!tmdev)
> > > > > +               return -ENOMEM;
> > > > > +
> > > > > +       tmdev->dev = dev;
> > > > > +       tmdev->chip = of_device_get_match_data(&pdev->dev);
> > > > > +       if (!tmdev->chip)
> > > > > +               return -EINVAL;
> > > > > +
> > > > > +       platform_set_drvdata(pdev, tmdev);
> > > > > +
> > > > > +       ret = sun8i_ths_resource_init(tmdev);
> > > > > +       if (ret)
> > > > > +               return ret;
> > > > > +
> > > > > +       irq = platform_get_irq(pdev, 0);
> > > > > +       if (irq < 0)
> > > > > +               return irq;
> > > > > +
> > > > > +       ret = tmdev->chip->init(tmdev);
> > > > > +       if (ret)
> > > > > +               return ret;
> > > > > +
> > > > > +       ret = sun8i_ths_register(tmdev);
> > > > > +       if (ret)
> > > > > +               return ret;
> > > > > +
> > > > > +       /*
> > > > > +        * Avoid entering the interrupt handler, the thermal device is not
> > > > > +        * registered yet, we deffer the registration of the interrupt to
> > > > > +        * the end.
> > > > > +        */
> > > > > +       ret = devm_request_threaded_irq(dev, irq, NULL,
> > > > > +                                       sun8i_irq_thread,
> > > > > +                                       IRQF_ONESHOT, "ths", tmdev);
> > > > > +       if (ret)
> > > > > +               return ret;
> > > > > +
> > > > > +       return ret;
> > > > > +}
> > > > > +
> > > > > +static int sun8i_ths_remove(struct platform_device *pdev)
> > > > > +{
> > > > > +       struct ths_device *tmdev = platform_get_drvdata(pdev);
> > > > > +
> > > > > +       clk_disable_unprepare(tmdev->mod_clk);
> > > > > +       clk_disable_unprepare(tmdev->bus_clk);
> > > > > +       reset_control_assert(tmdev->reset);
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static const struct ths_thermal_chip sun8i_a83t_ths = {
> > > > > +       .sensor_num = 3,
> > > > > +       .scale = 705,
> > > > > +       .offset = 191668,
> > > > > +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> > > > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > > > +       .init = sun8i_h3_thermal_init,
> > > > > +       .irq_ack = sun8i_h3_irq_ack,
> > > > > +       .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,
> > > > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > > > +       .init = sun8i_h3_thermal_init,
> > > > > +       .irq_ack = sun8i_h3_irq_ack,
> > > > > +       .calc_temp = sun8i_ths_calc_temp,
> > > > > +};
> > > > > +
> > > > > +static const struct ths_thermal_chip sun8i_r40_ths = {
> > > > > +       .sensor_num = 3,
> > > > > +       .offset = 251086,
> > > > > +       .scale = 1130,
> > > > > +       .has_mod_clk = true,
> > > > > +       .has_bus_clk_reset = true,
> > > > > +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> > > > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > > > +       .init = sun8i_h3_thermal_init,
> > > > > +       .irq_ack = sun8i_h3_irq_ack,
> > > > > +       .calc_temp = sun8i_ths_calc_temp,
> > > > > +};
> > > > > +
> > > > > +static const struct ths_thermal_chip sun50i_a64_ths = {
> > > > > +       .sensor_num = 3,
> > > > > +       .offset = 253890,
> > > > > +       .scale = 1170,
> > > > > +       .has_mod_clk = true,
> > > > > +       .has_bus_clk_reset = true,
> > > > > +       .temp_data_base = SUN8I_THS_TEMP_DATA,
> > > > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > > > +       .init = sun8i_h3_thermal_init,
> > > > > +       .irq_ack = sun8i_h3_irq_ack,
> > > > > +       .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,
> > > > > +       .calibrate = sun8i_h3_ths_calibrate,
> > > > > +       .init = sun8i_h3_thermal_init,
> > > > > +       .irq_ack = sun8i_h3_irq_ack,
> > > > > +       .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,
> > > > > +       .calibrate = sun50i_h6_ths_calibrate,
> > > > > +       .init = sun50i_h6_thermal_init,
> > > > > +       .irq_ack = sun50i_h6_irq_ack,
> > > > > +       .calc_temp = sun8i_ths_calc_temp,
> > > > > +};
> > > > > +
> > > > > +static const struct of_device_id of_ths_match[] = {
> > > > > +       { .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths },
> > > > > +       { .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths },
> > > > > +       { .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths },
> > > > > +       { .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths },
> > > > > +       { .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths },
> > > > > +       { .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths },
> > > > > +       { /* sentinel */ },
> > > > > +};
> > > > > +MODULE_DEVICE_TABLE(of, of_ths_match);
> > > > > +
> > > > > +static struct platform_driver ths_driver = {
> > > > > +       .probe = sun8i_ths_probe,
> > > > > +       .remove = sun8i_ths_remove,
> > > > > +       .driver = {
> > > > > +               .name = "sun8i-thermal",
> > > > > +               .of_match_table = of_ths_match,
> > > > > +       },
> > > > > +};
> > > > > +module_platform_driver(ths_driver);
> > > > > +
> > > > > +MODULE_DESCRIPTION("Thermal sensor driver for Allwinner SOC");
> > > > > +MODULE_LICENSE("GPL v2");
> > > > > --
> > > > > 2.24.0
> > > > >
> > > >
> > > > Thx,
> > > > Yangtao
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > linux-arm-kernel@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

end of thread, back to index

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-27  5:29 [PATCH v6 0/7] add thermal sensor driver for A64, A83T, H3, H5, H6, R40 Vasily Khoruzhick
2019-11-27  5:29 ` [PATCH v6 1/7] thermal: sun8i: add thermal driver for H6/H5/H3/A64/A83T/R40 Vasily Khoruzhick
2019-11-27 11:14   ` Ondřej Jirman
2019-11-27 17:35     ` Maxime Ripard
2019-11-27 19:44       ` Frank Lee
2019-11-27 19:48         ` Vasily Khoruzhick
2019-11-27 22:24           ` Ondřej Jirman
2019-11-28 15:16             ` Frank Lee
2019-11-29  0:53               ` Ondřej Jirman
2019-11-28  7:42           ` Maxime Ripard
2019-11-27 17:27   ` Maxime Ripard
2019-11-28 16:43   ` Frank Lee
2019-11-28 17:06     ` Ondřej Jirman
2019-11-29 11:46       ` Frank Lee
2019-12-02  1:58         ` Ondřej Jirman
2019-12-09 18:04           ` Frank Lee
2019-11-27  5:29 ` [PATCH v6 2/7] dt-bindings: thermal: add YAML schema for sun8i-thermal driver bindings Vasily Khoruzhick
2019-11-27 17:44   ` Maxime Ripard
2019-11-27 18:07     ` Ondřej Jirman
2019-11-27 18:52       ` Vasily Khoruzhick
2019-11-27 20:23     ` Vasily Khoruzhick
2019-11-28  7:43       ` Maxime Ripard
2019-11-28 14:35         ` Frank Lee
2019-11-28 14:45           ` Frank Lee
2019-11-28 16:24             ` Vasily Khoruzhick
2019-11-27  5:29 ` [PATCH v6 3/7] ARM: dts: sun8i-a83t: Add thermal sensor and thermal zones Vasily Khoruzhick
2019-11-27  5:29 ` [PATCH v6 4/7] ARM: dts: sun8i-h3: " Vasily Khoruzhick
2019-11-27 19:40   ` Samuel Holland
2019-11-27  5:29 ` [PATCH v6 5/7] arm64: dts: allwinner: sun50i-h5: " Vasily Khoruzhick
2019-11-27  5:29 ` [PATCH v6 6/7] arm64: dts: allwinner: sun50i-h6: " Vasily Khoruzhick
2019-11-27  5:29 ` [PATCH v6 7/7] arm64: dts: allwinner: a64: Add thermal sensors " Vasily Khoruzhick

Linux-PM Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-pm/0 linux-pm/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-pm linux-pm/ https://lore.kernel.org/linux-pm \
		linux-pm@vger.kernel.org
	public-inbox-index linux-pm

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-pm


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git