Linux Audio

Check our new training course

Loading...
v6.13.7
  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}
v5.4
  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 (!data || IS_ERR(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 (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 (data) {
266		cpufreq_cooling_unregister(data->cool_dev);
267		if (data->policy)
268			cpufreq_cpu_put(data->policy);
269	}
270
271	return 0;
272}