File Index
Files and directories
Sign in
FILE

st_thermal.c

File preview with syntax highlighting.

linux-6.19.14/drivers/thermal/st/st_thermal.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * ST Thermal Sensor Driver core routines
  4 * Author: Ajit Pal Singh <ajitpal.singh@st.com>
  5 *
  6 * Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited
  7 */
  8
  9#include <linux/clk.h>
 10#include <linux/module.h>
 11#include <linux/of.h>
 12#include <linux/of_device.h>
 13
 14#include "st_thermal.h"
 15#include "../thermal_hwmon.h"
 16
 17/* The Thermal Framework expects millidegrees */
 18#define mcelsius(temp)			((temp) * 1000)
 19
 20/*
 21 * Function to allocate regfields which are common
 22 * between syscfg and memory mapped based sensors
 23 */
 24static int st_thermal_alloc_regfields(struct st_thermal_sensor *sensor)
 25{
 26	struct device *dev = sensor->dev;
 27	struct regmap *regmap = sensor->regmap;
 28	const struct reg_field *reg_fields = sensor->cdata->reg_fields;
 29
 30	sensor->dcorrect = devm_regmap_field_alloc(dev, regmap,
 31						   reg_fields[DCORRECT]);
 32
 33	sensor->overflow = devm_regmap_field_alloc(dev, regmap,
 34						   reg_fields[OVERFLOW]);
 35
 36	sensor->temp_data = devm_regmap_field_alloc(dev, regmap,
 37						    reg_fields[DATA]);
 38
 39	if (IS_ERR(sensor->dcorrect) ||
 40	    IS_ERR(sensor->overflow) ||
 41	    IS_ERR(sensor->temp_data)) {
 42		dev_err(dev, "failed to allocate common regfields\n");
 43		return -EINVAL;
 44	}
 45
 46	return sensor->ops->alloc_regfields(sensor);
 47}
 48
 49static int st_thermal_sensor_on(struct st_thermal_sensor *sensor)
 50{
 51	int ret;
 52	struct device *dev = sensor->dev;
 53
 54	ret = clk_prepare_enable(sensor->clk);
 55	if (ret) {
 56		dev_err(dev, "failed to enable clk\n");
 57		return ret;
 58	}
 59
 60	ret = sensor->ops->power_ctrl(sensor, POWER_ON);
 61	if (ret) {
 62		dev_err(dev, "failed to power on sensor\n");
 63		clk_disable_unprepare(sensor->clk);
 64	}
 65
 66	return ret;
 67}
 68
 69static int st_thermal_sensor_off(struct st_thermal_sensor *sensor)
 70{
 71	int ret;
 72
 73	ret = sensor->ops->power_ctrl(sensor, POWER_OFF);
 74	if (ret)
 75		return ret;
 76
 77	clk_disable_unprepare(sensor->clk);
 78
 79	return 0;
 80}
 81
 82static int st_thermal_calibration(struct st_thermal_sensor *sensor)
 83{
 84	int ret;
 85	unsigned int val;
 86	struct device *dev = sensor->dev;
 87
 88	/* Check if sensor calibration data is already written */
 89	ret = regmap_field_read(sensor->dcorrect, &val);
 90	if (ret) {
 91		dev_err(dev, "failed to read calibration data\n");
 92		return ret;
 93	}
 94
 95	if (!val) {
 96		/*
 97		 * Sensor calibration value not set by bootloader,
 98		 * default calibration data to be used
 99		 */
100		ret = regmap_field_write(sensor->dcorrect,
101					 sensor->cdata->calibration_val);
102		if (ret)
103			dev_err(dev, "failed to set calibration data\n");
104	}
105
106	return ret;
107}
108
109/* Callback to get temperature from HW*/
110static int st_thermal_get_temp(struct thermal_zone_device *th, int *temperature)
111{
112	struct st_thermal_sensor *sensor = thermal_zone_device_priv(th);
113	unsigned int temp;
114	unsigned int overflow;
115	int ret;
116
117	ret = regmap_field_read(sensor->overflow, &overflow);
118	if (ret)
119		return ret;
120	if (overflow)
121		return -EIO;
122
123	ret = regmap_field_read(sensor->temp_data, &temp);
124	if (ret)
125		return ret;
126
127	temp += sensor->cdata->temp_adjust_val;
128	temp = mcelsius(temp);
129
130	*temperature = temp;
131
132	return 0;
133}
134
135static const struct thermal_zone_device_ops st_tz_ops = {
136	.get_temp	= st_thermal_get_temp,
137};
138
139int st_thermal_register(struct platform_device *pdev,
140			const struct of_device_id *st_thermal_of_match)
141{
142	struct st_thermal_sensor *sensor;
143	struct device *dev = &pdev->dev;
144	struct device_node *np = dev->of_node;
145	const struct of_device_id *match;
146
147	int ret;
148
149	if (!np) {
150		dev_err(dev, "device tree node not found\n");
151		return -EINVAL;
152	}
153
154	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
155	if (!sensor)
156		return -ENOMEM;
157
158	sensor->dev = dev;
159
160	match = of_match_device(st_thermal_of_match, dev);
161	if (!(match && match->data))
162		return -EINVAL;
163
164	sensor->cdata = match->data;
165	if (!sensor->cdata->ops)
166		return -EINVAL;
167
168	sensor->ops = sensor->cdata->ops;
169
170	ret = (sensor->ops->regmap_init)(sensor);
171	if (ret)
172		return ret;
173
174	ret = st_thermal_alloc_regfields(sensor);
175	if (ret)
176		return ret;
177
178	sensor->clk = devm_clk_get(dev, "thermal");
179	if (IS_ERR(sensor->clk)) {
180		dev_err(dev, "failed to fetch clock\n");
181		return PTR_ERR(sensor->clk);
182	}
183
184	if (sensor->ops->register_enable_irq) {
185		ret = sensor->ops->register_enable_irq(sensor);
186		if (ret)
187			return ret;
188	}
189
190	ret = st_thermal_sensor_on(sensor);
191	if (ret)
192		return ret;
193
194	ret = st_thermal_calibration(sensor);
195	if (ret)
196		goto sensor_off;
197
198	sensor->thermal_dev =
199		devm_thermal_of_zone_register(dev, 0, sensor, &st_tz_ops);
200	if (IS_ERR(sensor->thermal_dev)) {
201		dev_err(dev, "failed to register thermal of zone\n");
202		ret = PTR_ERR(sensor->thermal_dev);
203		goto sensor_off;
204	}
205
206	platform_set_drvdata(pdev, sensor);
207
208	/*
209	 * devm_thermal_of_zone_register() doesn't enable hwmon by default
210	 * Enable it here
211	 */
212	devm_thermal_add_hwmon_sysfs(dev, sensor->thermal_dev);
213
214	return 0;
215
216sensor_off:
217	st_thermal_sensor_off(sensor);
218
219	return ret;
220}
221EXPORT_SYMBOL_GPL(st_thermal_register);
222
223void st_thermal_unregister(struct platform_device *pdev)
224{
225	struct st_thermal_sensor *sensor = platform_get_drvdata(pdev);
226
227	st_thermal_sensor_off(sensor);
228	thermal_remove_hwmon_sysfs(sensor->thermal_dev);
229	devm_thermal_of_zone_unregister(sensor->dev, sensor->thermal_dev);
230}
231EXPORT_SYMBOL_GPL(st_thermal_unregister);
232
233static int st_thermal_suspend(struct device *dev)
234{
235	struct st_thermal_sensor *sensor = dev_get_drvdata(dev);
236
237	return st_thermal_sensor_off(sensor);
238}
239
240static int st_thermal_resume(struct device *dev)
241{
242	int ret;
243	struct st_thermal_sensor *sensor = dev_get_drvdata(dev);
244
245	ret = st_thermal_sensor_on(sensor);
246	if (ret)
247		return ret;
248
249	ret = st_thermal_calibration(sensor);
250	if (ret)
251		return ret;
252
253	if (sensor->ops->enable_irq) {
254		ret = sensor->ops->enable_irq(sensor);
255		if (ret)
256			return ret;
257	}
258
259	return 0;
260}
261
262DEFINE_SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume);
263EXPORT_SYMBOL_GPL(st_thermal_pm_ops);
264
265MODULE_AUTHOR("STMicroelectronics (R&D) Limited <ajitpal.singh@st.com>");
266MODULE_DESCRIPTION("STMicroelectronics STi SoC Thermal Sensor Driver");
267MODULE_LICENSE("GPL v2");
~/ Read-only file preview.