Linux Audio

Check our new training course

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