xref: /linux/drivers/hwmon/eic7700-pvt.c (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * ESWIN EIC7700 Voltage, Temperature sensor driver
4  *
5  * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.
6  *
7  * Authors:
8  *   Yulin Lu <luyulin@eswincomputing.com>
9  *   Huan He <hehuan1@eswincomputing.com>
10  */
11 
12 #include <linux/bitfield.h>
13 #include <linux/clk.h>
14 #include <linux/delay.h>
15 #include <linux/device.h>
16 #include <linux/interrupt.h>
17 #include <linux/io.h>
18 #include <linux/module.h>
19 #include <linux/of.h>
20 #include <linux/platform_device.h>
21 #include <linux/pm_runtime.h>
22 #include <linux/polynomial.h>
23 #include <linux/reset.h>
24 #include "eic7700-pvt.h"
25 
26 static const struct pvt_sensor_info pvt_info[] = {
27 	PVT_SENSOR_INFO(0, "Temperature", hwmon_temp, TEMP),
28 	PVT_SENSOR_INFO(0, "Voltage", hwmon_in, VOLT),
29 };
30 
31 static const char * const pvt_clk_names[PVT_CLK_NUM] = {"enable", "apb"};
32 
33 /*
34  * The original translation formulae of the temperature (in degrees of Celsius)
35  * to PVT data and vice-versa are following:
36  * N = 6.0818e-8*(T^4) +1.2873e-5*(T^3) + 7.2244e-3*(T^2) + 3.6484*(T^1) +
37  *     1.6198e2,
38  * T = -1.8439e-11*(N^4) + 8.0705e-8*(N^3) + -1.8501e-4*(N^2) +
39  *     3.2843e-1*(N^1) - 4.8690e1,
40  * where T = [-40, 125]C and N = [27, 771].
41  * They must be accordingly altered to be suitable for the integer arithmetics.
42  * The technique is called 'factor redistribution', which just makes sure the
43  * multiplications and divisions are made so to have a result of the operations
44  * within the integer numbers limit. In addition we need to translate the
45  * formulae to accept millidegrees of Celsius. Here what they look like after
46  * the alterations:
47  * N = (60818e-20*(T^4) + 12873e-14*(T^3) + 72244e-9*(T^2) + 36484e-3*T +
48  *     16198e2) / 1e4,
49  * T = -18439e-12*(N^4) + 80705e-9*(N^3) - 185010e-6*(N^2) + 328430e-3*N -
50  *     48690,
51  * where T = [-40000, 125000] mC and N = [27, 771].
52  */
53 static const struct polynomial poly_N_to_temp = {
54 	.total_divider = 1,
55 	.terms = {
56 		{4, -18439, 1000, 1},
57 		{3, 80705, 1000, 1},
58 		{2, -185010, 1000, 1},
59 		{1, 328430, 1000, 1},
60 		{0, -48690, 1, 1}
61 	}
62 };
63 
64 /*
65  * Similar alterations are performed for the voltage conversion equations.
66  * The original formulae are:
67  * N = 1.3905e3*V - 5.7685e2,
68  * V = (N + 5.7685e2) / 1.3905e3,
69  * where V = [0.72, 0.88] V and N = [424, 646].
70  * After the optimization they looks as follows:
71  * N = (13905e-3*V - 5768.5) / 10,
72  * V = (N * 10^5 / 13905 + 57685 * 10^3 / 13905) / 10.
73  * where V = [720, 880] mV and N = [424, 646].
74  */
75 static const struct polynomial poly_N_to_volt = {
76 	.total_divider = 10,
77 	.terms = {
78 		{1, 100000, 13905, 1},
79 		{0, 57685000, 1, 13905}
80 	}
81 };
82 
83 static inline u32 eic7700_pvt_update(void __iomem *reg, u32 mask, u32 data)
84 {
85 	u32 old;
86 
87 	old = readl_relaxed(reg);
88 	writel((old & ~mask) | (data & mask), reg);
89 
90 	return old & mask;
91 }
92 
93 static inline void eic7700_pvt_set_mode(struct pvt_hwmon *pvt, u32 mode)
94 {
95 	u32 old;
96 
97 	mode = FIELD_PREP(PVT_MODE_MASK, mode);
98 
99 	old = eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0);
100 	eic7700_pvt_update(pvt->regs + PVT_MODE, PVT_MODE_MASK, mode);
101 	eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, old);
102 }
103 
104 static inline void eic7700_pvt_set_trim(struct pvt_hwmon *pvt, u32 val)
105 {
106 	u32 old;
107 
108 	old = eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0);
109 	writel(val, pvt->regs + PVT_TRIM);
110 	eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, old);
111 }
112 
113 static irqreturn_t eic7700_pvt_hard_isr(int irq, void *data)
114 {
115 	struct pvt_hwmon *pvt = data;
116 	u32 stat, val;
117 	int active;
118 
119 	if (IS_ENABLED(CONFIG_PM)) {
120 		active = pm_runtime_get_if_active(pvt->dev);
121 		if (active <= 0)
122 			return IRQ_NONE;
123 	}
124 
125 	stat = readl(pvt->regs + PVT_INT);
126 	if (!(stat & PVT_INT_STAT)) {
127 		if (IS_ENABLED(CONFIG_PM))
128 			pm_runtime_put(pvt->dev);
129 		return IRQ_NONE;
130 	}
131 
132 	eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR);
133 	/*
134 	 * Read the data, update the cache and notify a waiter of this event.
135 	 */
136 	val = readl(pvt->regs + PVT_DATA);
137 	WRITE_ONCE(pvt->data_cache, FIELD_GET(PVT_DATA_OUT, val));
138 	complete(&pvt->conversion);
139 
140 	if (IS_ENABLED(CONFIG_PM))
141 		pm_runtime_put(pvt->dev);
142 
143 	return IRQ_HANDLED;
144 }
145 
146 static int eic7700_pvt_read_data(struct pvt_hwmon *pvt,
147 				 enum pvt_sensor_type type, long *val)
148 {
149 	unsigned long timeout;
150 	u32 data;
151 	int ret;
152 
153 	/*
154 	 * Wait for PVT conversion to complete and update the data cache. The
155 	 * data read procedure is following: set the requested PVT sensor mode,
156 	 * enable conversion, wait until conversion is finished, then disable
157 	 * conversion and IRQ, and read the cached data.
158 	 */
159 	reinit_completion(&pvt->conversion);
160 
161 	eic7700_pvt_set_mode(pvt, pvt_info[type].mode);
162 	eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, PVT_ENA_EN);
163 
164 	/*
165 	 * Wait with timeout since in case if the sensor is suddenly powered
166 	 * down the request won't be completed and the caller will hang up on
167 	 * this procedure until the power is back up again. Multiply the
168 	 * timeout by the factor of two to prevent a false timeout.
169 	 */
170 	timeout = 2 * usecs_to_jiffies(ktime_to_us(pvt->timeout));
171 	ret = wait_for_completion_timeout(&pvt->conversion, timeout);
172 
173 	eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0);
174 	eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR);
175 
176 	if (!ret)
177 		synchronize_irq(pvt->irq);
178 
179 	data = READ_ONCE(pvt->data_cache);
180 
181 	if (!ret)
182 		return -ETIMEDOUT;
183 
184 	if (type == PVT_TEMP)
185 		*val = polynomial_calc(&poly_N_to_temp, data);
186 	else
187 		*val = polynomial_calc(&poly_N_to_volt, data);
188 
189 	return 0;
190 }
191 
192 static const struct hwmon_channel_info *pvt_channel_info[] = {
193 	HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
194 	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL),
195 	HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL),
196 	NULL
197 };
198 
199 static umode_t eic7700_pvt_hwmon_is_visible(const void *data,
200 					    enum hwmon_sensor_types type,
201 					    u32 attr, int ch)
202 {
203 	switch (type) {
204 	case hwmon_temp:
205 		switch (attr) {
206 		case hwmon_temp_input:
207 		case hwmon_temp_label:
208 			return 0444;
209 		}
210 		break;
211 	case hwmon_in:
212 		switch (attr) {
213 		case hwmon_in_input:
214 		case hwmon_in_label:
215 			return 0444;
216 		}
217 		break;
218 	default:
219 		break;
220 	}
221 
222 	return 0;
223 }
224 
225 static int eic7700_pvt_hwmon_read(struct device *dev,
226 				  enum hwmon_sensor_types type, u32 attr,
227 				  int ch, long *val)
228 {
229 	struct pvt_hwmon *pvt = dev_get_drvdata(dev);
230 	int ret;
231 
232 	ret = pm_runtime_get_sync(pvt->dev);
233 	if (ret < 0) {
234 		dev_err(pvt->dev, "Failed to resume PVT device: %d\n", ret);
235 		pm_runtime_put_noidle(pvt->dev);
236 		return ret;
237 	}
238 
239 	switch (type) {
240 	case hwmon_temp:
241 		switch (attr) {
242 		case hwmon_temp_input:
243 			ret = eic7700_pvt_read_data(pvt, ch, val);
244 			break;
245 		default:
246 			ret = -EOPNOTSUPP;
247 		}
248 		break;
249 	case hwmon_in:
250 		if (attr == hwmon_in_input)
251 			ret = eic7700_pvt_read_data(pvt, PVT_VOLT + ch, val);
252 		else
253 			ret = -EOPNOTSUPP;
254 		break;
255 	default:
256 		ret = -EOPNOTSUPP;
257 	}
258 
259 	pm_runtime_mark_last_busy(pvt->dev);
260 	pm_runtime_put_autosuspend(pvt->dev);
261 	return ret;
262 }
263 
264 static int eic7700_pvt_hwmon_read_string(struct device *dev,
265 					 enum hwmon_sensor_types type, u32 attr,
266 					 int ch, const char **str)
267 {
268 	switch (type) {
269 	case hwmon_temp:
270 		if (attr == hwmon_temp_label) {
271 			*str = pvt_info[ch].label;
272 			return 0;
273 		}
274 		break;
275 	case hwmon_in:
276 		if (attr == hwmon_in_label) {
277 			*str = pvt_info[PVT_VOLT + ch].label;
278 			return 0;
279 		}
280 		break;
281 	default:
282 		break;
283 	}
284 
285 	return -EOPNOTSUPP;
286 }
287 
288 static const struct hwmon_ops pvt_hwmon_ops = {
289 	.is_visible = eic7700_pvt_hwmon_is_visible,
290 	.read = eic7700_pvt_hwmon_read,
291 	.read_string = eic7700_pvt_hwmon_read_string
292 };
293 
294 static const struct hwmon_chip_info pvt_hwmon_info = {
295 	.ops = &pvt_hwmon_ops,
296 	.info = pvt_channel_info
297 };
298 
299 static struct pvt_hwmon *eic7700_pvt_create_data(struct platform_device *pdev)
300 {
301 	struct device *dev = &pdev->dev;
302 	struct pvt_hwmon *pvt;
303 
304 	pvt = devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL);
305 	if (!pvt)
306 		return ERR_PTR(-ENOMEM);
307 
308 	pvt->dev = dev;
309 	init_completion(&pvt->conversion);
310 
311 	return pvt;
312 }
313 
314 static int eic7700_pvt_init_iface(struct pvt_hwmon *pvt)
315 {
316 	/*
317 	 * Make sure controller are disabled so not to accidentally have ISR
318 	 * executed before the driver data is fully initialized. Clear the IRQ
319 	 * status as well.
320 	 */
321 	eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0);
322 	eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR);
323 	readl(pvt->regs + PVT_INT);
324 	readl(pvt->regs + PVT_DATA);
325 
326 	/* Setup default sensor mode and temperature trim. */
327 	eic7700_pvt_set_mode(pvt, pvt_info[PVT_TEMP].mode);
328 
329 	/*
330 	 * Max conversion latency (~333 µs) derived from PVT spec:
331 	 * maximum sampling rate = 3000 samples/sec.
332 	 */
333 	pvt->timeout = ns_to_ktime(PVT_TOUT_MIN);
334 
335 	eic7700_pvt_set_trim(pvt, PVT_TRIM_DEF);
336 
337 	return 0;
338 }
339 
340 static int eic7700_pvt_request_irq(struct pvt_hwmon *pvt)
341 {
342 	struct platform_device *pdev = to_platform_device(pvt->dev);
343 	int ret;
344 
345 	pvt->irq = platform_get_irq(pdev, 0);
346 	if (pvt->irq < 0)
347 		return pvt->irq;
348 
349 	ret = devm_request_threaded_irq(pvt->dev, pvt->irq,
350 					eic7700_pvt_hard_isr, NULL,
351 					IRQF_TRIGGER_HIGH, "pvt", pvt);
352 	if (ret) {
353 		dev_err(pvt->dev, "Couldn't request PVT IRQ\n");
354 		return ret;
355 	}
356 
357 	return 0;
358 }
359 
360 static int eic7700_pvt_create_hwmon(struct pvt_hwmon *pvt)
361 {
362 	pvt->hwmon = devm_hwmon_device_register_with_info(pvt->dev, "pvt",
363 							  pvt, &pvt_hwmon_info,
364 							  NULL);
365 	if (IS_ERR(pvt->hwmon)) {
366 		dev_err(pvt->dev, "Couldn't create hwmon device\n");
367 		return PTR_ERR(pvt->hwmon);
368 	}
369 
370 	return 0;
371 }
372 
373 static void eic7700_pvt_disable_pm_runtime(void *data)
374 {
375 	struct pvt_hwmon *pvt = data;
376 
377 	pm_runtime_dont_use_autosuspend(pvt->dev);
378 	pm_runtime_disable(pvt->dev);
379 
380 	if (!pm_runtime_status_suspended(pvt->dev)) {
381 		clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks);
382 		pm_runtime_set_suspended(pvt->dev);
383 	}
384 }
385 
386 static int eic7700_pvt_probe(struct platform_device *pdev)
387 {
388 	struct reset_control *rst;
389 	struct pvt_hwmon *pvt;
390 	int i, ret;
391 
392 	pvt = eic7700_pvt_create_data(pdev);
393 	if (IS_ERR(pvt))
394 		return PTR_ERR(pvt);
395 
396 	platform_set_drvdata(pdev, pvt);
397 
398 	pvt->regs = devm_platform_ioremap_resource(pdev, 0);
399 	if (IS_ERR(pvt->regs))
400 		return PTR_ERR(pvt->regs);
401 
402 	for (i = 0; i < PVT_CLK_NUM; i++)
403 		pvt->clks[i].id = pvt_clk_names[i];
404 
405 	ret = devm_clk_bulk_get(&pdev->dev, PVT_CLK_NUM, pvt->clks);
406 	if (ret)
407 		return dev_err_probe(&pdev->dev, ret,
408 				     "Couldn't get clock descriptors\n");
409 
410 	rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL);
411 	if (IS_ERR(rst))
412 		return dev_err_probe(pvt->dev, PTR_ERR(rst),
413 				     "Couldn't get reset control\n");
414 
415 	ret = clk_bulk_prepare_enable(PVT_CLK_NUM, pvt->clks);
416 	if (ret)
417 		return dev_err_probe(pvt->dev, ret,
418 				     "Failed to enable clocks\n");
419 
420 	ret = eic7700_pvt_init_iface(pvt);
421 	if (ret) {
422 		clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks);
423 		return ret;
424 	}
425 
426 	if (IS_ENABLED(CONFIG_PM))
427 		clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks);
428 
429 	pm_runtime_enable(&pdev->dev);
430 	pm_runtime_set_autosuspend_delay(&pdev->dev, 3000);
431 	pm_runtime_use_autosuspend(&pdev->dev);
432 	pm_runtime_get_noresume(&pdev->dev);
433 
434 	ret = devm_add_action_or_reset(pvt->dev, eic7700_pvt_disable_pm_runtime,
435 				       pvt);
436 	if (ret) {
437 		pm_runtime_put_noidle(&pdev->dev);
438 		return dev_err_probe(&pdev->dev, ret,
439 				     "Can't register PM cleanup\n");
440 	}
441 
442 	ret = eic7700_pvt_request_irq(pvt);
443 	if (ret)
444 		goto err_put_pm_runtime;
445 
446 	ret = eic7700_pvt_create_hwmon(pvt);
447 	if (ret)
448 		goto err_put_pm_runtime;
449 
450 	pm_runtime_put_autosuspend(&pdev->dev);
451 
452 	return 0;
453 
454 err_put_pm_runtime:
455 	pm_runtime_put_noidle(&pdev->dev);
456 	return ret;
457 }
458 
459 static int __maybe_unused eic7700_pvt_runtime_resume(struct device *dev)
460 {
461 	struct pvt_hwmon *pvt = dev_get_drvdata(dev);
462 	int ret;
463 
464 	ret = clk_bulk_prepare_enable(PVT_CLK_NUM, pvt->clks);
465 	if (ret) {
466 		dev_err(dev, "Failed to enable clocks: %d\n", ret);
467 		return ret;
468 	}
469 
470 	eic7700_pvt_set_trim(pvt, PVT_TRIM_DEF);
471 
472 	return 0;
473 }
474 
475 static int __maybe_unused eic7700_pvt_runtime_suspend(struct device *dev)
476 {
477 	struct pvt_hwmon *pvt = dev_get_drvdata(dev);
478 
479 	clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks);
480 
481 	return 0;
482 }
483 
484 static const struct dev_pm_ops eic7700_pvt_pm_ops = {
485 	SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
486 	RUNTIME_PM_OPS(eic7700_pvt_runtime_suspend, eic7700_pvt_runtime_resume,
487 		       NULL)
488 };
489 
490 static const struct of_device_id pvt_of_match[] = {
491 	{ .compatible = "eswin,eic7700-pvt"},
492 	{ }
493 };
494 MODULE_DEVICE_TABLE(of, pvt_of_match);
495 
496 static struct platform_driver pvt_driver = {
497 	.probe = eic7700_pvt_probe,
498 	.driver = {
499 		.name = "eic7700-pvt",
500 		.of_match_table = pvt_of_match,
501 		.pm = pm_ptr(&eic7700_pvt_pm_ops),
502 	},
503 };
504 module_platform_driver(pvt_driver);
505 
506 MODULE_AUTHOR("Yulin Lu <luyulin@eswincomputing.com>");
507 MODULE_AUTHOR("Huan He <hehuan1@eswincomputing.com>");
508 MODULE_DESCRIPTION("Eswin eic7700 PVT driver");
509 MODULE_LICENSE("GPL");
510