]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - drivers/thermal/exynos_thermal.c
hwmon: exynos4: move thermal sensor driver to driver/thermal directory
[mirror_ubuntu-hirsute-kernel.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>
36
c48cbba6 37#include <linux/platform_data/exynos_thermal.h>
9d97e5c8
DK
38
39#define EXYNOS4_TMU_REG_TRIMINFO 0x0
40#define EXYNOS4_TMU_REG_CONTROL 0x20
41#define EXYNOS4_TMU_REG_STATUS 0x28
42#define EXYNOS4_TMU_REG_CURRENT_TEMP 0x40
43#define EXYNOS4_TMU_REG_THRESHOLD_TEMP 0x44
44#define EXYNOS4_TMU_REG_TRIG_LEVEL0 0x50
45#define EXYNOS4_TMU_REG_TRIG_LEVEL1 0x54
46#define EXYNOS4_TMU_REG_TRIG_LEVEL2 0x58
47#define EXYNOS4_TMU_REG_TRIG_LEVEL3 0x5C
48#define EXYNOS4_TMU_REG_PAST_TEMP0 0x60
49#define EXYNOS4_TMU_REG_PAST_TEMP1 0x64
50#define EXYNOS4_TMU_REG_PAST_TEMP2 0x68
51#define EXYNOS4_TMU_REG_PAST_TEMP3 0x6C
52#define EXYNOS4_TMU_REG_INTEN 0x70
53#define EXYNOS4_TMU_REG_INTSTAT 0x74
54#define EXYNOS4_TMU_REG_INTCLEAR 0x78
55
56#define EXYNOS4_TMU_GAIN_SHIFT 8
57#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT 24
58
59#define EXYNOS4_TMU_TRIM_TEMP_MASK 0xff
60#define EXYNOS4_TMU_CORE_ON 3
61#define EXYNOS4_TMU_CORE_OFF 2
62#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET 50
63#define EXYNOS4_TMU_TRIG_LEVEL0_MASK 0x1
64#define EXYNOS4_TMU_TRIG_LEVEL1_MASK 0x10
65#define EXYNOS4_TMU_TRIG_LEVEL2_MASK 0x100
66#define EXYNOS4_TMU_TRIG_LEVEL3_MASK 0x1000
67#define EXYNOS4_TMU_INTCLEAR_VAL 0x1111
68
69struct exynos4_tmu_data {
70 struct exynos4_tmu_platform_data *pdata;
9d97e5c8
DK
71 struct resource *mem;
72 void __iomem *base;
73 int irq;
74 struct work_struct irq_work;
75 struct mutex lock;
76 struct clk *clk;
77 u8 temp_error1, temp_error2;
78};
79
80/*
81 * TMU treats temperature as a mapped temperature code.
82 * The temperature is converted differently depending on the calibration type.
83 */
84static int temp_to_code(struct exynos4_tmu_data *data, u8 temp)
85{
86 struct exynos4_tmu_platform_data *pdata = data->pdata;
87 int temp_code;
88
89 /* temp should range between 25 and 125 */
90 if (temp < 25 || temp > 125) {
91 temp_code = -EINVAL;
92 goto out;
93 }
94
95 switch (pdata->cal_type) {
96 case TYPE_TWO_POINT_TRIMMING:
97 temp_code = (temp - 25) *
98 (data->temp_error2 - data->temp_error1) /
99 (85 - 25) + data->temp_error1;
100 break;
101 case TYPE_ONE_POINT_TRIMMING:
102 temp_code = temp + data->temp_error1 - 25;
103 break;
104 default:
105 temp_code = temp + EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
106 break;
107 }
108out:
109 return temp_code;
110}
111
112/*
113 * Calculate a temperature value from a temperature code.
114 * The unit of the temperature is degree Celsius.
115 */
116static int code_to_temp(struct exynos4_tmu_data *data, u8 temp_code)
117{
118 struct exynos4_tmu_platform_data *pdata = data->pdata;
119 int temp;
120
121 /* temp_code should range between 75 and 175 */
122 if (temp_code < 75 || temp_code > 175) {
123 temp = -ENODATA;
124 goto out;
125 }
126
127 switch (pdata->cal_type) {
128 case TYPE_TWO_POINT_TRIMMING:
129 temp = (temp_code - data->temp_error1) * (85 - 25) /
130 (data->temp_error2 - data->temp_error1) + 25;
131 break;
132 case TYPE_ONE_POINT_TRIMMING:
133 temp = temp_code - data->temp_error1 + 25;
134 break;
135 default:
136 temp = temp_code - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
137 break;
138 }
139out:
140 return temp;
141}
142
143static int exynos4_tmu_initialize(struct platform_device *pdev)
144{
145 struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
146 struct exynos4_tmu_platform_data *pdata = data->pdata;
147 unsigned int status, trim_info;
148 int ret = 0, threshold_code;
149
150 mutex_lock(&data->lock);
151 clk_enable(data->clk);
152
153 status = readb(data->base + EXYNOS4_TMU_REG_STATUS);
154 if (!status) {
155 ret = -EBUSY;
156 goto out;
157 }
158
159 /* Save trimming info in order to perform calibration */
160 trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO);
161 data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK;
162 data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK);
163
164 /* Write temperature code for threshold */
165 threshold_code = temp_to_code(data, pdata->threshold);
166 if (threshold_code < 0) {
167 ret = threshold_code;
168 goto out;
169 }
170 writeb(threshold_code,
171 data->base + EXYNOS4_TMU_REG_THRESHOLD_TEMP);
172
173 writeb(pdata->trigger_levels[0],
174 data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0);
175 writeb(pdata->trigger_levels[1],
176 data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1);
177 writeb(pdata->trigger_levels[2],
178 data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2);
179 writeb(pdata->trigger_levels[3],
180 data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3);
181
182 writel(EXYNOS4_TMU_INTCLEAR_VAL,
183 data->base + EXYNOS4_TMU_REG_INTCLEAR);
184out:
185 clk_disable(data->clk);
186 mutex_unlock(&data->lock);
187
188 return ret;
189}
190
191static void exynos4_tmu_control(struct platform_device *pdev, bool on)
192{
193 struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
194 struct exynos4_tmu_platform_data *pdata = data->pdata;
195 unsigned int con, interrupt_en;
196
197 mutex_lock(&data->lock);
198 clk_enable(data->clk);
199
200 con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT |
201 pdata->gain << EXYNOS4_TMU_GAIN_SHIFT;
202 if (on) {
203 con |= EXYNOS4_TMU_CORE_ON;
204 interrupt_en = pdata->trigger_level3_en << 12 |
205 pdata->trigger_level2_en << 8 |
206 pdata->trigger_level1_en << 4 |
207 pdata->trigger_level0_en;
208 } else {
209 con |= EXYNOS4_TMU_CORE_OFF;
210 interrupt_en = 0; /* Disable all interrupts */
211 }
212 writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN);
213 writel(con, data->base + EXYNOS4_TMU_REG_CONTROL);
214
215 clk_disable(data->clk);
216 mutex_unlock(&data->lock);
217}
218
219static int exynos4_tmu_read(struct exynos4_tmu_data *data)
220{
221 u8 temp_code;
222 int temp;
223
224 mutex_lock(&data->lock);
225 clk_enable(data->clk);
226
227 temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP);
228 temp = code_to_temp(data, temp_code);
229
230 clk_disable(data->clk);
231 mutex_unlock(&data->lock);
232
233 return temp;
234}
235
236static void exynos4_tmu_work(struct work_struct *work)
237{
238 struct exynos4_tmu_data *data = container_of(work,
239 struct exynos4_tmu_data, irq_work);
240
241 mutex_lock(&data->lock);
242 clk_enable(data->clk);
243
244 writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
245
9d97e5c8
DK
246 enable_irq(data->irq);
247
248 clk_disable(data->clk);
249 mutex_unlock(&data->lock);
250}
251
252static irqreturn_t exynos4_tmu_irq(int irq, void *id)
253{
254 struct exynos4_tmu_data *data = id;
255
256 disable_irq_nosync(irq);
257 schedule_work(&data->irq_work);
258
259 return IRQ_HANDLED;
260}
261
9d97e5c8
DK
262static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
263{
264 struct exynos4_tmu_data *data;
265 struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
266 int ret;
267
268 if (!pdata) {
269 dev_err(&pdev->dev, "No platform init data supplied.\n");
270 return -ENODEV;
271 }
272
273 data = kzalloc(sizeof(struct exynos4_tmu_data), GFP_KERNEL);
274 if (!data) {
275 dev_err(&pdev->dev, "Failed to allocate driver structure\n");
276 return -ENOMEM;
277 }
278
279 data->irq = platform_get_irq(pdev, 0);
280 if (data->irq < 0) {
281 ret = data->irq;
282 dev_err(&pdev->dev, "Failed to get platform irq\n");
283 goto err_free;
284 }
285
286 INIT_WORK(&data->irq_work, exynos4_tmu_work);
287
288 data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
289 if (!data->mem) {
290 ret = -ENOENT;
291 dev_err(&pdev->dev, "Failed to get platform resource\n");
292 goto err_free;
293 }
294
295 data->mem = request_mem_region(data->mem->start,
296 resource_size(data->mem), pdev->name);
297 if (!data->mem) {
298 ret = -ENODEV;
299 dev_err(&pdev->dev, "Failed to request memory region\n");
300 goto err_free;
301 }
302
303 data->base = ioremap(data->mem->start, resource_size(data->mem));
304 if (!data->base) {
305 ret = -ENODEV;
306 dev_err(&pdev->dev, "Failed to ioremap memory\n");
307 goto err_mem_region;
308 }
309
310 ret = request_irq(data->irq, exynos4_tmu_irq,
3f67f835 311 IRQF_TRIGGER_RISING,
9d97e5c8
DK
312 "exynos4-tmu", data);
313 if (ret) {
314 dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
315 goto err_io_remap;
316 }
317
318 data->clk = clk_get(NULL, "tmu_apbif");
319 if (IS_ERR(data->clk)) {
320 ret = PTR_ERR(data->clk);
321 dev_err(&pdev->dev, "Failed to get clock\n");
322 goto err_irq;
323 }
324
325 data->pdata = pdata;
326 platform_set_drvdata(pdev, data);
327 mutex_init(&data->lock);
328
329 ret = exynos4_tmu_initialize(pdev);
330 if (ret) {
331 dev_err(&pdev->dev, "Failed to initialize TMU\n");
332 goto err_clk;
333 }
334
9d97e5c8
DK
335 exynos4_tmu_control(pdev, true);
336
337 return 0;
9d97e5c8
DK
338err_clk:
339 platform_set_drvdata(pdev, NULL);
340 clk_put(data->clk);
341err_irq:
342 free_irq(data->irq, data);
343err_io_remap:
344 iounmap(data->base);
345err_mem_region:
346 release_mem_region(data->mem->start, resource_size(data->mem));
347err_free:
348 kfree(data);
349
350 return ret;
351}
352
353static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
354{
355 struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
356
357 exynos4_tmu_control(pdev, false);
358
9d97e5c8
DK
359 clk_put(data->clk);
360
361 free_irq(data->irq, data);
362
363 iounmap(data->base);
364 release_mem_region(data->mem->start, resource_size(data->mem));
365
366 platform_set_drvdata(pdev, NULL);
367
368 kfree(data);
369
370 return 0;
371}
372
08cd6753
RW
373#ifdef CONFIG_PM_SLEEP
374static int exynos4_tmu_suspend(struct device *dev)
9d97e5c8 375{
08cd6753 376 exynos4_tmu_control(to_platform_device(dev), false);
9d97e5c8
DK
377
378 return 0;
379}
380
08cd6753 381static int exynos4_tmu_resume(struct device *dev)
9d97e5c8 382{
08cd6753
RW
383 struct platform_device *pdev = to_platform_device(dev);
384
9d97e5c8
DK
385 exynos4_tmu_initialize(pdev);
386 exynos4_tmu_control(pdev, true);
387
388 return 0;
389}
08cd6753
RW
390
391static SIMPLE_DEV_PM_OPS(exynos4_tmu_pm,
392 exynos4_tmu_suspend, exynos4_tmu_resume);
c48cbba6 393#define EXYNOS4_TMU_PM (&exynos4_tmu_pm)
9d97e5c8 394#else
08cd6753 395#define EXYNOS4_TMU_PM NULL
9d97e5c8
DK
396#endif
397
398static struct platform_driver exynos4_tmu_driver = {
399 .driver = {
400 .name = "exynos4-tmu",
401 .owner = THIS_MODULE,
08cd6753 402 .pm = EXYNOS4_TMU_PM,
9d97e5c8
DK
403 },
404 .probe = exynos4_tmu_probe,
405 .remove = __devexit_p(exynos4_tmu_remove),
9d97e5c8
DK
406};
407
25a236a5 408module_platform_driver(exynos4_tmu_driver);
9d97e5c8
DK
409
410MODULE_DESCRIPTION("EXYNOS4 TMU Driver");
411MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
412MODULE_LICENSE("GPL");
413MODULE_ALIAS("platform:exynos4-tmu");