ti-thermal-common.c 9.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
 * OMAP thermal driver interface
 *
 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
 * Contact:
 *   Eduardo Valentin <eduardo.valentin@ti.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include <linux/device.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/workqueue.h>
#include <linux/thermal.h>
#include <linux/cpufreq.h>
32
#include <linux/cpumask.h>
33 34
#include <linux/cpu_cooling.h>

35 36
#include "ti-thermal.h"
#include "ti-bandgap.h"
37 38

/* common data structures */
39 40
struct ti_thermal_data {
	struct thermal_zone_device *ti_thermal;
41
	struct thermal_zone_device *pcb_tz;
42
	struct thermal_cooling_device *cool_dev;
43
	struct ti_bandgap *bgp;
44 45 46 47 48
	enum thermal_device_mode mode;
	struct work_struct thermal_wq;
	int sensor_id;
};

49
static void ti_thermal_work(struct work_struct *work)
50
{
51 52
	struct ti_thermal_data *data = container_of(work,
					struct ti_thermal_data, thermal_wq);
53

54
	thermal_zone_device_update(data->ti_thermal);
55

56 57
	dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n",
		data->ti_thermal->type);
58 59 60
}

/**
61
 * ti_thermal_hotspot_temperature - returns sensor extrapolated temperature
62 63 64 65
 * @t:	omap sensor temperature
 * @s:	omap sensor slope value
 * @c:	omap sensor const value
 */
66
static inline int ti_thermal_hotspot_temperature(int t, int s, int c)
67 68 69 70 71 72 73 74 75 76 77
{
	int delta = t * s / 1000 + c;

	if (delta < 0)
		delta = 0;

	return t + delta;
}

/* thermal zone ops */
/* Get temperature callback function for thermal zone*/
78 79
static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal,
				      unsigned long *temp)
80
{
81
	struct thermal_zone_device *pcb_tz = NULL;
82 83
	struct ti_thermal_data *data = thermal->devdata;
	struct ti_bandgap *bgp;
84
	const struct ti_temp_sensor *s;
85 86
	int ret, tmp, slope, constant;
	unsigned long pcb_temp;
87

88 89 90
	if (!data)
		return 0;

91 92
	bgp = data->bgp;
	s = &bgp->conf->sensors[data->sensor_id];
93

94
	ret = ti_bandgap_read_temperature(bgp, data->sensor_id, &tmp);
95 96 97
	if (ret)
		return ret;

98 99 100 101 102
	/* Default constants */
	slope = s->slope;
	constant = s->constant;

	pcb_tz = data->pcb_tz;
103
	/* In case pcb zone is available, use the extrapolation rule with it */
104 105 106 107 108 109 110 111 112 113
	if (!IS_ERR_OR_NULL(pcb_tz)) {
		ret = thermal_zone_get_temp(pcb_tz, &pcb_temp);
		if (!ret) {
			tmp -= pcb_temp; /* got a valid PCB temp */
			slope = s->slope_pcb;
			constant = s->constant_pcb;
		} else {
			dev_err(bgp->dev,
				"Failed to read PCB state. Using defaults\n");
		}
114
	}
115
	*temp = ti_thermal_hotspot_temperature(tmp, slope, constant);
116 117 118 119 120

	return ret;
}

/* Bind callback functions for thermal zone */
121 122
static int ti_thermal_bind(struct thermal_zone_device *thermal,
			   struct thermal_cooling_device *cdev)
123
{
124
	struct ti_thermal_data *data = thermal->devdata;
125
	int id;
126 127 128 129 130 131 132 133 134 135 136

	if (IS_ERR_OR_NULL(data))
		return -ENODEV;

	/* check if this is the cooling device we registered */
	if (data->cool_dev != cdev)
		return 0;

	id = data->sensor_id;

	/* Simple thing, two trips, one passive another critical */
E
Eduardo Valentin 已提交
137
	return thermal_zone_bind_cooling_device(thermal, 0, cdev,
138
	/* bind with min and max states defined by cpu_cooling */
E
Eduardo Valentin 已提交
139 140
						THERMAL_NO_LIMIT,
						THERMAL_NO_LIMIT);
141 142 143
}

/* Unbind callback functions for thermal zone */
144 145
static int ti_thermal_unbind(struct thermal_zone_device *thermal,
			     struct thermal_cooling_device *cdev)
146
{
147
	struct ti_thermal_data *data = thermal->devdata;
148 149 150 151 152 153 154 155 156 157 158 159 160

	if (IS_ERR_OR_NULL(data))
		return -ENODEV;

	/* check if this is the cooling device we registered */
	if (data->cool_dev != cdev)
		return 0;

	/* Simple thing, two trips, one passive another critical */
	return thermal_zone_unbind_cooling_device(thermal, 0, cdev);
}

/* Get mode callback functions for thermal zone */
161 162
static int ti_thermal_get_mode(struct thermal_zone_device *thermal,
			       enum thermal_device_mode *mode)
163
{
164
	struct ti_thermal_data *data = thermal->devdata;
165 166 167 168 169 170 171 172

	if (data)
		*mode = data->mode;

	return 0;
}

/* Set mode callback functions for thermal zone */
173 174
static int ti_thermal_set_mode(struct thermal_zone_device *thermal,
			       enum thermal_device_mode mode)
175
{
176
	struct ti_thermal_data *data = thermal->devdata;
177

178
	if (!data->ti_thermal) {
179 180 181 182
		dev_notice(&thermal->device, "thermal zone not registered\n");
		return 0;
	}

183
	mutex_lock(&data->ti_thermal->lock);
184 185

	if (mode == THERMAL_DEVICE_ENABLED)
186
		data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE;
187
	else
188
		data->ti_thermal->polling_delay = 0;
189

190
	mutex_unlock(&data->ti_thermal->lock);
191 192

	data->mode = mode;
193
	thermal_zone_device_update(data->ti_thermal);
194
	dev_dbg(&thermal->device, "thermal polling set for duration=%d msec\n",
195
		data->ti_thermal->polling_delay);
196 197 198 199 200

	return 0;
}

/* Get trip type callback functions for thermal zone */
201 202
static int ti_thermal_get_trip_type(struct thermal_zone_device *thermal,
				    int trip, enum thermal_trip_type *type)
203
{
204
	if (!ti_thermal_is_valid_trip(trip))
205 206 207 208 209 210 211 212 213 214 215
		return -EINVAL;

	if (trip + 1 == OMAP_TRIP_NUMBER)
		*type = THERMAL_TRIP_CRITICAL;
	else
		*type = THERMAL_TRIP_PASSIVE;

	return 0;
}

/* Get trip temperature callback functions for thermal zone */
216 217
static int ti_thermal_get_trip_temp(struct thermal_zone_device *thermal,
				    int trip, unsigned long *temp)
218
{
219
	if (!ti_thermal_is_valid_trip(trip))
220 221
		return -EINVAL;

222
	*temp = ti_thermal_get_trip_value(trip);
223 224 225 226

	return 0;
}

227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
/* Get the temperature trend callback functions for thermal zone */
static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
				int trip, enum thermal_trend *trend)
{
	struct ti_thermal_data *data = thermal->devdata;
	struct ti_bandgap *bgp;
	int id, tr, ret = 0;

	bgp = data->bgp;
	id = data->sensor_id;

	ret = ti_bandgap_get_trend(bgp, id, &tr);
	if (ret)
		return ret;

	if (tr > 0)
		*trend = THERMAL_TREND_RAISING;
	else if (tr < 0)
		*trend = THERMAL_TREND_DROPPING;
	else
		*trend = THERMAL_TREND_STABLE;

	return 0;
}

252
/* Get critical temperature callback functions for thermal zone */
253 254
static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal,
				    unsigned long *temp)
255 256
{
	/* shutdown zone */
257
	return ti_thermal_get_trip_temp(thermal, OMAP_TRIP_NUMBER - 1, temp);
258 259
}

260 261
static struct thermal_zone_device_ops ti_thermal_ops = {
	.get_temp = ti_thermal_get_temp,
262
	.get_trend = ti_thermal_get_trend,
263 264 265 266 267 268 269
	.bind = ti_thermal_bind,
	.unbind = ti_thermal_unbind,
	.get_mode = ti_thermal_get_mode,
	.set_mode = ti_thermal_set_mode,
	.get_trip_type = ti_thermal_get_trip_type,
	.get_trip_temp = ti_thermal_get_trip_temp,
	.get_crit_temp = ti_thermal_get_crit_temp,
270 271
};

272 273
static struct ti_thermal_data
*ti_thermal_build_data(struct ti_bandgap *bgp, int id)
274
{
275
	struct ti_thermal_data *data;
276

277
	data = devm_kzalloc(bgp->dev, sizeof(*data), GFP_KERNEL);
278
	if (!data) {
279
		dev_err(bgp->dev, "kzalloc fail\n");
280
		return NULL;
281 282
	}
	data->sensor_id = id;
283
	data->bgp = bgp;
284
	data->mode = THERMAL_DEVICE_ENABLED;
285
	data->pcb_tz = thermal_zone_get_zone_by_name("pcb");
286
	INIT_WORK(&data->thermal_wq, ti_thermal_work);
287

288 289 290
	return data;
}

291 292
int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
			     char *domain)
293
{
294
	struct ti_thermal_data *data;
295

296
	data = ti_bandgap_get_sensor_data(bgp, id);
297

298
	if (IS_ERR_OR_NULL(data))
299
		data = ti_thermal_build_data(bgp, id);
300 301 302 303

	if (!data)
		return -EINVAL;

304
	/* Create thermal zone */
305 306
	data->ti_thermal = thermal_zone_device_register(domain,
				OMAP_TRIP_NUMBER, 0, data, &ti_thermal_ops,
307
				NULL, FAST_TEMP_MONITORING_RATE,
308
				FAST_TEMP_MONITORING_RATE);
309
	if (IS_ERR_OR_NULL(data->ti_thermal)) {
310
		dev_err(bgp->dev, "thermal zone device is NULL\n");
311
		return PTR_ERR(data->ti_thermal);
312
	}
313 314
	data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE;
	ti_bandgap_set_sensor_data(bgp, id, data);
315 316 317 318

	return 0;
}

319
int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
320
{
321
	struct ti_thermal_data *data;
322

323
	data = ti_bandgap_get_sensor_data(bgp, id);
324

325
	thermal_zone_device_unregister(data->ti_thermal);
326 327 328 329

	return 0;
}

330
int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id)
331
{
332
	struct ti_thermal_data *data;
333

334
	data = ti_bandgap_get_sensor_data(bgp, id);
335 336 337 338 339 340

	schedule_work(&data->thermal_wq);

	return 0;
}

341
int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)
342
{
343
	struct ti_thermal_data *data;
344

345
	data = ti_bandgap_get_sensor_data(bgp, id);
346
	if (IS_ERR_OR_NULL(data))
347
		data = ti_thermal_build_data(bgp, id);
348 349 350

	if (!data)
		return -EINVAL;
351

352 353 354 355 356
	if (!cpufreq_get_current_driver()) {
		dev_dbg(bgp->dev, "no cpufreq driver yet\n");
		return -EPROBE_DEFER;
	}

357
	/* Register cooling device */
358
	data->cool_dev = cpufreq_cooling_register(cpu_present_mask);
359
	if (IS_ERR_OR_NULL(data->cool_dev)) {
360
		dev_err(bgp->dev,
361 362 363
			"Failed to register cpufreq cooling device\n");
		return PTR_ERR(data->cool_dev);
	}
364
	ti_bandgap_set_sensor_data(bgp, id, data);
365 366 367 368

	return 0;
}

369
int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id)
370
{
371
	struct ti_thermal_data *data;
372

373
	data = ti_bandgap_get_sensor_data(bgp, id);
374 375 376 377
	cpufreq_cooling_unregister(data->cool_dev);

	return 0;
}