All of lore.kernel.org
 help / color / mirror / Atom feed
From: Haylen Chu <heylenay@4d2.org>
To: "Rafael J. Wysocki" <rafael@kernel.org>,
	Daniel Lezcano <daniel.lezcano@linaro.org>,
	Zhang Rui <rui.zhang@intel.com>,
	Lukasz Luba <lukasz.luba@arm.com>, Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Chen Wang <unicorn_wang@outlook.com>,
	Inochi Amaoto <inochiama@outlook.com>,
	Paul Walmsley <paul.walmsley@sifive.com>,
	Palmer Dabbelt <palmer@dabbelt.com>,
	Albert Ou <aou@eecs.berkeley.edu>,
	Jisheng Zhang <jszhang@kernel.org>
Cc: linux-pm@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-riscv@lists.infradead.org,
	Haylen Chu <heylenay@4d2.org>
Subject: [PATCH v5 2/3] thermal: cv1800: Add cv1800 thermal driver support
Date: Mon, 14 Oct 2024 07:38:12 +0000	[thread overview]
Message-ID: <20241014073813.23984-3-heylenay@4d2.org> (raw)
In-Reply-To: <20241014073813.23984-1-heylenay@4d2.org>

Add support for cv1800 SoC integrated thermal sensors. Temperature is
calculated with a curve-fitted formula. Threshold registers are set as
specified by trip points and the thermal zone will be updated on
threshold violation interrupts.

Signed-off-by: Haylen Chu <heylenay@4d2.org>
---
 drivers/thermal/Kconfig          |   6 +
 drivers/thermal/Makefile         |   1 +
 drivers/thermal/cv1800_thermal.c | 296 +++++++++++++++++++++++++++++++
 3 files changed, 303 insertions(+)
 create mode 100644 drivers/thermal/cv1800_thermal.c

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 61e7ae524b1f..901524ba8b38 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -499,4 +499,10 @@ config LOONGSON2_THERMAL
 	  is higher than the high temperature threshold or lower than the low
 	  temperature threshold, the interrupt will occur.
 
+config CV1800_THERMAL
+	tristate "Temperature sensor driver for Sophgo CV1800"
+	help
+	  If you say yes here you get support for thermal sensor integrated in
+	  Sophgo CV1800 SoC.
+
 endif
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 41c4d56beb40..b1890b570f9b 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -63,4 +63,5 @@ obj-$(CONFIG_AMLOGIC_THERMAL)     += amlogic_thermal.o
 obj-$(CONFIG_SPRD_THERMAL)	+= sprd_thermal.o
 obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL)	+= khadas_mcu_fan.o
 obj-$(CONFIG_LOONGSON2_THERMAL)	+= loongson2_thermal.o
+obj-$(CONFIG_CV1800_THERMAL)	+= cv1800_thermal.o
 obj-$(CONFIG_THERMAL_CORE_TESTING)	+= testing/
diff --git a/drivers/thermal/cv1800_thermal.c b/drivers/thermal/cv1800_thermal.c
new file mode 100644
index 000000000000..588fc2da06e1
--- /dev/null
+++ b/drivers/thermal/cv1800_thermal.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Sophgo Inc.
+ * Copyright (C) 2024 Haylen Chu <heylenay@4d2.org>
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+#include <linux/units.h>
+
+#define TEMPSEN_VERSION					0x0
+#define TEMPSEN_CTRL					0x4
+#define  TEMPSEN_CTRL_EN				BIT(0)
+#define  TEMPSEN_CTRL_SEL_MASK				GENMASK(7, 4)
+#define  TEMPSEN_CTRL_SEL_OFFSET			4
+#define TEMPSEN_STATUS					0x8
+#define TEMPSEN_SET					0xc
+#define  TEMPSEN_SET_CHOPSEL_MASK			GENMASK(5, 4)
+#define  TEMPSEN_SET_CHOPSEL_OFFSET			4
+#define  TEMPSEN_SET_CHOPSEL_128T			0
+#define  TEMPSEN_SET_CHOPSEL_256T			1
+#define  TEMPSEN_SET_CHOPSEL_512T			2
+#define  TEMPSEN_SET_CHOPSEL_1024T			3
+#define  TEMPSEN_SET_ACCSEL_MASK			GENMASK(7, 6)
+#define  TEMPSEN_SET_ACCSEL_OFFSET			6
+#define  TEMPSEN_SET_ACCSEL_512T			0
+#define  TEMPSEN_SET_ACCSEL_1024T			1
+#define  TEMPSEN_SET_ACCSEL_2048T			2
+#define  TEMPSEN_SET_ACCSEL_4096T			3
+#define  TEMPSEN_SET_CYC_CLKDIV_MASK			GENMASK(15, 8)
+#define  TEMPSEN_SET_CYC_CLKDIV_OFFSET			8
+#define TEMPSEN_INTR_EN					0x10
+#define TEMPSEN_INTR_CLR				0x14
+#define TEMPSEN_INTR_STA				0x18
+#define TEMPSEN_INTR_RAW				0x1c
+#define  TEMPSEN_INTR_THRESHOLD_HIGH(n)			BIT(4 + (n))
+#define  TEMPSEN_INTR_THRESHOLD_LOW(n)			BIT(8 + (n))
+#define TEMPSEN_RESULT(n)				(0x20 + (n) * 4)
+#define  TEMPSEN_RESULT_RESULT_MASK			GENMASK(12, 0)
+#define  TEMPSEN_RESULT_MAX_RESULT_MASK			GENMASK(28, 16)
+#define  TEMPSEN_RESULT_CLR_MAX_RESULT			BIT(31)
+#define TEMPSEN_THRESHOLD(n)				(0x40 + (n) * 4)
+#define  TEMPSEN_THRESHOLD_HIGH_OFFSET			0
+#define  TEMPSEN_THRESHOLD_LOW_OFFSET			16
+#define TEMPSEN_AUTO_PERIOD				0x64
+#define  TEMPSEN_AUTO_PERIOD_AUTO_CYCLE_MASK		GENMASK(23, 0)
+#define  TEMPSEN_AUTO_PERIOD_AUTO_CYCLE_OFFSET		0
+
+struct cv1800_thermal_zone {
+	struct device *dev;
+	void __iomem *base;
+	struct clk *clk_tempsen;
+	u32 sample_cycle;
+};
+
+static void cv1800_thermal_init(struct cv1800_thermal_zone *ctz)
+{
+	void __iomem *base = ctz->base;
+	u32 regval;
+
+	writel(readl(base + TEMPSEN_INTR_RAW), base + TEMPSEN_INTR_CLR);
+	writel(TEMPSEN_RESULT_CLR_MAX_RESULT, base + TEMPSEN_RESULT(0));
+
+	regval = readl(base + TEMPSEN_SET);
+	regval &= ~TEMPSEN_SET_CHOPSEL_MASK;
+	regval &= ~TEMPSEN_SET_ACCSEL_MASK;
+	regval &= ~TEMPSEN_SET_CYC_CLKDIV_MASK;
+	regval |= TEMPSEN_SET_CHOPSEL_1024T << TEMPSEN_SET_CHOPSEL_OFFSET;
+	regval |= TEMPSEN_SET_ACCSEL_2048T << TEMPSEN_SET_ACCSEL_OFFSET;
+	regval |= 0x31 << TEMPSEN_SET_CYC_CLKDIV_OFFSET;
+	writel(regval, base + TEMPSEN_SET);
+
+	regval = readl(base + TEMPSEN_AUTO_PERIOD);
+	regval &= ~TEMPSEN_AUTO_PERIOD_AUTO_CYCLE_MASK;
+	regval |= ctz->sample_cycle << TEMPSEN_AUTO_PERIOD_AUTO_CYCLE_OFFSET;
+	writel(regval, base + TEMPSEN_AUTO_PERIOD);
+
+	regval = readl(base + TEMPSEN_CTRL);
+	regval &= ~TEMPSEN_CTRL_SEL_MASK;
+	regval |= 1 << TEMPSEN_CTRL_SEL_OFFSET;
+	regval |= TEMPSEN_CTRL_EN;
+	writel(regval, base + TEMPSEN_CTRL);
+}
+
+static void cv1800_thermal_deinit(struct cv1800_thermal_zone *ctz)
+{
+	void __iomem *base = ctz->base;
+	u32 regval;
+
+	regval = readl(base + TEMPSEN_CTRL);
+	regval &= ~(TEMPSEN_CTRL_SEL_MASK | TEMPSEN_CTRL_EN);
+	writel(regval, base + TEMPSEN_CTRL);
+
+	writel(0, base + TEMPSEN_INTR_EN);
+}
+
+/*
+ *	Raw register value to temperature (mC) formula:
+ *
+ *		       read_val * 1000 * 716
+ *	Temperature = ----------------------- - 273000
+ *				divider
+ *
+ *	where divider should be ticks number of accumulation period,
+ *	e.g. 2048 for TEMPSEN_CTRL_ACCSEL_2048T
+ */
+static int cv1800_calc_temp(u32 result)
+{
+	return (result * 1000) * 716 / 2048 - 273000;
+}
+
+static u32 cv1800_calc_raw(int temp)
+{
+	return (temp + 273000) * 2048 / (1000 * 716);
+}
+
+static int cv1800_set_threshold(struct cv1800_thermal_zone *ctz,
+				int low, int high)
+{
+	writel((cv1800_calc_raw(low) << TEMPSEN_THRESHOLD_LOW_OFFSET) |
+	       (cv1800_calc_raw(high) << TEMPSEN_THRESHOLD_HIGH_OFFSET),
+	       ctz->base + TEMPSEN_THRESHOLD(0));
+	writel(TEMPSEN_INTR_THRESHOLD_LOW(0) | TEMPSEN_INTR_THRESHOLD_HIGH(0),
+	       ctz->base + TEMPSEN_INTR_EN);
+
+	return 0;
+}
+
+static irqreturn_t cv1800_irq_thread(int irq, void *dev)
+{
+	struct thermal_zone_device *tdev = dev;
+	struct cv1800_thermal_zone *ctz = thermal_zone_device_priv(tdev);
+
+	writel(readl(ctz->base + TEMPSEN_INTR_RAW),
+	       ctz->base + TEMPSEN_INTR_CLR);
+
+	thermal_zone_device_update(tdev, THERMAL_EVENT_UNSPECIFIED);
+
+	return IRQ_HANDLED;
+}
+
+static int cv1800_get_temp(struct thermal_zone_device *tdev, int *temperature)
+{
+	struct cv1800_thermal_zone *ctz = thermal_zone_device_priv(tdev);
+	void __iomem *base = ctz->base;
+	u32 result;
+
+	result = readl(base + TEMPSEN_RESULT(0)) & TEMPSEN_RESULT_RESULT_MASK;
+	*temperature = cv1800_calc_temp(result);
+
+	return 0;
+}
+
+static int cv1800_set_trips(struct thermal_zone_device *tdev, int low, int high)
+{
+	struct cv1800_thermal_zone *ctz = thermal_zone_device_priv(tdev);
+
+	return cv1800_set_threshold(ctz,
+				    clamp(low, -273 * 1000, 200 * 1000),
+				    clamp(high, -273 * 1000, 200 * 1000));
+}
+
+static const struct thermal_zone_device_ops cv1800_thermal_ops = {
+	.get_temp	= cv1800_get_temp,
+	.set_trips	= cv1800_set_trips,
+};
+
+static const struct of_device_id cv1800_thermal_of_match[] = {
+	{ .compatible = "sophgo,cv1800-thermal" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, cv1800_thermal_of_match);
+
+static int
+cv1800_parse_dt(struct cv1800_thermal_zone *ctz)
+{
+	struct device_node *np = ctz->dev->of_node;
+	u32 tmp;
+
+	if (of_property_read_u32(np, "sample-rate-hz", &tmp)) {
+		ctz->sample_cycle = 1000000;
+	} else {
+		/* sample cycle should be at least 524us */
+		if (tmp > 1000000 / 524) {
+			dev_err(ctz->dev, "invalid sample rate %d\n", tmp);
+			return -EINVAL;
+		}
+
+		ctz->sample_cycle = 1000000 / tmp;
+	}
+
+	return 0;
+}
+
+static int cv1800_thermal_probe(struct platform_device *pdev)
+{
+	struct thermal_zone_device *tdev;
+	struct cv1800_thermal_zone *ctz;
+	struct resource *res;
+	int ret, irq;
+
+	ctz = devm_kzalloc(&pdev->dev, sizeof(*ctz), GFP_KERNEL);
+	if (!ctz)
+		return -ENOMEM;
+
+	ctz->dev = &pdev->dev;
+
+	ret = cv1800_parse_dt(ctz);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret, "failed to parse dt\n");
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctz->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ctz->base))
+		return dev_err_probe(&pdev->dev, PTR_ERR(ctz->base),
+				     "failed to map tempsen registers\n");
+
+	ctz->clk_tempsen = devm_clk_get_enabled(&pdev->dev, NULL);
+	if (IS_ERR(ctz->clk_tempsen))
+		return dev_err_probe(&pdev->dev, PTR_ERR(ctz->clk_tempsen),
+				     "failed to get clk_tempsen\n");
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	cv1800_thermal_init(ctz);
+
+	tdev = devm_thermal_of_zone_register(&pdev->dev, 0, ctz,
+					     &cv1800_thermal_ops);
+	if (IS_ERR(tdev))
+		return dev_err_probe(&pdev->dev, PTR_ERR(tdev),
+				     "failed to register thermal zone\n");
+
+	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+					cv1800_irq_thread, IRQF_ONESHOT,
+					"cv1800_thermal", tdev);
+	if (ret < 0)
+		return dev_err_probe(&pdev->dev, ret, "failed to request irq\n");
+
+	platform_set_drvdata(pdev, ctz);
+
+	return 0;
+}
+
+static void cv1800_thermal_remove(struct platform_device *pdev)
+{
+	struct cv1800_thermal_zone *ctz = platform_get_drvdata(pdev);
+
+	cv1800_thermal_deinit(ctz);
+}
+
+static int __maybe_unused cv1800_thermal_suspend(struct device *dev)
+{
+	struct cv1800_thermal_zone *ctz = dev_get_drvdata(dev);
+
+	cv1800_thermal_deinit(ctz);
+	clk_disable_unprepare(ctz->clk_tempsen);
+
+	return 0;
+}
+
+static int __maybe_unused cv1800_thermal_resume(struct device *dev)
+{
+	struct cv1800_thermal_zone *ctz = dev_get_drvdata(dev);
+
+	clk_prepare_enable(ctz->clk_tempsen);
+	cv1800_thermal_init(ctz);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(cv1800_thermal_pm_ops,
+			 cv1800_thermal_suspend, cv1800_thermal_resume);
+
+static struct platform_driver cv1800_thermal_driver = {
+	.probe = cv1800_thermal_probe,
+	.remove = cv1800_thermal_remove,
+	.driver = {
+		.name = "cv1800-thermal",
+		.pm = &cv1800_thermal_pm_ops,
+		.of_match_table = cv1800_thermal_of_match,
+	},
+};
+
+module_platform_driver(cv1800_thermal_driver);
+
+MODULE_DESCRIPTION("Sophgo CV1800 thermal driver");
+MODULE_AUTHOR("Haylen Chu <heylenay@4d2.org>");
+MODULE_LICENSE("GPL");
-- 
2.46.2


WARNING: multiple messages have this Message-ID (diff)
From: Haylen Chu <heylenay@4d2.org>
To: "Rafael J. Wysocki" <rafael@kernel.org>,
	Daniel Lezcano <daniel.lezcano@linaro.org>,
	Zhang Rui <rui.zhang@intel.com>,
	Lukasz Luba <lukasz.luba@arm.com>, Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Chen Wang <unicorn_wang@outlook.com>,
	Inochi Amaoto <inochiama@outlook.com>,
	Paul Walmsley <paul.walmsley@sifive.com>,
	Palmer Dabbelt <palmer@dabbelt.com>,
	Albert Ou <aou@eecs.berkeley.edu>,
	Jisheng Zhang <jszhang@kernel.org>
Cc: linux-pm@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-riscv@lists.infradead.org,
	Haylen Chu <heylenay@4d2.org>
Subject: [PATCH v5 2/3] thermal: cv1800: Add cv1800 thermal driver support
Date: Mon, 14 Oct 2024 07:38:12 +0000	[thread overview]
Message-ID: <20241014073813.23984-3-heylenay@4d2.org> (raw)
In-Reply-To: <20241014073813.23984-1-heylenay@4d2.org>

Add support for cv1800 SoC integrated thermal sensors. Temperature is
calculated with a curve-fitted formula. Threshold registers are set as
specified by trip points and the thermal zone will be updated on
threshold violation interrupts.

Signed-off-by: Haylen Chu <heylenay@4d2.org>
---
 drivers/thermal/Kconfig          |   6 +
 drivers/thermal/Makefile         |   1 +
 drivers/thermal/cv1800_thermal.c | 296 +++++++++++++++++++++++++++++++
 3 files changed, 303 insertions(+)
 create mode 100644 drivers/thermal/cv1800_thermal.c

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 61e7ae524b1f..901524ba8b38 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -499,4 +499,10 @@ config LOONGSON2_THERMAL
 	  is higher than the high temperature threshold or lower than the low
 	  temperature threshold, the interrupt will occur.
 
+config CV1800_THERMAL
+	tristate "Temperature sensor driver for Sophgo CV1800"
+	help
+	  If you say yes here you get support for thermal sensor integrated in
+	  Sophgo CV1800 SoC.
+
 endif
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 41c4d56beb40..b1890b570f9b 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -63,4 +63,5 @@ obj-$(CONFIG_AMLOGIC_THERMAL)     += amlogic_thermal.o
 obj-$(CONFIG_SPRD_THERMAL)	+= sprd_thermal.o
 obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL)	+= khadas_mcu_fan.o
 obj-$(CONFIG_LOONGSON2_THERMAL)	+= loongson2_thermal.o
+obj-$(CONFIG_CV1800_THERMAL)	+= cv1800_thermal.o
 obj-$(CONFIG_THERMAL_CORE_TESTING)	+= testing/
diff --git a/drivers/thermal/cv1800_thermal.c b/drivers/thermal/cv1800_thermal.c
new file mode 100644
index 000000000000..588fc2da06e1
--- /dev/null
+++ b/drivers/thermal/cv1800_thermal.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Sophgo Inc.
+ * Copyright (C) 2024 Haylen Chu <heylenay@4d2.org>
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+#include <linux/units.h>
+
+#define TEMPSEN_VERSION					0x0
+#define TEMPSEN_CTRL					0x4
+#define  TEMPSEN_CTRL_EN				BIT(0)
+#define  TEMPSEN_CTRL_SEL_MASK				GENMASK(7, 4)
+#define  TEMPSEN_CTRL_SEL_OFFSET			4
+#define TEMPSEN_STATUS					0x8
+#define TEMPSEN_SET					0xc
+#define  TEMPSEN_SET_CHOPSEL_MASK			GENMASK(5, 4)
+#define  TEMPSEN_SET_CHOPSEL_OFFSET			4
+#define  TEMPSEN_SET_CHOPSEL_128T			0
+#define  TEMPSEN_SET_CHOPSEL_256T			1
+#define  TEMPSEN_SET_CHOPSEL_512T			2
+#define  TEMPSEN_SET_CHOPSEL_1024T			3
+#define  TEMPSEN_SET_ACCSEL_MASK			GENMASK(7, 6)
+#define  TEMPSEN_SET_ACCSEL_OFFSET			6
+#define  TEMPSEN_SET_ACCSEL_512T			0
+#define  TEMPSEN_SET_ACCSEL_1024T			1
+#define  TEMPSEN_SET_ACCSEL_2048T			2
+#define  TEMPSEN_SET_ACCSEL_4096T			3
+#define  TEMPSEN_SET_CYC_CLKDIV_MASK			GENMASK(15, 8)
+#define  TEMPSEN_SET_CYC_CLKDIV_OFFSET			8
+#define TEMPSEN_INTR_EN					0x10
+#define TEMPSEN_INTR_CLR				0x14
+#define TEMPSEN_INTR_STA				0x18
+#define TEMPSEN_INTR_RAW				0x1c
+#define  TEMPSEN_INTR_THRESHOLD_HIGH(n)			BIT(4 + (n))
+#define  TEMPSEN_INTR_THRESHOLD_LOW(n)			BIT(8 + (n))
+#define TEMPSEN_RESULT(n)				(0x20 + (n) * 4)
+#define  TEMPSEN_RESULT_RESULT_MASK			GENMASK(12, 0)
+#define  TEMPSEN_RESULT_MAX_RESULT_MASK			GENMASK(28, 16)
+#define  TEMPSEN_RESULT_CLR_MAX_RESULT			BIT(31)
+#define TEMPSEN_THRESHOLD(n)				(0x40 + (n) * 4)
+#define  TEMPSEN_THRESHOLD_HIGH_OFFSET			0
+#define  TEMPSEN_THRESHOLD_LOW_OFFSET			16
+#define TEMPSEN_AUTO_PERIOD				0x64
+#define  TEMPSEN_AUTO_PERIOD_AUTO_CYCLE_MASK		GENMASK(23, 0)
+#define  TEMPSEN_AUTO_PERIOD_AUTO_CYCLE_OFFSET		0
+
+struct cv1800_thermal_zone {
+	struct device *dev;
+	void __iomem *base;
+	struct clk *clk_tempsen;
+	u32 sample_cycle;
+};
+
+static void cv1800_thermal_init(struct cv1800_thermal_zone *ctz)
+{
+	void __iomem *base = ctz->base;
+	u32 regval;
+
+	writel(readl(base + TEMPSEN_INTR_RAW), base + TEMPSEN_INTR_CLR);
+	writel(TEMPSEN_RESULT_CLR_MAX_RESULT, base + TEMPSEN_RESULT(0));
+
+	regval = readl(base + TEMPSEN_SET);
+	regval &= ~TEMPSEN_SET_CHOPSEL_MASK;
+	regval &= ~TEMPSEN_SET_ACCSEL_MASK;
+	regval &= ~TEMPSEN_SET_CYC_CLKDIV_MASK;
+	regval |= TEMPSEN_SET_CHOPSEL_1024T << TEMPSEN_SET_CHOPSEL_OFFSET;
+	regval |= TEMPSEN_SET_ACCSEL_2048T << TEMPSEN_SET_ACCSEL_OFFSET;
+	regval |= 0x31 << TEMPSEN_SET_CYC_CLKDIV_OFFSET;
+	writel(regval, base + TEMPSEN_SET);
+
+	regval = readl(base + TEMPSEN_AUTO_PERIOD);
+	regval &= ~TEMPSEN_AUTO_PERIOD_AUTO_CYCLE_MASK;
+	regval |= ctz->sample_cycle << TEMPSEN_AUTO_PERIOD_AUTO_CYCLE_OFFSET;
+	writel(regval, base + TEMPSEN_AUTO_PERIOD);
+
+	regval = readl(base + TEMPSEN_CTRL);
+	regval &= ~TEMPSEN_CTRL_SEL_MASK;
+	regval |= 1 << TEMPSEN_CTRL_SEL_OFFSET;
+	regval |= TEMPSEN_CTRL_EN;
+	writel(regval, base + TEMPSEN_CTRL);
+}
+
+static void cv1800_thermal_deinit(struct cv1800_thermal_zone *ctz)
+{
+	void __iomem *base = ctz->base;
+	u32 regval;
+
+	regval = readl(base + TEMPSEN_CTRL);
+	regval &= ~(TEMPSEN_CTRL_SEL_MASK | TEMPSEN_CTRL_EN);
+	writel(regval, base + TEMPSEN_CTRL);
+
+	writel(0, base + TEMPSEN_INTR_EN);
+}
+
+/*
+ *	Raw register value to temperature (mC) formula:
+ *
+ *		       read_val * 1000 * 716
+ *	Temperature = ----------------------- - 273000
+ *				divider
+ *
+ *	where divider should be ticks number of accumulation period,
+ *	e.g. 2048 for TEMPSEN_CTRL_ACCSEL_2048T
+ */
+static int cv1800_calc_temp(u32 result)
+{
+	return (result * 1000) * 716 / 2048 - 273000;
+}
+
+static u32 cv1800_calc_raw(int temp)
+{
+	return (temp + 273000) * 2048 / (1000 * 716);
+}
+
+static int cv1800_set_threshold(struct cv1800_thermal_zone *ctz,
+				int low, int high)
+{
+	writel((cv1800_calc_raw(low) << TEMPSEN_THRESHOLD_LOW_OFFSET) |
+	       (cv1800_calc_raw(high) << TEMPSEN_THRESHOLD_HIGH_OFFSET),
+	       ctz->base + TEMPSEN_THRESHOLD(0));
+	writel(TEMPSEN_INTR_THRESHOLD_LOW(0) | TEMPSEN_INTR_THRESHOLD_HIGH(0),
+	       ctz->base + TEMPSEN_INTR_EN);
+
+	return 0;
+}
+
+static irqreturn_t cv1800_irq_thread(int irq, void *dev)
+{
+	struct thermal_zone_device *tdev = dev;
+	struct cv1800_thermal_zone *ctz = thermal_zone_device_priv(tdev);
+
+	writel(readl(ctz->base + TEMPSEN_INTR_RAW),
+	       ctz->base + TEMPSEN_INTR_CLR);
+
+	thermal_zone_device_update(tdev, THERMAL_EVENT_UNSPECIFIED);
+
+	return IRQ_HANDLED;
+}
+
+static int cv1800_get_temp(struct thermal_zone_device *tdev, int *temperature)
+{
+	struct cv1800_thermal_zone *ctz = thermal_zone_device_priv(tdev);
+	void __iomem *base = ctz->base;
+	u32 result;
+
+	result = readl(base + TEMPSEN_RESULT(0)) & TEMPSEN_RESULT_RESULT_MASK;
+	*temperature = cv1800_calc_temp(result);
+
+	return 0;
+}
+
+static int cv1800_set_trips(struct thermal_zone_device *tdev, int low, int high)
+{
+	struct cv1800_thermal_zone *ctz = thermal_zone_device_priv(tdev);
+
+	return cv1800_set_threshold(ctz,
+				    clamp(low, -273 * 1000, 200 * 1000),
+				    clamp(high, -273 * 1000, 200 * 1000));
+}
+
+static const struct thermal_zone_device_ops cv1800_thermal_ops = {
+	.get_temp	= cv1800_get_temp,
+	.set_trips	= cv1800_set_trips,
+};
+
+static const struct of_device_id cv1800_thermal_of_match[] = {
+	{ .compatible = "sophgo,cv1800-thermal" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, cv1800_thermal_of_match);
+
+static int
+cv1800_parse_dt(struct cv1800_thermal_zone *ctz)
+{
+	struct device_node *np = ctz->dev->of_node;
+	u32 tmp;
+
+	if (of_property_read_u32(np, "sample-rate-hz", &tmp)) {
+		ctz->sample_cycle = 1000000;
+	} else {
+		/* sample cycle should be at least 524us */
+		if (tmp > 1000000 / 524) {
+			dev_err(ctz->dev, "invalid sample rate %d\n", tmp);
+			return -EINVAL;
+		}
+
+		ctz->sample_cycle = 1000000 / tmp;
+	}
+
+	return 0;
+}
+
+static int cv1800_thermal_probe(struct platform_device *pdev)
+{
+	struct thermal_zone_device *tdev;
+	struct cv1800_thermal_zone *ctz;
+	struct resource *res;
+	int ret, irq;
+
+	ctz = devm_kzalloc(&pdev->dev, sizeof(*ctz), GFP_KERNEL);
+	if (!ctz)
+		return -ENOMEM;
+
+	ctz->dev = &pdev->dev;
+
+	ret = cv1800_parse_dt(ctz);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret, "failed to parse dt\n");
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctz->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ctz->base))
+		return dev_err_probe(&pdev->dev, PTR_ERR(ctz->base),
+				     "failed to map tempsen registers\n");
+
+	ctz->clk_tempsen = devm_clk_get_enabled(&pdev->dev, NULL);
+	if (IS_ERR(ctz->clk_tempsen))
+		return dev_err_probe(&pdev->dev, PTR_ERR(ctz->clk_tempsen),
+				     "failed to get clk_tempsen\n");
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	cv1800_thermal_init(ctz);
+
+	tdev = devm_thermal_of_zone_register(&pdev->dev, 0, ctz,
+					     &cv1800_thermal_ops);
+	if (IS_ERR(tdev))
+		return dev_err_probe(&pdev->dev, PTR_ERR(tdev),
+				     "failed to register thermal zone\n");
+
+	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+					cv1800_irq_thread, IRQF_ONESHOT,
+					"cv1800_thermal", tdev);
+	if (ret < 0)
+		return dev_err_probe(&pdev->dev, ret, "failed to request irq\n");
+
+	platform_set_drvdata(pdev, ctz);
+
+	return 0;
+}
+
+static void cv1800_thermal_remove(struct platform_device *pdev)
+{
+	struct cv1800_thermal_zone *ctz = platform_get_drvdata(pdev);
+
+	cv1800_thermal_deinit(ctz);
+}
+
+static int __maybe_unused cv1800_thermal_suspend(struct device *dev)
+{
+	struct cv1800_thermal_zone *ctz = dev_get_drvdata(dev);
+
+	cv1800_thermal_deinit(ctz);
+	clk_disable_unprepare(ctz->clk_tempsen);
+
+	return 0;
+}
+
+static int __maybe_unused cv1800_thermal_resume(struct device *dev)
+{
+	struct cv1800_thermal_zone *ctz = dev_get_drvdata(dev);
+
+	clk_prepare_enable(ctz->clk_tempsen);
+	cv1800_thermal_init(ctz);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(cv1800_thermal_pm_ops,
+			 cv1800_thermal_suspend, cv1800_thermal_resume);
+
+static struct platform_driver cv1800_thermal_driver = {
+	.probe = cv1800_thermal_probe,
+	.remove = cv1800_thermal_remove,
+	.driver = {
+		.name = "cv1800-thermal",
+		.pm = &cv1800_thermal_pm_ops,
+		.of_match_table = cv1800_thermal_of_match,
+	},
+};
+
+module_platform_driver(cv1800_thermal_driver);
+
+MODULE_DESCRIPTION("Sophgo CV1800 thermal driver");
+MODULE_AUTHOR("Haylen Chu <heylenay@4d2.org>");
+MODULE_LICENSE("GPL");
-- 
2.46.2


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

  parent reply	other threads:[~2024-10-14  7:38 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-10-14  7:38 [PATCH v5 0/3] riscv: sophgo: add thermal sensor support for cv180x/sg200x SoCs Haylen Chu
2024-10-14  7:38 ` Haylen Chu
2024-10-14  7:38 ` [PATCH v5 1/3] dt-bindings: thermal: sophgo,cv1800-thermal: Add Sophgo CV1800 thermal Haylen Chu
2024-10-14  7:38   ` Haylen Chu
2024-10-15  5:52   ` Krzysztof Kozlowski
2024-10-15  5:52     ` Krzysztof Kozlowski
2024-10-15 13:07     ` Haylen Chu
2024-10-15 13:07       ` Haylen Chu
2024-10-15 13:34       ` Krzysztof Kozlowski
2024-10-15 13:34         ` Krzysztof Kozlowski
2024-10-16 11:53         ` Haylen Chu
2024-10-16 11:53           ` Haylen Chu
2024-10-16 12:03           ` Krzysztof Kozlowski
2024-10-16 12:03             ` Krzysztof Kozlowski
2024-10-15  5:55   ` Krzysztof Kozlowski
2024-10-15  5:55     ` Krzysztof Kozlowski
2024-10-15 13:20     ` Haylen Chu
2024-10-15 13:20       ` Haylen Chu
2024-10-14  7:38 ` Haylen Chu [this message]
2024-10-14  7:38   ` [PATCH v5 2/3] thermal: cv1800: Add cv1800 thermal driver support Haylen Chu
2024-10-14  7:38 ` [PATCH v5 3/3] riscv: dts: sophgo: cv18xx: Add sensor device and thermal zone Haylen Chu
2024-10-14  7:38   ` Haylen Chu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20241014073813.23984-3-heylenay@4d2.org \
    --to=heylenay@4d2.org \
    --cc=aou@eecs.berkeley.edu \
    --cc=conor+dt@kernel.org \
    --cc=daniel.lezcano@linaro.org \
    --cc=devicetree@vger.kernel.org \
    --cc=inochiama@outlook.com \
    --cc=jszhang@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=lukasz.luba@arm.com \
    --cc=palmer@dabbelt.com \
    --cc=paul.walmsley@sifive.com \
    --cc=rafael@kernel.org \
    --cc=robh@kernel.org \
    --cc=rui.zhang@intel.com \
    --cc=unicorn_wang@outlook.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.