File Index
Files and directories
Sign in
FILE

rzg3s_thermal.c

File preview with syntax highlighting.

linux-6.19.14/drivers/thermal/renesas/rzg3s_thermal.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Renesas RZ/G3S TSU Thermal Sensor Driver
  4 *
  5 * Copyright (C) 2024 Renesas Electronics Corporation
  6 */
  7
  8#include <linux/bitfield.h>
  9#include <linux/delay.h>
 10#include <linux/iio/consumer.h>
 11#include <linux/io.h>
 12#include <linux/module.h>
 13#include <linux/platform_device.h>
 14#include <linux/pm_runtime.h>
 15#include <linux/reset.h>
 16#include <linux/thermal.h>
 17#include <linux/units.h>
 18
 19#include "../thermal_hwmon.h"
 20
 21#define TSU_SM			0x0
 22#define TSU_SM_EN		BIT(0)
 23#define TSU_SM_OE		BIT(1)
 24#define OTPTSUTRIM_REG(n)	(0x18 + (n) * 0x4)
 25#define OTPTSUTRIM_EN_MASK	BIT(31)
 26#define OTPTSUTRIM_MASK		GENMASK(11, 0)
 27
 28#define TSU_READ_STEPS		8
 29
 30/* Default calibration values, if FUSE values are missing. */
 31#define SW_CALIB0_VAL		1297
 32#define SW_CALIB1_VAL		751
 33
 34#define MCELSIUS(temp)		((temp) * MILLIDEGREE_PER_DEGREE)
 35
 36/**
 37 * struct rzg3s_thermal_priv - RZ/G3S thermal private data structure
 38 * @base: TSU base address
 39 * @dev: device pointer
 40 * @tz: thermal zone pointer
 41 * @rstc: reset control
 42 * @channel: IIO channel to read the TSU
 43 * @mode: current device mode
 44 * @calib0: calibration value
 45 * @calib1: calibration value
 46 */
 47struct rzg3s_thermal_priv {
 48	void __iomem *base;
 49	struct device *dev;
 50	struct thermal_zone_device *tz;
 51	struct reset_control *rstc;
 52	struct iio_channel *channel;
 53	enum thermal_device_mode mode;
 54	u16 calib0;
 55	u16 calib1;
 56};
 57
 58static int rzg3s_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
 59{
 60	struct rzg3s_thermal_priv *priv = thermal_zone_device_priv(tz);
 61	int ts_code_ave = 0;
 62
 63	if (priv->mode != THERMAL_DEVICE_ENABLED)
 64		return -EAGAIN;
 65
 66	for (u8 i = 0; i < TSU_READ_STEPS; i++) {
 67		int ret, val;
 68
 69		ret = iio_read_channel_raw(priv->channel, &val);
 70		if (ret < 0)
 71			return ret;
 72
 73		ts_code_ave += val;
 74		/*
 75		 * According to the HW manual (Rev.1.10, section 40.4.4 Procedure for Measuring
 76		 * the Temperature) we need to wait here at leat 3us.
 77		 */
 78		usleep_range(5, 10);
 79	}
 80
 81	ts_code_ave = DIV_ROUND_CLOSEST(MCELSIUS(ts_code_ave), TSU_READ_STEPS);
 82
 83	/*
 84	 * According to the HW manual (Rev.1.10, section 40.4.4 Procedure for Measuring the
 85	 * Temperature) the computation formula is as follows:
 86	 *
 87	 * Tj = (ts_code_ave - priv->calib1) * 165 / (priv->calib0 - priv->calib1) - 40
 88	 *
 89	 * Convert everything to milli Celsius before applying the formula to avoid
 90	 * losing precision.
 91	 */
 92
 93	*temp = div_s64((s64)(ts_code_ave - MCELSIUS(priv->calib1)) * MCELSIUS(165),
 94			MCELSIUS(priv->calib0 - priv->calib1)) - MCELSIUS(40);
 95
 96	/* Report it in milli degrees Celsius and round it up to 0.5 degrees Celsius. */
 97	*temp = roundup(*temp, 500);
 98
 99	return 0;
100}
101
102static void rzg3s_thermal_set_mode(struct rzg3s_thermal_priv *priv,
103				   enum thermal_device_mode mode)
104{
105	struct device *dev = priv->dev;
106	int ret;
107
108	ret = pm_runtime_resume_and_get(dev);
109	if (ret)
110		return;
111
112	if (mode == THERMAL_DEVICE_DISABLED) {
113		writel(0, priv->base + TSU_SM);
114	} else {
115		writel(TSU_SM_EN, priv->base + TSU_SM);
116		/*
117		 * According to the HW manual (Rev.1.10, section 40.4.1 Procedure for
118		 * Starting the TSU) we need to wait here 30us or more.
119		 */
120		usleep_range(30, 40);
121
122		writel(TSU_SM_OE | TSU_SM_EN, priv->base + TSU_SM);
123		/*
124		 * According to the HW manual (Rev.1.10, section 40.4.1 Procedure for
125		 * Starting the TSU) we need to wait here 50us or more.
126		 */
127		usleep_range(50, 60);
128	}
129
130	pm_runtime_put_autosuspend(dev);
131}
132
133static int rzg3s_thermal_change_mode(struct thermal_zone_device *tz,
134				     enum thermal_device_mode mode)
135{
136	struct rzg3s_thermal_priv *priv = thermal_zone_device_priv(tz);
137
138	if (priv->mode == mode)
139		return 0;
140
141	rzg3s_thermal_set_mode(priv, mode);
142	priv->mode = mode;
143
144	return 0;
145}
146
147static const struct thermal_zone_device_ops rzg3s_tz_of_ops = {
148	.get_temp = rzg3s_thermal_get_temp,
149	.change_mode = rzg3s_thermal_change_mode,
150};
151
152static int rzg3s_thermal_read_calib(struct rzg3s_thermal_priv *priv)
153{
154	struct device *dev = priv->dev;
155	u32 val;
156	int ret;
157
158	ret = pm_runtime_resume_and_get(dev);
159	if (ret)
160		return ret;
161
162	val = readl(priv->base + OTPTSUTRIM_REG(0));
163	if (val & OTPTSUTRIM_EN_MASK)
164		priv->calib0 = FIELD_GET(OTPTSUTRIM_MASK, val);
165	else
166		priv->calib0 = SW_CALIB0_VAL;
167
168	val = readl(priv->base + OTPTSUTRIM_REG(1));
169	if (val & OTPTSUTRIM_EN_MASK)
170		priv->calib1 = FIELD_GET(OTPTSUTRIM_MASK, val);
171	else
172		priv->calib1 = SW_CALIB1_VAL;
173
174	pm_runtime_put_autosuspend(dev);
175
176	return 0;
177}
178
179static int rzg3s_thermal_probe(struct platform_device *pdev)
180{
181	struct rzg3s_thermal_priv *priv;
182	struct device *dev = &pdev->dev;
183	int ret;
184
185	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
186	if (!priv)
187		return -ENOMEM;
188
189	priv->base = devm_platform_ioremap_resource(pdev, 0);
190	if (IS_ERR(priv->base))
191		return PTR_ERR(priv->base);
192
193	priv->channel = devm_iio_channel_get(dev, "tsu");
194	if (IS_ERR(priv->channel))
195		return dev_err_probe(dev, PTR_ERR(priv->channel), "Failed to get IIO channel!\n");
196
197	priv->rstc = devm_reset_control_get_exclusive_deasserted(dev, NULL);
198	if (IS_ERR(priv->rstc))
199		return dev_err_probe(dev, PTR_ERR(priv->rstc), "Failed to get reset!\n");
200
201	priv->dev = dev;
202	priv->mode = THERMAL_DEVICE_DISABLED;
203	platform_set_drvdata(pdev, priv);
204
205	pm_runtime_set_autosuspend_delay(dev, 300);
206	pm_runtime_use_autosuspend(dev);
207	ret = devm_pm_runtime_enable(dev);
208	if (ret)
209		return dev_err_probe(dev, ret, "Failed to enable runtime PM!\n");
210
211	ret = rzg3s_thermal_read_calib(priv);
212	if (ret)
213		return dev_err_probe(dev, ret, "Failed to read calibration data!\n");
214
215	priv->tz = devm_thermal_of_zone_register(dev, 0, priv, &rzg3s_tz_of_ops);
216	if (IS_ERR(priv->tz))
217		return dev_err_probe(dev, PTR_ERR(priv->tz), "Failed to register thermal zone!\n");
218
219	ret = devm_thermal_add_hwmon_sysfs(dev, priv->tz);
220	if (ret)
221		return dev_err_probe(dev, ret, "Failed to add hwmon sysfs!\n");
222
223	return 0;
224}
225
226static int rzg3s_thermal_suspend(struct device *dev)
227{
228	struct rzg3s_thermal_priv *priv = dev_get_drvdata(dev);
229
230	rzg3s_thermal_set_mode(priv, THERMAL_DEVICE_DISABLED);
231
232	return reset_control_assert(priv->rstc);
233}
234
235static int rzg3s_thermal_resume(struct device *dev)
236{
237	struct rzg3s_thermal_priv *priv = dev_get_drvdata(dev);
238	int ret;
239
240	ret = reset_control_deassert(priv->rstc);
241	if (ret)
242		return ret;
243
244	if (priv->mode != THERMAL_DEVICE_DISABLED)
245		rzg3s_thermal_set_mode(priv, priv->mode);
246
247	return 0;
248}
249
250static const struct dev_pm_ops rzg3s_thermal_pm_ops = {
251	SYSTEM_SLEEP_PM_OPS(rzg3s_thermal_suspend, rzg3s_thermal_resume)
252};
253
254static const struct of_device_id rzg3s_thermal_dt_ids[] = {
255	{ .compatible = "renesas,r9a08g045-tsu" },
256	{ /* sentinel */ }
257};
258MODULE_DEVICE_TABLE(of, rzg3s_thermal_dt_ids);
259
260static struct platform_driver rzg3s_thermal_driver = {
261	.driver = {
262		.name = "rzg3s-thermal",
263		.of_match_table = rzg3s_thermal_dt_ids,
264		.pm = pm_ptr(&rzg3s_thermal_pm_ops),
265	},
266	.probe = rzg3s_thermal_probe,
267};
268module_platform_driver(rzg3s_thermal_driver);
269
270MODULE_DESCRIPTION("Renesas RZ/G3S Thermal Sensor Unit Driver");
271MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>");
272MODULE_LICENSE("GPL");
~/ Read-only file preview.