thermal: exynos5: add exynos5250 thermal sensor driver support
[deliverable/linux.git] / drivers / thermal / exynos_thermal.c
CommitLineData
9d97e5c8 1/*
c48cbba6 2 * exynos_thermal.c - Samsung EXYNOS TMU (Thermal Management Unit)
9d97e5c8
DK
3 *
4 * Copyright (C) 2011 Samsung Electronics
5 * Donggeun Kim <dg77.kim@samsung.com>
c48cbba6 6 * Amit Daniel Kachhap <amit.kachhap@linaro.org>
9d97e5c8
DK
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/module.h>
25#include <linux/err.h>
26#include <linux/kernel.h>
27#include <linux/slab.h>
28#include <linux/platform_device.h>
29#include <linux/interrupt.h>
30#include <linux/clk.h>
31#include <linux/workqueue.h>
32#include <linux/sysfs.h>
33#include <linux/kobject.h>
34#include <linux/io.h>
35#include <linux/mutex.h>
c48cbba6 36#include <linux/platform_data/exynos_thermal.h>
f22d9c03
ADK
37#include <linux/of.h>
38
39#include <plat/cpu.h>
40
41/* Exynos generic registers */
42#define EXYNOS_TMU_REG_TRIMINFO 0x0
43#define EXYNOS_TMU_REG_CONTROL 0x20
44#define EXYNOS_TMU_REG_STATUS 0x28
45#define EXYNOS_TMU_REG_CURRENT_TEMP 0x40
46#define EXYNOS_TMU_REG_INTEN 0x70
47#define EXYNOS_TMU_REG_INTSTAT 0x74
48#define EXYNOS_TMU_REG_INTCLEAR 0x78
49
50#define EXYNOS_TMU_TRIM_TEMP_MASK 0xff
51#define EXYNOS_TMU_GAIN_SHIFT 8
52#define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24
53#define EXYNOS_TMU_CORE_ON 3
54#define EXYNOS_TMU_CORE_OFF 2
55#define EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET 50
56
57/* Exynos4210 specific registers */
58#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44
59#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50
60#define EXYNOS4210_TMU_REG_TRIG_LEVEL1 0x54
61#define EXYNOS4210_TMU_REG_TRIG_LEVEL2 0x58
62#define EXYNOS4210_TMU_REG_TRIG_LEVEL3 0x5C
63#define EXYNOS4210_TMU_REG_PAST_TEMP0 0x60
64#define EXYNOS4210_TMU_REG_PAST_TEMP1 0x64
65#define EXYNOS4210_TMU_REG_PAST_TEMP2 0x68
66#define EXYNOS4210_TMU_REG_PAST_TEMP3 0x6C
67
68#define EXYNOS4210_TMU_TRIG_LEVEL0_MASK 0x1
69#define EXYNOS4210_TMU_TRIG_LEVEL1_MASK 0x10
70#define EXYNOS4210_TMU_TRIG_LEVEL2_MASK 0x100
71#define EXYNOS4210_TMU_TRIG_LEVEL3_MASK 0x1000
72#define EXYNOS4210_TMU_INTCLEAR_VAL 0x1111
73
74/* Exynos5250 and Exynos4412 specific registers */
75#define EXYNOS_TMU_TRIMINFO_CON 0x14
76#define EXYNOS_THD_TEMP_RISE 0x50
77#define EXYNOS_THD_TEMP_FALL 0x54
78#define EXYNOS_EMUL_CON 0x80
79
80#define EXYNOS_TRIMINFO_RELOAD 0x1
81#define EXYNOS_TMU_CLEAR_RISE_INT 0x111
82#define EXYNOS_TMU_CLEAR_FALL_INT (0x111 << 16)
83#define EXYNOS_MUX_ADDR_VALUE 6
84#define EXYNOS_MUX_ADDR_SHIFT 20
85#define EXYNOS_TMU_TRIP_MODE_SHIFT 13
86
87#define EFUSE_MIN_VALUE 40
88#define EFUSE_MAX_VALUE 100
89
90/* In-kernel thermal framework related macros & definations */
91#define SENSOR_NAME_LEN 16
92#define MAX_TRIP_COUNT 8
93#define MAX_COOLING_DEVICE 4
94
95#define ACTIVE_INTERVAL 500
96#define IDLE_INTERVAL 10000
97
98/* CPU Zone information */
99#define PANIC_ZONE 4
100#define WARN_ZONE 3
101#define MONITOR_ZONE 2
102#define SAFE_ZONE 1
103
104#define GET_ZONE(trip) (trip + 2)
105#define GET_TRIP(zone) (zone - 2)
106
107struct exynos_tmu_data {
108 struct exynos_tmu_platform_data *pdata;
9d97e5c8
DK
109 struct resource *mem;
110 void __iomem *base;
111 int irq;
f22d9c03 112 enum soc_type soc;
9d97e5c8
DK
113 struct work_struct irq_work;
114 struct mutex lock;
115 struct clk *clk;
116 u8 temp_error1, temp_error2;
117};
118
119/*
120 * TMU treats temperature as a mapped temperature code.
121 * The temperature is converted differently depending on the calibration type.
122 */
f22d9c03 123static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
9d97e5c8 124{
f22d9c03 125 struct exynos_tmu_platform_data *pdata = data->pdata;
9d97e5c8
DK
126 int temp_code;
127
f22d9c03
ADK
128 if (data->soc == SOC_ARCH_EXYNOS4210)
129 /* temp should range between 25 and 125 */
130 if (temp < 25 || temp > 125) {
131 temp_code = -EINVAL;
132 goto out;
133 }
9d97e5c8
DK
134
135 switch (pdata->cal_type) {
136 case TYPE_TWO_POINT_TRIMMING:
137 temp_code = (temp - 25) *
138 (data->temp_error2 - data->temp_error1) /
139 (85 - 25) + data->temp_error1;
140 break;
141 case TYPE_ONE_POINT_TRIMMING:
142 temp_code = temp + data->temp_error1 - 25;
143 break;
144 default:
f22d9c03 145 temp_code = temp + EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET;
9d97e5c8
DK
146 break;
147 }
148out:
149 return temp_code;
150}
151
152/*
153 * Calculate a temperature value from a temperature code.
154 * The unit of the temperature is degree Celsius.
155 */
f22d9c03 156static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code)
9d97e5c8 157{
f22d9c03 158 struct exynos_tmu_platform_data *pdata = data->pdata;
9d97e5c8
DK
159 int temp;
160
f22d9c03
ADK
161 if (data->soc == SOC_ARCH_EXYNOS4210)
162 /* temp_code should range between 75 and 175 */
163 if (temp_code < 75 || temp_code > 175) {
164 temp = -ENODATA;
165 goto out;
166 }
9d97e5c8
DK
167
168 switch (pdata->cal_type) {
169 case TYPE_TWO_POINT_TRIMMING:
170 temp = (temp_code - data->temp_error1) * (85 - 25) /
171 (data->temp_error2 - data->temp_error1) + 25;
172 break;
173 case TYPE_ONE_POINT_TRIMMING:
174 temp = temp_code - data->temp_error1 + 25;
175 break;
176 default:
f22d9c03 177 temp = temp_code - EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET;
9d97e5c8
DK
178 break;
179 }
180out:
181 return temp;
182}
183
f22d9c03 184static int exynos_tmu_initialize(struct platform_device *pdev)
9d97e5c8 185{
f22d9c03
ADK
186 struct exynos_tmu_data *data = platform_get_drvdata(pdev);
187 struct exynos_tmu_platform_data *pdata = data->pdata;
188 unsigned int status, trim_info, rising_threshold;
9d97e5c8
DK
189 int ret = 0, threshold_code;
190
191 mutex_lock(&data->lock);
192 clk_enable(data->clk);
193
f22d9c03 194 status = readb(data->base + EXYNOS_TMU_REG_STATUS);
9d97e5c8
DK
195 if (!status) {
196 ret = -EBUSY;
197 goto out;
198 }
199
f22d9c03
ADK
200 if (data->soc == SOC_ARCH_EXYNOS) {
201 __raw_writel(EXYNOS_TRIMINFO_RELOAD,
202 data->base + EXYNOS_TMU_TRIMINFO_CON);
203 }
9d97e5c8 204 /* Save trimming info in order to perform calibration */
f22d9c03
ADK
205 trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
206 data->temp_error1 = trim_info & EXYNOS_TMU_TRIM_TEMP_MASK;
207 data->temp_error2 = ((trim_info >> 8) & EXYNOS_TMU_TRIM_TEMP_MASK);
208
209 if ((EFUSE_MIN_VALUE > data->temp_error1) ||
210 (data->temp_error1 > EFUSE_MAX_VALUE) ||
211 (data->temp_error2 != 0))
212 data->temp_error1 = pdata->efuse_value;
213
214 if (data->soc == SOC_ARCH_EXYNOS4210) {
215 /* Write temperature code for threshold */
216 threshold_code = temp_to_code(data, pdata->threshold);
217 if (threshold_code < 0) {
218 ret = threshold_code;
219 goto out;
220 }
221 writeb(threshold_code,
222 data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
223
224 writeb(pdata->trigger_levels[0],
225 data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0);
226 writeb(pdata->trigger_levels[1],
227 data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL1);
228 writeb(pdata->trigger_levels[2],
229 data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL2);
230 writeb(pdata->trigger_levels[3],
231 data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL3);
232
233 writel(EXYNOS4210_TMU_INTCLEAR_VAL,
234 data->base + EXYNOS_TMU_REG_INTCLEAR);
235 } else if (data->soc == SOC_ARCH_EXYNOS) {
236 /* Write temperature code for threshold */
237 threshold_code = temp_to_code(data, pdata->trigger_levels[0]);
238 if (threshold_code < 0) {
239 ret = threshold_code;
240 goto out;
241 }
242 rising_threshold = threshold_code;
243 threshold_code = temp_to_code(data, pdata->trigger_levels[1]);
244 if (threshold_code < 0) {
245 ret = threshold_code;
246 goto out;
247 }
248 rising_threshold |= (threshold_code << 8);
249 threshold_code = temp_to_code(data, pdata->trigger_levels[2]);
250 if (threshold_code < 0) {
251 ret = threshold_code;
252 goto out;
253 }
254 rising_threshold |= (threshold_code << 16);
255
256 writel(rising_threshold,
257 data->base + EXYNOS_THD_TEMP_RISE);
258 writel(0, data->base + EXYNOS_THD_TEMP_FALL);
259
260 writel(EXYNOS_TMU_CLEAR_RISE_INT|EXYNOS_TMU_CLEAR_FALL_INT,
261 data->base + EXYNOS_TMU_REG_INTCLEAR);
9d97e5c8 262 }
9d97e5c8
DK
263out:
264 clk_disable(data->clk);
265 mutex_unlock(&data->lock);
266
267 return ret;
268}
269
f22d9c03 270static void exynos_tmu_control(struct platform_device *pdev, bool on)
9d97e5c8 271{
f22d9c03
ADK
272 struct exynos_tmu_data *data = platform_get_drvdata(pdev);
273 struct exynos_tmu_platform_data *pdata = data->pdata;
9d97e5c8
DK
274 unsigned int con, interrupt_en;
275
276 mutex_lock(&data->lock);
277 clk_enable(data->clk);
278
f22d9c03
ADK
279 con = pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT |
280 pdata->gain << EXYNOS_TMU_GAIN_SHIFT;
281
282 if (data->soc == SOC_ARCH_EXYNOS) {
283 con |= pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT;
284 con |= (EXYNOS_MUX_ADDR_VALUE << EXYNOS_MUX_ADDR_SHIFT);
285 }
286
9d97e5c8 287 if (on) {
f22d9c03 288 con |= EXYNOS_TMU_CORE_ON;
9d97e5c8
DK
289 interrupt_en = pdata->trigger_level3_en << 12 |
290 pdata->trigger_level2_en << 8 |
291 pdata->trigger_level1_en << 4 |
292 pdata->trigger_level0_en;
293 } else {
f22d9c03 294 con |= EXYNOS_TMU_CORE_OFF;
9d97e5c8
DK
295 interrupt_en = 0; /* Disable all interrupts */
296 }
f22d9c03
ADK
297 writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN);
298 writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
9d97e5c8
DK
299
300 clk_disable(data->clk);
301 mutex_unlock(&data->lock);
302}
303
f22d9c03 304static int exynos_tmu_read(struct exynos_tmu_data *data)
9d97e5c8
DK
305{
306 u8 temp_code;
307 int temp;
308
309 mutex_lock(&data->lock);
310 clk_enable(data->clk);
311
f22d9c03 312 temp_code = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP);
9d97e5c8
DK
313 temp = code_to_temp(data, temp_code);
314
315 clk_disable(data->clk);
316 mutex_unlock(&data->lock);
317
318 return temp;
319}
320
f22d9c03 321static void exynos_tmu_work(struct work_struct *work)
9d97e5c8 322{
f22d9c03
ADK
323 struct exynos_tmu_data *data = container_of(work,
324 struct exynos_tmu_data, irq_work);
9d97e5c8
DK
325
326 mutex_lock(&data->lock);
327 clk_enable(data->clk);
328
9d97e5c8 329
f22d9c03
ADK
330 if (data->soc == SOC_ARCH_EXYNOS)
331 writel(EXYNOS_TMU_CLEAR_RISE_INT,
332 data->base + EXYNOS_TMU_REG_INTCLEAR);
333 else
334 writel(EXYNOS4210_TMU_INTCLEAR_VAL,
335 data->base + EXYNOS_TMU_REG_INTCLEAR);
9d97e5c8
DK
336
337 clk_disable(data->clk);
338 mutex_unlock(&data->lock);
f22d9c03 339 enable_irq(data->irq);
9d97e5c8
DK
340}
341
f22d9c03 342static irqreturn_t exynos_tmu_irq(int irq, void *id)
9d97e5c8 343{
f22d9c03 344 struct exynos_tmu_data *data = id;
9d97e5c8
DK
345
346 disable_irq_nosync(irq);
347 schedule_work(&data->irq_work);
348
349 return IRQ_HANDLED;
350}
351
f22d9c03 352static int __devinit exynos_tmu_probe(struct platform_device *pdev)
9d97e5c8 353{
f22d9c03
ADK
354 struct exynos_tmu_data *data;
355 struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data;
9d97e5c8
DK
356 int ret;
357
358 if (!pdata) {
359 dev_err(&pdev->dev, "No platform init data supplied.\n");
360 return -ENODEV;
361 }
f22d9c03 362 data = kzalloc(sizeof(struct exynos_tmu_data), GFP_KERNEL);
9d97e5c8
DK
363 if (!data) {
364 dev_err(&pdev->dev, "Failed to allocate driver structure\n");
365 return -ENOMEM;
366 }
367
368 data->irq = platform_get_irq(pdev, 0);
369 if (data->irq < 0) {
370 ret = data->irq;
371 dev_err(&pdev->dev, "Failed to get platform irq\n");
372 goto err_free;
373 }
374
f22d9c03 375 INIT_WORK(&data->irq_work, exynos_tmu_work);
9d97e5c8
DK
376
377 data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
378 if (!data->mem) {
379 ret = -ENOENT;
380 dev_err(&pdev->dev, "Failed to get platform resource\n");
381 goto err_free;
382 }
383
384 data->mem = request_mem_region(data->mem->start,
385 resource_size(data->mem), pdev->name);
386 if (!data->mem) {
387 ret = -ENODEV;
388 dev_err(&pdev->dev, "Failed to request memory region\n");
389 goto err_free;
390 }
391
392 data->base = ioremap(data->mem->start, resource_size(data->mem));
393 if (!data->base) {
394 ret = -ENODEV;
395 dev_err(&pdev->dev, "Failed to ioremap memory\n");
396 goto err_mem_region;
397 }
398
f22d9c03
ADK
399 ret = request_irq(data->irq, exynos_tmu_irq,
400 IRQF_TRIGGER_RISING, "exynos-tmu", data);
9d97e5c8
DK
401 if (ret) {
402 dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
403 goto err_io_remap;
404 }
405
406 data->clk = clk_get(NULL, "tmu_apbif");
407 if (IS_ERR(data->clk)) {
408 ret = PTR_ERR(data->clk);
409 dev_err(&pdev->dev, "Failed to get clock\n");
410 goto err_irq;
411 }
412
f22d9c03
ADK
413 if (pdata->type == SOC_ARCH_EXYNOS ||
414 pdata->type == SOC_ARCH_EXYNOS4210)
415 data->soc = pdata->type;
416 else {
417 ret = -EINVAL;
418 dev_err(&pdev->dev, "Platform not supported\n");
419 goto err_clk;
420 }
421
9d97e5c8
DK
422 data->pdata = pdata;
423 platform_set_drvdata(pdev, data);
424 mutex_init(&data->lock);
425
f22d9c03 426 ret = exynos_tmu_initialize(pdev);
9d97e5c8
DK
427 if (ret) {
428 dev_err(&pdev->dev, "Failed to initialize TMU\n");
429 goto err_clk;
430 }
431
f22d9c03 432 exynos_tmu_control(pdev, true);
9d97e5c8
DK
433
434 return 0;
9d97e5c8
DK
435err_clk:
436 platform_set_drvdata(pdev, NULL);
437 clk_put(data->clk);
438err_irq:
439 free_irq(data->irq, data);
440err_io_remap:
441 iounmap(data->base);
442err_mem_region:
443 release_mem_region(data->mem->start, resource_size(data->mem));
444err_free:
445 kfree(data);
446
447 return ret;
448}
449
f22d9c03 450static int __devexit exynos_tmu_remove(struct platform_device *pdev)
9d97e5c8 451{
f22d9c03 452 struct exynos_tmu_data *data = platform_get_drvdata(pdev);
9d97e5c8 453
f22d9c03 454 exynos_tmu_control(pdev, false);
9d97e5c8 455
9d97e5c8
DK
456 clk_put(data->clk);
457
458 free_irq(data->irq, data);
459
460 iounmap(data->base);
461 release_mem_region(data->mem->start, resource_size(data->mem));
462
463 platform_set_drvdata(pdev, NULL);
464
465 kfree(data);
466
467 return 0;
468}
469
08cd6753 470#ifdef CONFIG_PM_SLEEP
f22d9c03 471static int exynos_tmu_suspend(struct device *dev)
9d97e5c8 472{
f22d9c03 473 exynos_tmu_control(to_platform_device(dev), false);
9d97e5c8
DK
474
475 return 0;
476}
477
f22d9c03 478static int exynos_tmu_resume(struct device *dev)
9d97e5c8 479{
08cd6753
RW
480 struct platform_device *pdev = to_platform_device(dev);
481
f22d9c03
ADK
482 exynos_tmu_initialize(pdev);
483 exynos_tmu_control(pdev, true);
9d97e5c8
DK
484
485 return 0;
486}
08cd6753 487
f22d9c03
ADK
488static SIMPLE_DEV_PM_OPS(exynos_tmu_pm,
489 exynos_tmu_suspend, exynos_tmu_resume);
490#define EXYNOS_TMU_PM (&exynos_tmu_pm)
9d97e5c8 491#else
f22d9c03 492#define EXYNOS_TMU_PM NULL
9d97e5c8
DK
493#endif
494
f22d9c03 495static struct platform_driver exynos_tmu_driver = {
9d97e5c8 496 .driver = {
f22d9c03 497 .name = "exynos-tmu",
9d97e5c8 498 .owner = THIS_MODULE,
f22d9c03 499 .pm = EXYNOS_TMU_PM,
9d97e5c8 500 },
f22d9c03
ADK
501 .probe = exynos_tmu_probe,
502 .remove = __devexit_p(exynos_tmu_remove),
9d97e5c8
DK
503};
504
f22d9c03 505module_platform_driver(exynos_tmu_driver);
9d97e5c8 506
f22d9c03 507MODULE_DESCRIPTION("EXYNOS TMU Driver");
9d97e5c8
DK
508MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
509MODULE_LICENSE("GPL");
f22d9c03 510MODULE_ALIAS("platform:exynos-tmu");
This page took 0.100477 seconds and 5 git commands to generate.