xref: /linux/drivers/thermal/spacemit/k1_tsensor.c (revision 296a977f2bac45759a427dc57a21edb628f5c86c)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Thermal sensor driver for SpacemiT K1 SoC
4  *
5  * Copyright (C) 2026 Shuwei Wu <shuwei.wu@mailbox.org>
6  */
7 #include <linux/bitfield.h>
8 #include <linux/clk.h>
9 #include <linux/err.h>
10 #include <linux/interrupt.h>
11 #include <linux/io.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/of.h>
15 #include <linux/platform_device.h>
16 #include <linux/reset.h>
17 #include <linux/slab.h>
18 #include <linux/thermal.h>
19 
20 #include "../thermal_hwmon.h"
21 
22 #define K1_TSENSOR_PCTRL_REG		0x00
23 #define K1_TSENSOR_PCTRL_ENABLE		BIT(0)
24 #define K1_TSENSOR_PCTRL_TEMP_MODE	BIT(3)
25 #define K1_TSENSOR_PCTRL_RAW_SEL	BIT(7)
26 
27 #define K1_TSENSOR_PCTRL_CTUNE		GENMASK(11, 8)
28 #define K1_TSENSOR_PCTRL_SW_CTRL	GENMASK(21, 18)
29 #define K1_TSENSOR_PCTRL_HW_AUTO_MODE	BIT(23)
30 
31 #define K1_TSENSOR_EN_REG		0x08
32 #define K1_TSENSOR_EN_ALL		GENMASK(MAX_SENSOR_NUMBER - 1, 0)
33 
34 #define K1_TSENSOR_TIME_REG		0x0C
35 #define K1_TSENSOR_TIME_WAIT_REF_CNT	GENMASK(3, 0)
36 #define K1_TSENSOR_TIME_ADC_CNT_RST	GENMASK(7, 4)
37 #define K1_TSENSOR_TIME_FILTER_PERIOD	GENMASK(21, 20)
38 #define K1_TSENSOR_TIME_MASK		GENMASK(23, 0)
39 
40 #define K1_TSENSOR_INT_CLR_REG		0x10
41 #define K1_TSENSOR_INT_EN_REG		0x14
42 #define K1_TSENSOR_INT_STA_REG		0x18
43 
44 #define K1_TSENSOR_INT_EN_MASK		BIT(0)
45 #define K1_TSENSOR_INT_MASK(x)		(GENMASK(2, 1) << ((x) * 2))
46 
47 #define K1_TSENSOR_DATA_BASE_REG	0x20
48 #define K1_TSENSOR_DATA_REG(x)		(K1_TSENSOR_DATA_BASE_REG + ((x) / 2) * 4)
49 #define K1_TSENSOR_DATA_LOW_MASK	GENMASK(15, 0)
50 #define K1_TSENSOR_DATA_HIGH_MASK	GENMASK(31, 16)
51 
52 #define K1_TSENSOR_THRSH_BASE_REG	0x40
53 #define K1_TSENSOR_THRSH_REG(x)		(K1_TSENSOR_THRSH_BASE_REG + ((x) * 4))
54 #define K1_TSENSOR_THRSH_LOW_MASK	GENMASK(15, 0)
55 #define K1_TSENSOR_THRSH_HIGH_MASK	GENMASK(31, 16)
56 
57 #define MAX_SENSOR_NUMBER		5
58 
59 /* Hardware offset value required for temperature calculation */
60 #define TEMPERATURE_OFFSET		278
61 
62 struct k1_tsensor_channel {
63 	struct k1_tsensor *ts;
64 	struct thermal_zone_device *tzd;
65 	int id;
66 };
67 
68 struct k1_tsensor {
69 	void __iomem *base;
70 	struct k1_tsensor_channel ch[MAX_SENSOR_NUMBER];
71 };
72 
73 static void k1_tsensor_init(struct k1_tsensor *ts)
74 {
75 	u32 val;
76 
77 	/* Disable all the interrupts */
78 	writel(0xffffffff, ts->base + K1_TSENSOR_INT_EN_REG);
79 
80 	/* Configure ADC sampling time and filter period */
81 	val = readl(ts->base + K1_TSENSOR_TIME_REG);
82 	val &= ~K1_TSENSOR_TIME_MASK;
83 	val |= K1_TSENSOR_TIME_FILTER_PERIOD |
84 	       K1_TSENSOR_TIME_ADC_CNT_RST |
85 	       K1_TSENSOR_TIME_WAIT_REF_CNT;
86 	writel(val, ts->base + K1_TSENSOR_TIME_REG);
87 
88 	/*
89 	 * Enable all sensors' auto mode, enable dither control,
90 	 * consecutive mode, and power up sensor.
91 	 */
92 	val = readl(ts->base + K1_TSENSOR_PCTRL_REG);
93 	val &= ~K1_TSENSOR_PCTRL_SW_CTRL;
94 	val &= ~K1_TSENSOR_PCTRL_CTUNE;
95 	val |= K1_TSENSOR_PCTRL_RAW_SEL |
96 	       K1_TSENSOR_PCTRL_TEMP_MODE |
97 	       K1_TSENSOR_PCTRL_HW_AUTO_MODE |
98 	       K1_TSENSOR_PCTRL_ENABLE;
99 	writel(val, ts->base + K1_TSENSOR_PCTRL_REG);
100 
101 	/* Enable each sensor */
102 	val = readl(ts->base + K1_TSENSOR_EN_REG);
103 	val |= K1_TSENSOR_EN_ALL;
104 	writel(val, ts->base + K1_TSENSOR_EN_REG);
105 }
106 
107 static void k1_tsensor_enable_irq(struct k1_tsensor_channel *ch)
108 {
109 	struct k1_tsensor *ts = ch->ts;
110 	u32 val;
111 
112 	val = readl(ts->base + K1_TSENSOR_INT_CLR_REG);
113 	val |= K1_TSENSOR_INT_MASK(ch->id);
114 	writel(val, ts->base + K1_TSENSOR_INT_CLR_REG);
115 
116 	val = readl(ts->base + K1_TSENSOR_INT_EN_REG);
117 	val &= ~K1_TSENSOR_INT_MASK(ch->id);
118 	writel(val, ts->base + K1_TSENSOR_INT_EN_REG);
119 
120 	/* Enable thermal interrupt */
121 	val = readl(ts->base + K1_TSENSOR_INT_EN_REG);
122 	val |= K1_TSENSOR_INT_EN_MASK;
123 	writel(val, ts->base + K1_TSENSOR_INT_EN_REG);
124 }
125 
126 /*
127  * The conversion formula used is:
128  * T(m°C) = (((raw_value & mask) >> shift) - TEMPERATURE_OFFSET) * 1000
129  */
130 static int k1_tsensor_get_temp(struct thermal_zone_device *tz, int *temp)
131 {
132 	struct k1_tsensor_channel *ch = thermal_zone_device_priv(tz);
133 	struct k1_tsensor *ts = ch->ts;
134 	u32 val;
135 
136 	val = readl(ts->base + K1_TSENSOR_DATA_REG(ch->id));
137 	if (ch->id % 2)
138 		*temp = FIELD_GET(K1_TSENSOR_DATA_HIGH_MASK, val);
139 	else
140 		*temp = FIELD_GET(K1_TSENSOR_DATA_LOW_MASK, val);
141 
142 	*temp -= TEMPERATURE_OFFSET;
143 	*temp *= 1000;
144 
145 	return 0;
146 }
147 
148 /*
149  * For each sensor, the hardware threshold register is 32 bits:
150  * - Lower 16 bits [15:0] configure the low threshold temperature.
151  * - Upper 16 bits [31:16] configure the high threshold temperature.
152  */
153 static int k1_tsensor_set_trips(struct thermal_zone_device *tz, int low, int high)
154 {
155 	struct k1_tsensor_channel *ch = thermal_zone_device_priv(tz);
156 	struct k1_tsensor *ts = ch->ts;
157 	u32 val;
158 
159 	if (low >= high)
160 		return -EINVAL;
161 
162 	low = clamp_val(low / 1000 + TEMPERATURE_OFFSET, TEMPERATURE_OFFSET,
163 			FIELD_MAX(K1_TSENSOR_THRSH_LOW_MASK));
164 	high = clamp_val(high / 1000 + TEMPERATURE_OFFSET, TEMPERATURE_OFFSET,
165 			 FIELD_MAX(K1_TSENSOR_THRSH_HIGH_MASK));
166 
167 	val = readl(ts->base + K1_TSENSOR_THRSH_REG(ch->id));
168 
169 	val &= ~(K1_TSENSOR_THRSH_LOW_MASK | K1_TSENSOR_THRSH_HIGH_MASK);
170 	val |= FIELD_PREP(K1_TSENSOR_THRSH_LOW_MASK, low);
171 	val |= FIELD_PREP(K1_TSENSOR_THRSH_HIGH_MASK, high);
172 
173 	writel(val, ts->base + K1_TSENSOR_THRSH_REG(ch->id));
174 
175 	return 0;
176 }
177 
178 static const struct thermal_zone_device_ops k1_tsensor_ops = {
179 	.get_temp = k1_tsensor_get_temp,
180 	.set_trips = k1_tsensor_set_trips,
181 };
182 
183 static irqreturn_t k1_tsensor_irq_thread(int irq, void *data)
184 {
185 	struct k1_tsensor *ts = (struct k1_tsensor *)data;
186 	int mask, status, i;
187 
188 	status = readl(ts->base + K1_TSENSOR_INT_STA_REG);
189 
190 	for (i = 0; i < MAX_SENSOR_NUMBER; i++) {
191 		if (status & K1_TSENSOR_INT_MASK(i)) {
192 			mask = readl(ts->base + K1_TSENSOR_INT_CLR_REG);
193 			mask |= K1_TSENSOR_INT_MASK(i);
194 			writel(mask, ts->base + K1_TSENSOR_INT_CLR_REG);
195 			thermal_zone_device_update(ts->ch[i].tzd, THERMAL_EVENT_UNSPECIFIED);
196 		}
197 	}
198 
199 	return IRQ_HANDLED;
200 }
201 
202 static int k1_tsensor_probe(struct platform_device *pdev)
203 {
204 	struct device *dev = &pdev->dev;
205 	struct k1_tsensor *ts;
206 	struct reset_control *reset;
207 	struct clk *clk;
208 	int i, irq, ret;
209 
210 	ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
211 	if (!ts)
212 		return -ENOMEM;
213 
214 	ts->base = devm_platform_ioremap_resource(pdev, 0);
215 	if (IS_ERR(ts->base))
216 		return dev_err_probe(dev, PTR_ERR(ts->base), "Failed to get reg\n");
217 
218 	reset = devm_reset_control_get_exclusive_deasserted(dev, NULL);
219 	if (IS_ERR(reset))
220 		return dev_err_probe(dev, PTR_ERR(reset), "Failed to get/deassert reset control\n");
221 
222 	clk = devm_clk_get_enabled(dev, "core");
223 	if (IS_ERR(clk))
224 		return dev_err_probe(dev, PTR_ERR(clk), "Failed to get core clock\n");
225 
226 	clk = devm_clk_get_enabled(dev, "bus");
227 	if (IS_ERR(clk))
228 		return dev_err_probe(dev, PTR_ERR(clk), "Failed to get bus clock\n");
229 
230 	k1_tsensor_init(ts);
231 
232 	irq = platform_get_irq(pdev, 0);
233 	if (irq < 0)
234 		return irq;
235 
236 	ret = devm_request_threaded_irq(dev, irq, NULL,
237 					k1_tsensor_irq_thread,
238 					IRQF_ONESHOT, "k1_tsensor", ts);
239 	if (ret < 0)
240 		return ret;
241 
242 	for (i = 0; i < MAX_SENSOR_NUMBER; ++i) {
243 		ts->ch[i].id = i;
244 		ts->ch[i].ts = ts;
245 		ts->ch[i].tzd = devm_thermal_of_zone_register(dev, i, ts->ch + i, &k1_tsensor_ops);
246 		if (IS_ERR(ts->ch[i].tzd))
247 			return PTR_ERR(ts->ch[i].tzd);
248 
249 		/* Attach sysfs hwmon attributes for userspace monitoring */
250 		ret = devm_thermal_add_hwmon_sysfs(dev, ts->ch[i].tzd);
251 		if (ret)
252 			dev_warn(dev, "Failed to add hwmon sysfs attributes\n");
253 
254 		k1_tsensor_enable_irq(ts->ch + i);
255 	}
256 
257 	platform_set_drvdata(pdev, ts);
258 
259 	return 0;
260 }
261 
262 static const struct of_device_id k1_tsensor_dt_ids[] = {
263 	{ .compatible = "spacemit,k1-tsensor" },
264 	{ /* sentinel */ }
265 };
266 
267 MODULE_DEVICE_TABLE(of, k1_tsensor_dt_ids);
268 
269 static struct platform_driver k1_tsensor_driver = {
270 	.driver = {
271 		.name		= "k1_tsensor",
272 		.of_match_table = k1_tsensor_dt_ids,
273 	},
274 	.probe	= k1_tsensor_probe,
275 };
276 module_platform_driver(k1_tsensor_driver);
277 
278 MODULE_DESCRIPTION("SpacemiT K1 Thermal Sensor Driver");
279 MODULE_AUTHOR("Shuwei Wu <shuwei.wu@mailbox.org>");
280 MODULE_LICENSE("GPL");
281