• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * exynos_thermal_common.c - Samsung EXYNOS common thermal file
3  *
4  *  Copyright (C) 2013 Samsung Electronics
5  *  Amit Daniel Kachhap <amit.daniel@samsung.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22 
23 #include <linux/cpu_cooling.h>
24 #include <linux/err.h>
25 #include <linux/slab.h>
26 #include <linux/thermal.h>
27 
28 #include "exynos_thermal_common.h"
29 
30 struct exynos_thermal_zone {
31 	enum thermal_device_mode mode;
32 	struct thermal_zone_device *therm_dev;
33 	struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
34 	unsigned int cool_dev_size;
35 	struct platform_device *exynos4_dev;
36 	struct thermal_sensor_conf *sensor_conf;
37 	bool bind;
38 };
39 
40 /* Get mode callback functions for thermal zone */
exynos_get_mode(struct thermal_zone_device * thermal,enum thermal_device_mode * mode)41 static int exynos_get_mode(struct thermal_zone_device *thermal,
42 			enum thermal_device_mode *mode)
43 {
44 	struct exynos_thermal_zone *th_zone = thermal->devdata;
45 	if (th_zone)
46 		*mode = th_zone->mode;
47 	return 0;
48 }
49 
50 /* Set mode callback functions for thermal zone */
exynos_set_mode(struct thermal_zone_device * thermal,enum thermal_device_mode mode)51 static int exynos_set_mode(struct thermal_zone_device *thermal,
52 			enum thermal_device_mode mode)
53 {
54 	struct exynos_thermal_zone *th_zone = thermal->devdata;
55 	if (!th_zone) {
56 		dev_err(&thermal->device,
57 			"thermal zone not registered\n");
58 		return 0;
59 	}
60 
61 	mutex_lock(&thermal->lock);
62 
63 	if (mode == THERMAL_DEVICE_ENABLED &&
64 		!th_zone->sensor_conf->trip_data.trigger_falling)
65 		thermal->polling_delay = IDLE_INTERVAL;
66 	else
67 		thermal->polling_delay = 0;
68 
69 	mutex_unlock(&thermal->lock);
70 
71 	th_zone->mode = mode;
72 	thermal_zone_device_update(thermal);
73 	dev_dbg(th_zone->sensor_conf->dev,
74 		"thermal polling set for duration=%d msec\n",
75 		thermal->polling_delay);
76 	return 0;
77 }
78 
79 
80 /* Get trip type callback functions for thermal zone */
exynos_get_trip_type(struct thermal_zone_device * thermal,int trip,enum thermal_trip_type * type)81 static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
82 				 enum thermal_trip_type *type)
83 {
84 	struct exynos_thermal_zone *th_zone = thermal->devdata;
85 	int max_trip = th_zone->sensor_conf->trip_data.trip_count;
86 	int trip_type;
87 
88 	if (trip < 0 || trip >= max_trip)
89 		return -EINVAL;
90 
91 	trip_type = th_zone->sensor_conf->trip_data.trip_type[trip];
92 
93 	if (trip_type == SW_TRIP)
94 		*type = THERMAL_TRIP_CRITICAL;
95 	else if (trip_type == THROTTLE_ACTIVE)
96 		*type = THERMAL_TRIP_ACTIVE;
97 	else if (trip_type == THROTTLE_PASSIVE)
98 		*type = THERMAL_TRIP_PASSIVE;
99 	else
100 		return -EINVAL;
101 
102 	return 0;
103 }
104 
105 /* Get trip temperature callback functions for thermal zone */
exynos_get_trip_temp(struct thermal_zone_device * thermal,int trip,unsigned long * temp)106 static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
107 				unsigned long *temp)
108 {
109 	struct exynos_thermal_zone *th_zone = thermal->devdata;
110 	int max_trip = th_zone->sensor_conf->trip_data.trip_count;
111 
112 	if (trip < 0 || trip >= max_trip)
113 		return -EINVAL;
114 
115 	*temp = th_zone->sensor_conf->trip_data.trip_val[trip];
116 	/* convert the temperature into millicelsius */
117 	*temp = *temp * MCELSIUS;
118 
119 	return 0;
120 }
121 
122 /* Get critical temperature callback functions for thermal zone */
exynos_get_crit_temp(struct thermal_zone_device * thermal,unsigned long * temp)123 static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
124 				unsigned long *temp)
125 {
126 	struct exynos_thermal_zone *th_zone = thermal->devdata;
127 	int max_trip = th_zone->sensor_conf->trip_data.trip_count;
128 	/* Get the temp of highest trip*/
129 	return exynos_get_trip_temp(thermal, max_trip - 1, temp);
130 }
131 
132 /* Bind callback functions for thermal zone */
exynos_bind(struct thermal_zone_device * thermal,struct thermal_cooling_device * cdev)133 static int exynos_bind(struct thermal_zone_device *thermal,
134 			struct thermal_cooling_device *cdev)
135 {
136 	int ret = 0, i, tab_size, level;
137 	struct freq_clip_table *tab_ptr, *clip_data;
138 	struct exynos_thermal_zone *th_zone = thermal->devdata;
139 	struct thermal_sensor_conf *data = th_zone->sensor_conf;
140 
141 	tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data;
142 	tab_size = data->cooling_data.freq_clip_count;
143 
144 	if (tab_ptr == NULL || tab_size == 0)
145 		return 0;
146 
147 	/* find the cooling device registered*/
148 	for (i = 0; i < th_zone->cool_dev_size; i++)
149 		if (cdev == th_zone->cool_dev[i])
150 			break;
151 
152 	/* No matching cooling device */
153 	if (i == th_zone->cool_dev_size)
154 		return 0;
155 
156 	/* Bind the thermal zone to the cpufreq cooling device */
157 	for (i = 0; i < tab_size; i++) {
158 		clip_data = (struct freq_clip_table *)&(tab_ptr[i]);
159 		level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max);
160 		if (level == THERMAL_CSTATE_INVALID)
161 			return 0;
162 		switch (GET_ZONE(i)) {
163 		case MONITOR_ZONE:
164 		case WARN_ZONE:
165 			if (thermal_zone_bind_cooling_device(thermal, i, cdev,
166 								level, 0)) {
167 				dev_err(data->dev,
168 					"error unbinding cdev inst=%d\n", i);
169 				ret = -EINVAL;
170 			}
171 			th_zone->bind = true;
172 			break;
173 		default:
174 			ret = -EINVAL;
175 		}
176 	}
177 
178 	return ret;
179 }
180 
181 /* Unbind callback functions for thermal zone */
exynos_unbind(struct thermal_zone_device * thermal,struct thermal_cooling_device * cdev)182 static int exynos_unbind(struct thermal_zone_device *thermal,
183 			struct thermal_cooling_device *cdev)
184 {
185 	int ret = 0, i, tab_size;
186 	struct exynos_thermal_zone *th_zone = thermal->devdata;
187 	struct thermal_sensor_conf *data = th_zone->sensor_conf;
188 
189 	if (th_zone->bind == false)
190 		return 0;
191 
192 	tab_size = data->cooling_data.freq_clip_count;
193 
194 	if (tab_size == 0)
195 		return 0;
196 
197 	/* find the cooling device registered*/
198 	for (i = 0; i < th_zone->cool_dev_size; i++)
199 		if (cdev == th_zone->cool_dev[i])
200 			break;
201 
202 	/* No matching cooling device */
203 	if (i == th_zone->cool_dev_size)
204 		return 0;
205 
206 	/* Bind the thermal zone to the cpufreq cooling device */
207 	for (i = 0; i < tab_size; i++) {
208 		switch (GET_ZONE(i)) {
209 		case MONITOR_ZONE:
210 		case WARN_ZONE:
211 			if (thermal_zone_unbind_cooling_device(thermal, i,
212 								cdev)) {
213 				dev_err(data->dev,
214 					"error unbinding cdev inst=%d\n", i);
215 				ret = -EINVAL;
216 			}
217 			th_zone->bind = false;
218 			break;
219 		default:
220 			ret = -EINVAL;
221 		}
222 	}
223 	return ret;
224 }
225 
226 /* Get temperature callback functions for thermal zone */
exynos_get_temp(struct thermal_zone_device * thermal,unsigned long * temp)227 static int exynos_get_temp(struct thermal_zone_device *thermal,
228 			unsigned long *temp)
229 {
230 	struct exynos_thermal_zone *th_zone = thermal->devdata;
231 	void *data;
232 
233 	if (!th_zone->sensor_conf) {
234 		dev_err(&thermal->device,
235 			"Temperature sensor not initialised\n");
236 		return -EINVAL;
237 	}
238 	data = th_zone->sensor_conf->driver_data;
239 	*temp = th_zone->sensor_conf->read_temperature(data);
240 	/* convert the temperature into millicelsius */
241 	*temp = *temp * MCELSIUS;
242 	return 0;
243 }
244 
245 /* Get temperature callback functions for thermal zone */
exynos_set_emul_temp(struct thermal_zone_device * thermal,unsigned long temp)246 static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
247 						unsigned long temp)
248 {
249 	void *data;
250 	int ret = -EINVAL;
251 	struct exynos_thermal_zone *th_zone = thermal->devdata;
252 
253 	if (!th_zone->sensor_conf) {
254 		dev_err(&thermal->device,
255 			"Temperature sensor not initialised\n");
256 		return -EINVAL;
257 	}
258 	data = th_zone->sensor_conf->driver_data;
259 	if (th_zone->sensor_conf->write_emul_temp)
260 		ret = th_zone->sensor_conf->write_emul_temp(data, temp);
261 	return ret;
262 }
263 
264 /* Get the temperature trend */
exynos_get_trend(struct thermal_zone_device * thermal,int trip,enum thermal_trend * trend)265 static int exynos_get_trend(struct thermal_zone_device *thermal,
266 			int trip, enum thermal_trend *trend)
267 {
268 	int ret;
269 	unsigned long trip_temp;
270 
271 	ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
272 	if (ret < 0)
273 		return ret;
274 
275 	if (thermal->temperature >= trip_temp)
276 		*trend = THERMAL_TREND_RAISE_FULL;
277 	else
278 		*trend = THERMAL_TREND_DROP_FULL;
279 
280 	return 0;
281 }
282 /* Operation callback functions for thermal zone */
283 static struct thermal_zone_device_ops exynos_dev_ops = {
284 	.bind = exynos_bind,
285 	.unbind = exynos_unbind,
286 	.get_temp = exynos_get_temp,
287 	.set_emul_temp = exynos_set_emul_temp,
288 	.get_trend = exynos_get_trend,
289 	.get_mode = exynos_get_mode,
290 	.set_mode = exynos_set_mode,
291 	.get_trip_type = exynos_get_trip_type,
292 	.get_trip_temp = exynos_get_trip_temp,
293 	.get_crit_temp = exynos_get_crit_temp,
294 };
295 
296 /*
297  * This function may be called from interrupt based temperature sensor
298  * when threshold is changed.
299  */
exynos_report_trigger(struct thermal_sensor_conf * conf)300 void exynos_report_trigger(struct thermal_sensor_conf *conf)
301 {
302 	unsigned int i;
303 	char data[10];
304 	char *envp[] = { data, NULL };
305 	struct exynos_thermal_zone *th_zone;
306 
307 	if (!conf || !conf->pzone_data) {
308 		pr_err("Invalid temperature sensor configuration data\n");
309 		return;
310 	}
311 
312 	th_zone = conf->pzone_data;
313 
314 	if (th_zone->bind == false) {
315 		for (i = 0; i < th_zone->cool_dev_size; i++) {
316 			if (!th_zone->cool_dev[i])
317 				continue;
318 			exynos_bind(th_zone->therm_dev,
319 					th_zone->cool_dev[i]);
320 		}
321 	}
322 
323 	thermal_zone_device_update(th_zone->therm_dev);
324 
325 	mutex_lock(&th_zone->therm_dev->lock);
326 	/* Find the level for which trip happened */
327 	for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) {
328 		if (th_zone->therm_dev->last_temperature <
329 			th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS)
330 			break;
331 	}
332 
333 	if (th_zone->mode == THERMAL_DEVICE_ENABLED &&
334 		!th_zone->sensor_conf->trip_data.trigger_falling) {
335 		if (i > 0)
336 			th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL;
337 		else
338 			th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
339 	}
340 
341 	snprintf(data, sizeof(data), "%u", i);
342 	kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp);
343 	mutex_unlock(&th_zone->therm_dev->lock);
344 }
345 
346 /* Register with the in-kernel thermal management */
exynos_register_thermal(struct thermal_sensor_conf * sensor_conf)347 int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
348 {
349 	int ret;
350 	struct cpumask mask_val;
351 	struct exynos_thermal_zone *th_zone;
352 
353 	if (!sensor_conf || !sensor_conf->read_temperature) {
354 		pr_err("Temperature sensor not initialised\n");
355 		return -EINVAL;
356 	}
357 
358 	th_zone = devm_kzalloc(sensor_conf->dev,
359 				sizeof(struct exynos_thermal_zone), GFP_KERNEL);
360 	if (!th_zone)
361 		return -ENOMEM;
362 
363 	th_zone->sensor_conf = sensor_conf;
364 	/*
365 	 * TODO: 1) Handle multiple cooling devices in a thermal zone
366 	 *	 2) Add a flag/name in cooling info to map to specific
367 	 *	 sensor
368 	 */
369 	if (sensor_conf->cooling_data.freq_clip_count > 0) {
370 		cpumask_set_cpu(0, &mask_val);
371 		th_zone->cool_dev[th_zone->cool_dev_size] =
372 					cpufreq_cooling_register(&mask_val);
373 		if (IS_ERR(th_zone->cool_dev[th_zone->cool_dev_size])) {
374 			dev_err(sensor_conf->dev,
375 				"Failed to register cpufreq cooling device\n");
376 			ret = -EINVAL;
377 			goto err_unregister;
378 		}
379 		th_zone->cool_dev_size++;
380 	}
381 
382 	th_zone->therm_dev = thermal_zone_device_register(
383 			sensor_conf->name, sensor_conf->trip_data.trip_count,
384 			0, th_zone, &exynos_dev_ops, NULL, 0,
385 			sensor_conf->trip_data.trigger_falling ? 0 :
386 			IDLE_INTERVAL);
387 
388 	if (IS_ERR(th_zone->therm_dev)) {
389 		dev_err(sensor_conf->dev,
390 			"Failed to register thermal zone device\n");
391 		ret = PTR_ERR(th_zone->therm_dev);
392 		goto err_unregister;
393 	}
394 	th_zone->mode = THERMAL_DEVICE_ENABLED;
395 	sensor_conf->pzone_data = th_zone;
396 
397 	dev_info(sensor_conf->dev,
398 		"Exynos: Thermal zone(%s) registered\n", sensor_conf->name);
399 
400 	return 0;
401 
402 err_unregister:
403 	exynos_unregister_thermal(sensor_conf);
404 	return ret;
405 }
406 
407 /* Un-Register with the in-kernel thermal management */
exynos_unregister_thermal(struct thermal_sensor_conf * sensor_conf)408 void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf)
409 {
410 	int i;
411 	struct exynos_thermal_zone *th_zone;
412 
413 	if (!sensor_conf || !sensor_conf->pzone_data) {
414 		pr_err("Invalid temperature sensor configuration data\n");
415 		return;
416 	}
417 
418 	th_zone = sensor_conf->pzone_data;
419 
420 	thermal_zone_device_unregister(th_zone->therm_dev);
421 
422 	for (i = 0; i < th_zone->cool_dev_size; ++i)
423 		cpufreq_cooling_unregister(th_zone->cool_dev[i]);
424 
425 	dev_info(sensor_conf->dev,
426 		"Exynos: Kernel Thermal management unregistered\n");
427 }
428