[go: nahoru, domu]

exynos_tmu.c revision 4de0bdaa9677d11406c9becb70c60887c957e1f0
1/*
2 * exynos_tmu.c - Samsung EXYNOS TMU (Thermal Management Unit)
3 *
4 *  Copyright (C) 2011 Samsung Electronics
5 *  Donggeun Kim <dg77.kim@samsung.com>
6 *  Amit Daniel Kachhap <amit.kachhap@linaro.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 *
22 */
23
24#include <linux/clk.h>
25#include <linux/io.h>
26#include <linux/interrupt.h>
27#include <linux/module.h>
28#include <linux/of.h>
29#include <linux/platform_device.h>
30
31#include "exynos_thermal_common.h"
32#include "exynos_tmu.h"
33#include "exynos_tmu_data.h"
34
35struct exynos_tmu_data {
36	struct exynos_tmu_platform_data *pdata;
37	struct resource *mem;
38	void __iomem *base;
39	int irq;
40	enum soc_type soc;
41	struct work_struct irq_work;
42	struct mutex lock;
43	struct clk *clk;
44	u8 temp_error1, temp_error2;
45};
46
47/*
48 * TMU treats temperature as a mapped temperature code.
49 * The temperature is converted differently depending on the calibration type.
50 */
51static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
52{
53	struct exynos_tmu_platform_data *pdata = data->pdata;
54	int temp_code;
55
56	if (data->soc == SOC_ARCH_EXYNOS4210)
57		/* temp should range between 25 and 125 */
58		if (temp < 25 || temp > 125) {
59			temp_code = -EINVAL;
60			goto out;
61		}
62
63	switch (pdata->cal_type) {
64	case TYPE_TWO_POINT_TRIMMING:
65		temp_code = (temp - pdata->first_point_trim) *
66			(data->temp_error2 - data->temp_error1) /
67			(pdata->second_point_trim - pdata->first_point_trim) +
68			data->temp_error1;
69		break;
70	case TYPE_ONE_POINT_TRIMMING:
71		temp_code = temp + data->temp_error1 - pdata->first_point_trim;
72		break;
73	default:
74		temp_code = temp + pdata->default_temp_offset;
75		break;
76	}
77out:
78	return temp_code;
79}
80
81/*
82 * Calculate a temperature value from a temperature code.
83 * The unit of the temperature is degree Celsius.
84 */
85static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code)
86{
87	struct exynos_tmu_platform_data *pdata = data->pdata;
88	int temp;
89
90	if (data->soc == SOC_ARCH_EXYNOS4210)
91		/* temp_code should range between 75 and 175 */
92		if (temp_code < 75 || temp_code > 175) {
93			temp = -ENODATA;
94			goto out;
95		}
96
97	switch (pdata->cal_type) {
98	case TYPE_TWO_POINT_TRIMMING:
99		temp = (temp_code - data->temp_error1) *
100			(pdata->second_point_trim - pdata->first_point_trim) /
101			(data->temp_error2 - data->temp_error1) +
102			pdata->first_point_trim;
103		break;
104	case TYPE_ONE_POINT_TRIMMING:
105		temp = temp_code - data->temp_error1 + pdata->first_point_trim;
106		break;
107	default:
108		temp = temp_code - pdata->default_temp_offset;
109		break;
110	}
111out:
112	return temp;
113}
114
115static int exynos_tmu_initialize(struct platform_device *pdev)
116{
117	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
118	struct exynos_tmu_platform_data *pdata = data->pdata;
119	const struct exynos_tmu_registers *reg = pdata->registers;
120	unsigned int status, trim_info = 0, con;
121	unsigned int rising_threshold = 0, falling_threshold = 0;
122	int ret = 0, threshold_code, i, trigger_levs = 0;
123
124	mutex_lock(&data->lock);
125	clk_enable(data->clk);
126
127	status = readb(data->base + reg->tmu_status);
128	if (!status) {
129		ret = -EBUSY;
130		goto out;
131	}
132
133	if (data->soc == SOC_ARCH_EXYNOS)
134		__raw_writel(1, data->base + reg->triminfo_ctrl);
135
136	/* Save trimming info in order to perform calibration */
137	trim_info = readl(data->base + reg->triminfo_data);
138	data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK;
139	data->temp_error2 = ((trim_info >> reg->triminfo_85_shift) &
140				EXYNOS_TMU_TEMP_MASK);
141
142	if ((pdata->min_efuse_value > data->temp_error1) ||
143			(data->temp_error1 > pdata->max_efuse_value) ||
144			(data->temp_error2 != 0))
145		data->temp_error1 = pdata->efuse_value;
146
147	if (pdata->max_trigger_level > MAX_THRESHOLD_LEVS) {
148		dev_err(&pdev->dev, "Invalid max trigger level\n");
149		goto out;
150	}
151
152	for (i = 0; i < pdata->max_trigger_level; i++) {
153		if (!pdata->trigger_levels[i])
154			continue;
155
156		if ((pdata->trigger_type[i] == HW_TRIP) &&
157		(!pdata->trigger_levels[pdata->max_trigger_level - 1])) {
158			dev_err(&pdev->dev, "Invalid hw trigger level\n");
159			ret = -EINVAL;
160			goto out;
161		}
162
163		/* Count trigger levels except the HW trip*/
164		if (!(pdata->trigger_type[i] == HW_TRIP))
165			trigger_levs++;
166	}
167
168	if (data->soc == SOC_ARCH_EXYNOS4210) {
169		/* Write temperature code for threshold */
170		threshold_code = temp_to_code(data, pdata->threshold);
171		if (threshold_code < 0) {
172			ret = threshold_code;
173			goto out;
174		}
175		writeb(threshold_code,
176			data->base + reg->threshold_temp);
177		for (i = 0; i < trigger_levs; i++)
178			writeb(pdata->trigger_levels[i], data->base +
179			reg->threshold_th0 + i * sizeof(reg->threshold_th0));
180
181		writel(reg->inten_rise_mask, data->base + reg->tmu_intclear);
182	} else if (data->soc == SOC_ARCH_EXYNOS) {
183		/* Write temperature code for rising and falling threshold */
184		for (i = 0;
185		i < trigger_levs && i < EXYNOS_MAX_TRIGGER_PER_REG; i++) {
186			threshold_code = temp_to_code(data,
187						pdata->trigger_levels[i]);
188			if (threshold_code < 0) {
189				ret = threshold_code;
190				goto out;
191			}
192			rising_threshold |= threshold_code << 8 * i;
193			if (pdata->threshold_falling) {
194				threshold_code = temp_to_code(data,
195						pdata->trigger_levels[i] -
196						pdata->threshold_falling);
197				if (threshold_code > 0)
198					falling_threshold |=
199						threshold_code << 8 * i;
200			}
201		}
202
203		writel(rising_threshold,
204				data->base + reg->threshold_th0);
205		writel(falling_threshold,
206				data->base + reg->threshold_th1);
207
208		writel((reg->inten_rise_mask << reg->inten_rise_shift) |
209			(reg->inten_fall_mask << reg->inten_fall_shift),
210				data->base + reg->tmu_intclear);
211
212		/* if last threshold limit is also present */
213		i = pdata->max_trigger_level - 1;
214		if (pdata->trigger_levels[i] &&
215				(pdata->trigger_type[i] == HW_TRIP)) {
216			threshold_code = temp_to_code(data,
217						pdata->trigger_levels[i]);
218			if (threshold_code < 0) {
219				ret = threshold_code;
220				goto out;
221			}
222			rising_threshold |= threshold_code << 8 * i;
223			writel(rising_threshold,
224				data->base + reg->threshold_th0);
225			con = readl(data->base + reg->tmu_ctrl);
226			con |= (1 << reg->therm_trip_en_shift);
227			writel(con, data->base + reg->tmu_ctrl);
228		}
229	}
230out:
231	clk_disable(data->clk);
232	mutex_unlock(&data->lock);
233
234	return ret;
235}
236
237static void exynos_tmu_control(struct platform_device *pdev, bool on)
238{
239	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
240	struct exynos_tmu_platform_data *pdata = data->pdata;
241	const struct exynos_tmu_registers *reg = pdata->registers;
242	unsigned int con, interrupt_en;
243
244	mutex_lock(&data->lock);
245	clk_enable(data->clk);
246
247	con = readl(data->base + reg->tmu_ctrl);
248
249	if (pdata->reference_voltage) {
250		con &= ~(reg->buf_vref_sel_mask << reg->buf_vref_sel_shift);
251		con |= pdata->reference_voltage << reg->buf_vref_sel_shift;
252	}
253
254	if (pdata->gain) {
255		con &= ~(reg->buf_slope_sel_mask << reg->buf_slope_sel_shift);
256		con |= (pdata->gain << reg->buf_slope_sel_shift);
257	}
258
259	if (pdata->noise_cancel_mode) {
260		con &= ~(reg->therm_trip_mode_mask <<
261					reg->therm_trip_mode_shift);
262		con |= (pdata->noise_cancel_mode << reg->therm_trip_mode_shift);
263	}
264
265	if (on) {
266		con |= (1 << reg->core_en_shift);
267		interrupt_en =
268			pdata->trigger_enable[3] << reg->inten_rise3_shift |
269			pdata->trigger_enable[2] << reg->inten_rise2_shift |
270			pdata->trigger_enable[1] << reg->inten_rise1_shift |
271			pdata->trigger_enable[0] << reg->inten_rise0_shift;
272		if (pdata->threshold_falling)
273			interrupt_en |=
274				interrupt_en << reg->inten_fall0_shift;
275	} else {
276		con &= ~(1 << reg->core_en_shift);
277		interrupt_en = 0; /* Disable all interrupts */
278	}
279	writel(interrupt_en, data->base + reg->tmu_inten);
280	writel(con, data->base + reg->tmu_ctrl);
281
282	clk_disable(data->clk);
283	mutex_unlock(&data->lock);
284}
285
286static int exynos_tmu_read(struct exynos_tmu_data *data)
287{
288	struct exynos_tmu_platform_data *pdata = data->pdata;
289	const struct exynos_tmu_registers *reg = pdata->registers;
290	u8 temp_code;
291	int temp;
292
293	mutex_lock(&data->lock);
294	clk_enable(data->clk);
295
296	temp_code = readb(data->base + reg->tmu_cur_temp);
297	temp = code_to_temp(data, temp_code);
298
299	clk_disable(data->clk);
300	mutex_unlock(&data->lock);
301
302	return temp;
303}
304
305#ifdef CONFIG_THERMAL_EMULATION
306static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
307{
308	struct exynos_tmu_data *data = drv_data;
309	struct exynos_tmu_platform_data *pdata = data->pdata;
310	const struct exynos_tmu_registers *reg = pdata->registers;
311	unsigned int val;
312	int ret = -EINVAL;
313
314	if (data->soc == SOC_ARCH_EXYNOS4210)
315		goto out;
316
317	if (temp && temp < MCELSIUS)
318		goto out;
319
320	mutex_lock(&data->lock);
321	clk_enable(data->clk);
322
323	val = readl(data->base + reg->emul_con);
324
325	if (temp) {
326		temp /= MCELSIUS;
327
328		val = (EXYNOS_EMUL_TIME << reg->emul_time_shift) |
329			(temp_to_code(data, temp)
330			 << reg->emul_temp_shift) | EXYNOS_EMUL_ENABLE;
331	} else {
332		val &= ~EXYNOS_EMUL_ENABLE;
333	}
334
335	writel(val, data->base + reg->emul_con);
336
337	clk_disable(data->clk);
338	mutex_unlock(&data->lock);
339	return 0;
340out:
341	return ret;
342}
343#else
344static int exynos_tmu_set_emulation(void *drv_data,	unsigned long temp)
345	{ return -EINVAL; }
346#endif/*CONFIG_THERMAL_EMULATION*/
347
348static struct thermal_sensor_conf exynos_sensor_conf = {
349	.name			= "exynos-therm",
350	.read_temperature	= (int (*)(void *))exynos_tmu_read,
351	.write_emul_temp	= exynos_tmu_set_emulation,
352};
353
354static void exynos_tmu_work(struct work_struct *work)
355{
356	struct exynos_tmu_data *data = container_of(work,
357			struct exynos_tmu_data, irq_work);
358	struct exynos_tmu_platform_data *pdata = data->pdata;
359	const struct exynos_tmu_registers *reg = pdata->registers;
360	unsigned int val_irq;
361
362	exynos_report_trigger(&exynos_sensor_conf);
363	mutex_lock(&data->lock);
364	clk_enable(data->clk);
365
366	/* TODO: take action based on particular interrupt */
367	val_irq = readl(data->base + reg->tmu_intstat);
368	/* clear the interrupts */
369	writel(val_irq, data->base + reg->tmu_intclear);
370
371	clk_disable(data->clk);
372	mutex_unlock(&data->lock);
373
374	enable_irq(data->irq);
375}
376
377static irqreturn_t exynos_tmu_irq(int irq, void *id)
378{
379	struct exynos_tmu_data *data = id;
380
381	disable_irq_nosync(irq);
382	schedule_work(&data->irq_work);
383
384	return IRQ_HANDLED;
385}
386
387#ifdef CONFIG_OF
388static const struct of_device_id exynos_tmu_match[] = {
389	{
390		.compatible = "samsung,exynos4210-tmu",
391		.data = (void *)EXYNOS4210_TMU_DRV_DATA,
392	},
393	{
394		.compatible = "samsung,exynos4412-tmu",
395		.data = (void *)EXYNOS5250_TMU_DRV_DATA,
396	},
397	{
398		.compatible = "samsung,exynos5250-tmu",
399		.data = (void *)EXYNOS5250_TMU_DRV_DATA,
400	},
401	{},
402};
403MODULE_DEVICE_TABLE(of, exynos_tmu_match);
404#endif
405
406static struct platform_device_id exynos_tmu_driver_ids[] = {
407	{
408		.name		= "exynos4210-tmu",
409		.driver_data    = (kernel_ulong_t)EXYNOS4210_TMU_DRV_DATA,
410	},
411	{
412		.name		= "exynos5250-tmu",
413		.driver_data    = (kernel_ulong_t)EXYNOS5250_TMU_DRV_DATA,
414	},
415	{ },
416};
417MODULE_DEVICE_TABLE(platform, exynos_tmu_driver_ids);
418
419static inline struct  exynos_tmu_platform_data *exynos_get_driver_data(
420			struct platform_device *pdev)
421{
422#ifdef CONFIG_OF
423	if (pdev->dev.of_node) {
424		const struct of_device_id *match;
425		match = of_match_node(exynos_tmu_match, pdev->dev.of_node);
426		if (!match)
427			return NULL;
428		return (struct exynos_tmu_platform_data *) match->data;
429	}
430#endif
431	return (struct exynos_tmu_platform_data *)
432			platform_get_device_id(pdev)->driver_data;
433}
434
435static int exynos_tmu_probe(struct platform_device *pdev)
436{
437	struct exynos_tmu_data *data;
438	struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data;
439	int ret, i;
440
441	if (!pdata)
442		pdata = exynos_get_driver_data(pdev);
443
444	if (!pdata) {
445		dev_err(&pdev->dev, "No platform init data supplied.\n");
446		return -ENODEV;
447	}
448	data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
449					GFP_KERNEL);
450	if (!data) {
451		dev_err(&pdev->dev, "Failed to allocate driver structure\n");
452		return -ENOMEM;
453	}
454
455	data->irq = platform_get_irq(pdev, 0);
456	if (data->irq < 0) {
457		dev_err(&pdev->dev, "Failed to get platform irq\n");
458		return data->irq;
459	}
460
461	INIT_WORK(&data->irq_work, exynos_tmu_work);
462
463	data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
464	data->base = devm_ioremap_resource(&pdev->dev, data->mem);
465	if (IS_ERR(data->base))
466		return PTR_ERR(data->base);
467
468	ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq,
469		IRQF_TRIGGER_RISING, "exynos-tmu", data);
470	if (ret) {
471		dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
472		return ret;
473	}
474
475	data->clk = devm_clk_get(&pdev->dev, "tmu_apbif");
476	if (IS_ERR(data->clk)) {
477		dev_err(&pdev->dev, "Failed to get clock\n");
478		return  PTR_ERR(data->clk);
479	}
480
481	ret = clk_prepare(data->clk);
482	if (ret)
483		return ret;
484
485	if (pdata->type == SOC_ARCH_EXYNOS ||
486				pdata->type == SOC_ARCH_EXYNOS4210)
487		data->soc = pdata->type;
488	else {
489		ret = -EINVAL;
490		dev_err(&pdev->dev, "Platform not supported\n");
491		goto err_clk;
492	}
493
494	data->pdata = pdata;
495	platform_set_drvdata(pdev, data);
496	mutex_init(&data->lock);
497
498	ret = exynos_tmu_initialize(pdev);
499	if (ret) {
500		dev_err(&pdev->dev, "Failed to initialize TMU\n");
501		goto err_clk;
502	}
503
504	exynos_tmu_control(pdev, true);
505
506	/* Register the sensor with thermal management interface */
507	(&exynos_sensor_conf)->private_data = data;
508	exynos_sensor_conf.trip_data.trip_count = pdata->trigger_enable[0] +
509			pdata->trigger_enable[1] + pdata->trigger_enable[2]+
510			pdata->trigger_enable[3];
511
512	for (i = 0; i < exynos_sensor_conf.trip_data.trip_count; i++)
513		exynos_sensor_conf.trip_data.trip_val[i] =
514			pdata->threshold + pdata->trigger_levels[i];
515
516	exynos_sensor_conf.trip_data.trigger_falling = pdata->threshold_falling;
517
518	exynos_sensor_conf.cooling_data.freq_clip_count =
519						pdata->freq_tab_count;
520	for (i = 0; i < pdata->freq_tab_count; i++) {
521		exynos_sensor_conf.cooling_data.freq_data[i].freq_clip_max =
522					pdata->freq_tab[i].freq_clip_max;
523		exynos_sensor_conf.cooling_data.freq_data[i].temp_level =
524					pdata->freq_tab[i].temp_level;
525	}
526
527	ret = exynos_register_thermal(&exynos_sensor_conf);
528	if (ret) {
529		dev_err(&pdev->dev, "Failed to register thermal interface\n");
530		goto err_clk;
531	}
532
533	return 0;
534err_clk:
535	clk_unprepare(data->clk);
536	return ret;
537}
538
539static int exynos_tmu_remove(struct platform_device *pdev)
540{
541	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
542
543	exynos_tmu_control(pdev, false);
544
545	exynos_unregister_thermal(&exynos_sensor_conf);
546
547	clk_unprepare(data->clk);
548
549	return 0;
550}
551
552#ifdef CONFIG_PM_SLEEP
553static int exynos_tmu_suspend(struct device *dev)
554{
555	exynos_tmu_control(to_platform_device(dev), false);
556
557	return 0;
558}
559
560static int exynos_tmu_resume(struct device *dev)
561{
562	struct platform_device *pdev = to_platform_device(dev);
563
564	exynos_tmu_initialize(pdev);
565	exynos_tmu_control(pdev, true);
566
567	return 0;
568}
569
570static SIMPLE_DEV_PM_OPS(exynos_tmu_pm,
571			 exynos_tmu_suspend, exynos_tmu_resume);
572#define EXYNOS_TMU_PM	(&exynos_tmu_pm)
573#else
574#define EXYNOS_TMU_PM	NULL
575#endif
576
577static struct platform_driver exynos_tmu_driver = {
578	.driver = {
579		.name   = "exynos-tmu",
580		.owner  = THIS_MODULE,
581		.pm     = EXYNOS_TMU_PM,
582		.of_match_table = of_match_ptr(exynos_tmu_match),
583	},
584	.probe = exynos_tmu_probe,
585	.remove	= exynos_tmu_remove,
586	.id_table = exynos_tmu_driver_ids,
587};
588
589module_platform_driver(exynos_tmu_driver);
590
591MODULE_DESCRIPTION("EXYNOS TMU Driver");
592MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
593MODULE_LICENSE("GPL");
594MODULE_ALIAS("platform:exynos-tmu");
595