1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * OMAP thermal driver interface
4 *
5 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
6 * Contact:
7 *   Eduardo Valentin <eduardo.valentin@ti.com>
8 */
9
10#include <linux/device.h>
11#include <linux/err.h>
12#include <linux/mutex.h>
13#include <linux/gfp.h>
14#include <linux/kernel.h>
15#include <linux/workqueue.h>
16#include <linux/thermal.h>
17#include <linux/cpufreq.h>
18#include <linux/cpumask.h>
19#include <linux/cpu_cooling.h>
20#include <linux/of.h>
21
22#include "ti-thermal.h"
23#include "ti-bandgap.h"
24#include "../thermal_hwmon.h"
25
26#define TI_BANDGAP_UPDATE_INTERVAL_MS 250
27
28/* common data structures */
29struct ti_thermal_data {
30	struct cpufreq_policy *policy;
31	struct thermal_zone_device *ti_thermal;
32	struct thermal_zone_device *pcb_tz;
33	struct thermal_cooling_device *cool_dev;
34	struct ti_bandgap *bgp;
35	enum thermal_device_mode mode;
36	struct work_struct thermal_wq;
37	int sensor_id;
38	bool our_zone;
39};
40
41static void ti_thermal_work(struct work_struct *work)
42{
43	struct ti_thermal_data *data = container_of(work,
44					struct ti_thermal_data, thermal_wq);
45
46	thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED);
47
48	dev_dbg(data->bgp->dev, "updated thermal zone %s\n",
49		thermal_zone_device_type(data->ti_thermal));
50}
51
52/**
53 * ti_thermal_hotspot_temperature - returns sensor extrapolated temperature
54 * @t:	omap sensor temperature
55 * @s:	omap sensor slope value
56 * @c:	omap sensor const value
57 */
58static inline int ti_thermal_hotspot_temperature(int t, int s, int c)
59{
60	int delta = t * s / 1000 + c;
61
62	if (delta < 0)
63		delta = 0;
64
65	return t + delta;
66}
67
68/* thermal zone ops */
69/* Get temperature callback function for thermal zone */
70static inline int __ti_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
71{
72	struct thermal_zone_device *pcb_tz = NULL;
73	struct ti_thermal_data *data = thermal_zone_device_priv(tz);
74	struct ti_bandgap *bgp;
75	const struct ti_temp_sensor *s;
76	int ret, tmp, slope, constant;
77	int pcb_temp;
78
79	if (!data)
80		return 0;
81
82	bgp = data->bgp;
83	s = &bgp->conf->sensors[data->sensor_id];
84
85	ret = ti_bandgap_read_temperature(bgp, data->sensor_id, &tmp);
86	if (ret)
87		return ret;
88
89	/* Default constants */
90	slope = thermal_zone_get_slope(tz);
91	constant = thermal_zone_get_offset(tz);
92
93	pcb_tz = data->pcb_tz;
94	/* In case pcb zone is available, use the extrapolation rule with it */
95	if (!IS_ERR(pcb_tz)) {
96		ret = thermal_zone_get_temp(pcb_tz, &pcb_temp);
97		if (!ret) {
98			tmp -= pcb_temp; /* got a valid PCB temp */
99			slope = s->slope_pcb;
100			constant = s->constant_pcb;
101		} else {
102			dev_err(bgp->dev,
103				"Failed to read PCB state. Using defaults\n");
104			ret = 0;
105		}
106	}
107	*temp = ti_thermal_hotspot_temperature(tmp, slope, constant);
108
109	return ret;
110}
111
112static int __ti_thermal_get_trend(struct thermal_zone_device *tz,
113				  const struct thermal_trip *trip,
114				  enum thermal_trend *trend)
115{
116	struct ti_thermal_data *data = thermal_zone_device_priv(tz);
117	struct ti_bandgap *bgp;
118	int id, tr, ret = 0;
119
120	bgp = data->bgp;
121	id = data->sensor_id;
122
123	ret = ti_bandgap_get_trend(bgp, id, &tr);
124	if (ret)
125		return ret;
126
127	if (tr > 0)
128		*trend = THERMAL_TREND_RAISING;
129	else if (tr < 0)
130		*trend = THERMAL_TREND_DROPPING;
131	else
132		*trend = THERMAL_TREND_STABLE;
133
134	return 0;
135}
136
137static const struct thermal_zone_device_ops ti_of_thermal_ops = {
138	.get_temp = __ti_thermal_get_temp,
139	.get_trend = __ti_thermal_get_trend,
140};
141
142static struct ti_thermal_data
143*ti_thermal_build_data(struct ti_bandgap *bgp, int id)
144{
145	struct ti_thermal_data *data;
146
147	data = devm_kzalloc(bgp->dev, sizeof(*data), GFP_KERNEL);
148	if (!data) {
149		dev_err(bgp->dev, "kzalloc fail\n");
150		return NULL;
151	}
152	data->sensor_id = id;
153	data->bgp = bgp;
154	data->mode = THERMAL_DEVICE_ENABLED;
155	/* pcb_tz will be either valid or PTR_ERR() */
156	data->pcb_tz = thermal_zone_get_zone_by_name("pcb");
157	INIT_WORK(&data->thermal_wq, ti_thermal_work);
158
159	return data;
160}
161
162int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
163			     char *domain)
164{
165	struct ti_thermal_data *data;
166
167	data = ti_bandgap_get_sensor_data(bgp, id);
168
169	if (IS_ERR_OR_NULL(data))
170		data = ti_thermal_build_data(bgp, id);
171
172	if (!data)
173		return -EINVAL;
174
175	/* in case this is specified by DT */
176	data->ti_thermal = devm_thermal_of_zone_register(bgp->dev, id,
177					data, &ti_of_thermal_ops);
178	if (IS_ERR(data->ti_thermal)) {
179		dev_err(bgp->dev, "thermal zone device is NULL\n");
180		return PTR_ERR(data->ti_thermal);
181	}
182
183	ti_bandgap_set_sensor_data(bgp, id, data);
184	ti_bandgap_write_update_interval(bgp, data->sensor_id,
185					 TI_BANDGAP_UPDATE_INTERVAL_MS);
186
187	devm_thermal_add_hwmon_sysfs(bgp->dev, data->ti_thermal);
188
189	return 0;
190}
191
192int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
193{
194	struct ti_thermal_data *data;
195
196	data = ti_bandgap_get_sensor_data(bgp, id);
197
198	if (!IS_ERR_OR_NULL(data) && data->ti_thermal) {
199		if (data->our_zone)
200			thermal_zone_device_unregister(data->ti_thermal);
201	}
202
203	return 0;
204}
205
206int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id)
207{
208	struct ti_thermal_data *data;
209
210	data = ti_bandgap_get_sensor_data(bgp, id);
211
212	schedule_work(&data->thermal_wq);
213
214	return 0;
215}
216
217int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)
218{
219	struct ti_thermal_data *data;
220	struct device_node *np = bgp->dev->of_node;
221
222	/*
223	 * We are assuming here that if one deploys the zone
224	 * using DT, then it must be aware that the cooling device
225	 * loading has to happen via cpufreq driver.
226	 */
227	if (of_property_present(np, "#thermal-sensor-cells"))
228		return 0;
229
230	data = ti_bandgap_get_sensor_data(bgp, id);
231	if (!data || IS_ERR(data))
232		data = ti_thermal_build_data(bgp, id);
233
234	if (!data)
235		return -EINVAL;
236
237	data->policy = cpufreq_cpu_get(0);
238	if (!data->policy) {
239		pr_debug("%s: CPUFreq policy not found\n", __func__);
240		return -EPROBE_DEFER;
241	}
242
243	/* Register cooling device */
244	data->cool_dev = cpufreq_cooling_register(data->policy);
245	if (IS_ERR(data->cool_dev)) {
246		int ret = PTR_ERR(data->cool_dev);
247		dev_err(bgp->dev, "Failed to register cpu cooling device %d\n",
248			ret);
249		cpufreq_cpu_put(data->policy);
250
251		return ret;
252	}
253	ti_bandgap_set_sensor_data(bgp, id, data);
254
255	return 0;
256}
257
258int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id)
259{
260	struct ti_thermal_data *data;
261
262	data = ti_bandgap_get_sensor_data(bgp, id);
263
264	if (!IS_ERR_OR_NULL(data)) {
265		cpufreq_cooling_unregister(data->cool_dev);
266		if (data->policy)
267			cpufreq_cpu_put(data->policy);
268	}
269
270	return 0;
271}
272