Linux 3.9-rc5
[deliverable/linux.git] / drivers / staging / omap-thermal / omap-bandgap.c
CommitLineData
8feaf0ce
EV
1/*
2 * OMAP4 Bandgap temperature sensor driver
3 *
4 * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/
5 * Author: J Keerthy <j-keerthy@ti.com>
6 * Author: Moiz Sonasath <m-sonasath@ti.com>
7 * Couple of fixes, DT and MFD adaptation:
8 * Eduardo Valentin <eduardo.valentin@ti.com>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * version 2 as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA
23 *
24 */
25
26#include <linux/module.h>
27#include <linux/export.h>
28#include <linux/init.h>
29#include <linux/kernel.h>
30#include <linux/interrupt.h>
31#include <linux/clk.h>
32#include <linux/gpio.h>
33#include <linux/platform_device.h>
34#include <linux/err.h>
35#include <linux/types.h>
36#include <linux/mutex.h>
37#include <linux/reboot.h>
38#include <linux/of_device.h>
39#include <linux/of_platform.h>
40#include <linux/of_irq.h>
2aeeb8ac 41#include <linux/io.h>
8feaf0ce
EV
42
43#include "omap-bandgap.h"
44
45static u32 omap_bandgap_readl(struct omap_bandgap *bg_ptr, u32 reg)
46{
47 return readl(bg_ptr->base + reg);
48}
49
50static void omap_bandgap_writel(struct omap_bandgap *bg_ptr, u32 val, u32 reg)
51{
52 writel(val, bg_ptr->base + reg);
53}
54
55static int omap_bandgap_power(struct omap_bandgap *bg_ptr, bool on)
56{
57 struct temp_sensor_registers *tsr;
58 int i;
59 u32 ctrl;
60
61 if (!OMAP_BANDGAP_HAS(bg_ptr, POWER_SWITCH))
62 return 0;
63
64 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
65 tsr = bg_ptr->conf->sensors[i].registers;
66 ctrl = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
67 ctrl &= ~tsr->bgap_tempsoff_mask;
68 /* active on 0 */
69 ctrl |= !on << __ffs(tsr->bgap_tempsoff_mask);
70
71 /* write BGAP_TEMPSOFF should be reset to 0 */
72 omap_bandgap_writel(bg_ptr, ctrl, tsr->temp_sensor_ctrl);
73 }
74
75 return 0;
76}
77
78/* This is the Talert handler. Call it only if HAS(TALERT) is set */
79static irqreturn_t talert_irq_handler(int irq, void *data)
80{
81 struct omap_bandgap *bg_ptr = data;
82 struct temp_sensor_registers *tsr;
83 u32 t_hot = 0, t_cold = 0, temp, ctrl;
84 int i;
85
86 bg_ptr = data;
87 /* Read the status of t_hot */
88 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
89 tsr = bg_ptr->conf->sensors[i].registers;
90 t_hot = omap_bandgap_readl(bg_ptr, tsr->bgap_status);
91 t_hot &= tsr->status_hot_mask;
92
93 /* Read the status of t_cold */
94 t_cold = omap_bandgap_readl(bg_ptr, tsr->bgap_status);
95 t_cold &= tsr->status_cold_mask;
96
97 if (!t_cold && !t_hot)
98 continue;
99
100 ctrl = omap_bandgap_readl(bg_ptr, tsr->bgap_mask_ctrl);
101 /*
102 * One TALERT interrupt: Two sources
103 * If the interrupt is due to t_hot then mask t_hot and
104 * and unmask t_cold else mask t_cold and unmask t_hot
105 */
106 if (t_hot) {
107 ctrl &= ~tsr->mask_hot_mask;
108 ctrl |= tsr->mask_cold_mask;
109 } else if (t_cold) {
110 ctrl &= ~tsr->mask_cold_mask;
111 ctrl |= tsr->mask_hot_mask;
112 }
113
114 omap_bandgap_writel(bg_ptr, ctrl, tsr->bgap_mask_ctrl);
115
71e303f5
EV
116 dev_dbg(bg_ptr->dev,
117 "%s: IRQ from %s sensor: hotevent %d coldevent %d\n",
118 __func__, bg_ptr->conf->sensors[i].domain,
119 t_hot, t_cold);
120
8feaf0ce
EV
121 /* read temperature */
122 temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
123 temp &= tsr->bgap_dtemp_mask;
124
125 /* report temperature to whom may concern */
126 if (bg_ptr->conf->report_temperature)
127 bg_ptr->conf->report_temperature(bg_ptr, i);
128 }
129
130 return IRQ_HANDLED;
131}
132
133/* This is the Tshut handler. Call it only if HAS(TSHUT) is set */
134static irqreturn_t omap_bandgap_tshut_irq_handler(int irq, void *data)
135{
136 orderly_poweroff(true);
137
138 return IRQ_HANDLED;
139}
140
141static
142int adc_to_temp_conversion(struct omap_bandgap *bg_ptr, int id, int adc_val,
143 int *t)
144{
145 struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[id].ts_data;
146
147 /* look up for temperature in the table and return the temperature */
148 if (adc_val < ts_data->adc_start_val || adc_val > ts_data->adc_end_val)
149 return -ERANGE;
150
151 *t = bg_ptr->conv_table[adc_val - ts_data->adc_start_val];
152
153 return 0;
154}
155
156static int temp_to_adc_conversion(long temp, struct omap_bandgap *bg_ptr, int i,
157 int *adc)
158{
159 struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[i].ts_data;
160 int high, low, mid;
161
162 low = 0;
163 high = ts_data->adc_end_val - ts_data->adc_start_val;
164 mid = (high + low) / 2;
165
463bf503 166 if (temp < bg_ptr->conv_table[low] || temp > bg_ptr->conv_table[high])
8feaf0ce
EV
167 return -EINVAL;
168
169 while (low < high) {
170 if (temp < bg_ptr->conv_table[mid])
171 high = mid - 1;
172 else
173 low = mid + 1;
174 mid = (low + high) / 2;
175 }
176
177 *adc = ts_data->adc_start_val + low;
178
179 return 0;
180}
181
182/* Talert masks. Call it only if HAS(TALERT) is set */
183static int temp_sensor_unmask_interrupts(struct omap_bandgap *bg_ptr, int id,
184 u32 t_hot, u32 t_cold)
185{
186 struct temp_sensor_registers *tsr;
187 u32 temp, reg_val;
188
189 /* Read the current on die temperature */
190 tsr = bg_ptr->conf->sensors[id].registers;
191 temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
192 temp &= tsr->bgap_dtemp_mask;
193
194 reg_val = omap_bandgap_readl(bg_ptr, tsr->bgap_mask_ctrl);
195 if (temp < t_hot)
196 reg_val |= tsr->mask_hot_mask;
197 else
198 reg_val &= ~tsr->mask_hot_mask;
199
200 if (t_cold < temp)
201 reg_val |= tsr->mask_cold_mask;
202 else
203 reg_val &= ~tsr->mask_cold_mask;
204 omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_mask_ctrl);
205
206 return 0;
207}
208
209static
210int add_hyst(int adc_val, int hyst_val, struct omap_bandgap *bg_ptr, int i,
211 u32 *sum)
212{
213 int temp, ret;
214
215 ret = adc_to_temp_conversion(bg_ptr, i, adc_val, &temp);
216 if (ret < 0)
217 return ret;
218
219 temp += hyst_val;
220
221 return temp_to_adc_conversion(temp, bg_ptr, i, sum);
222}
223
224/* Talert Thot threshold. Call it only if HAS(TALERT) is set */
225static
226int temp_sensor_configure_thot(struct omap_bandgap *bg_ptr, int id, int t_hot)
227{
228 struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[id].ts_data;
229 struct temp_sensor_registers *tsr;
230 u32 thresh_val, reg_val;
231 int cold, err = 0;
232
233 tsr = bg_ptr->conf->sensors[id].registers;
234
235 /* obtain the T cold value */
236 thresh_val = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
237 cold = (thresh_val & tsr->threshold_tcold_mask) >>
238 __ffs(tsr->threshold_tcold_mask);
239 if (t_hot <= cold) {
240 /* change the t_cold to t_hot - 5000 millidegrees */
241 err |= add_hyst(t_hot, -ts_data->hyst_val, bg_ptr, id, &cold);
242 /* write the new t_cold value */
243 reg_val = thresh_val & (~tsr->threshold_tcold_mask);
244 reg_val |= cold << __ffs(tsr->threshold_tcold_mask);
245 omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
246 thresh_val = reg_val;
247 }
248
249 /* write the new t_hot value */
250 reg_val = thresh_val & ~tsr->threshold_thot_mask;
251 reg_val |= (t_hot << __ffs(tsr->threshold_thot_mask));
252 omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
253 if (err) {
254 dev_err(bg_ptr->dev, "failed to reprogram thot threshold\n");
255 return -EIO;
256 }
257
258 return temp_sensor_unmask_interrupts(bg_ptr, id, t_hot, cold);
259}
260
261/* Talert Thot and Tcold thresholds. Call it only if HAS(TALERT) is set */
262static
263int temp_sensor_init_talert_thresholds(struct omap_bandgap *bg_ptr, int id,
264 int t_hot, int t_cold)
265{
266 struct temp_sensor_registers *tsr;
267 u32 reg_val, thresh_val;
268
269 tsr = bg_ptr->conf->sensors[id].registers;
270 thresh_val = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
271
272 /* write the new t_cold value */
273 reg_val = thresh_val & ~tsr->threshold_tcold_mask;
274 reg_val |= (t_cold << __ffs(tsr->threshold_tcold_mask));
275 omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
276
277 thresh_val = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
278
279 /* write the new t_hot value */
280 reg_val = thresh_val & ~tsr->threshold_thot_mask;
281 reg_val |= (t_hot << __ffs(tsr->threshold_thot_mask));
282 omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
283
284 reg_val = omap_bandgap_readl(bg_ptr, tsr->bgap_mask_ctrl);
285 reg_val |= tsr->mask_hot_mask;
286 reg_val |= tsr->mask_cold_mask;
287 omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_mask_ctrl);
288
289 return 0;
290}
291
292/* Talert Tcold threshold. Call it only if HAS(TALERT) is set */
293static
294int temp_sensor_configure_tcold(struct omap_bandgap *bg_ptr, int id,
295 int t_cold)
296{
297 struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[id].ts_data;
298 struct temp_sensor_registers *tsr;
299 u32 thresh_val, reg_val;
300 int hot, err = 0;
301
302 tsr = bg_ptr->conf->sensors[id].registers;
303 /* obtain the T cold value */
304 thresh_val = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
305 hot = (thresh_val & tsr->threshold_thot_mask) >>
306 __ffs(tsr->threshold_thot_mask);
307
308 if (t_cold >= hot) {
309 /* change the t_hot to t_cold + 5000 millidegrees */
310 err |= add_hyst(t_cold, ts_data->hyst_val, bg_ptr, id, &hot);
311 /* write the new t_hot value */
312 reg_val = thresh_val & (~tsr->threshold_thot_mask);
313 reg_val |= hot << __ffs(tsr->threshold_thot_mask);
314 omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
315 thresh_val = reg_val;
316 }
317
318 /* write the new t_cold value */
319 reg_val = thresh_val & ~tsr->threshold_tcold_mask;
320 reg_val |= (t_cold << __ffs(tsr->threshold_tcold_mask));
321 omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
322 if (err) {
323 dev_err(bg_ptr->dev, "failed to reprogram tcold threshold\n");
324 return -EIO;
325 }
326
327 return temp_sensor_unmask_interrupts(bg_ptr, id, hot, t_cold);
328}
329
330/* This is Tshut Thot config. Call it only if HAS(TSHUT_CONFIG) is set */
331static int temp_sensor_configure_tshut_hot(struct omap_bandgap *bg_ptr,
332 int id, int tshut_hot)
333{
334 struct temp_sensor_registers *tsr;
335 u32 reg_val;
336
337 tsr = bg_ptr->conf->sensors[id].registers;
338 reg_val = omap_bandgap_readl(bg_ptr, tsr->tshut_threshold);
339 reg_val &= ~tsr->tshut_hot_mask;
340 reg_val |= tshut_hot << __ffs(tsr->tshut_hot_mask);
341 omap_bandgap_writel(bg_ptr, reg_val, tsr->tshut_threshold);
342
343 return 0;
344}
345
346/* This is Tshut Tcold config. Call it only if HAS(TSHUT_CONFIG) is set */
347static int temp_sensor_configure_tshut_cold(struct omap_bandgap *bg_ptr,
348 int id, int tshut_cold)
349{
350 struct temp_sensor_registers *tsr;
351 u32 reg_val;
352
353 tsr = bg_ptr->conf->sensors[id].registers;
354 reg_val = omap_bandgap_readl(bg_ptr, tsr->tshut_threshold);
355 reg_val &= ~tsr->tshut_cold_mask;
356 reg_val |= tshut_cold << __ffs(tsr->tshut_cold_mask);
357 omap_bandgap_writel(bg_ptr, reg_val, tsr->tshut_threshold);
358
359 return 0;
360}
361
362/* This is counter config. Call it only if HAS(COUNTER) is set */
363static int configure_temp_sensor_counter(struct omap_bandgap *bg_ptr, int id,
364 u32 counter)
365{
366 struct temp_sensor_registers *tsr;
367 u32 val;
368
369 tsr = bg_ptr->conf->sensors[id].registers;
370 val = omap_bandgap_readl(bg_ptr, tsr->bgap_counter);
371 val &= ~tsr->counter_mask;
372 val |= counter << __ffs(tsr->counter_mask);
373 omap_bandgap_writel(bg_ptr, val, tsr->bgap_counter);
374
375 return 0;
376}
377
378#define bandgap_is_valid(b) \
379 (!IS_ERR_OR_NULL(b))
380#define bandgap_is_valid_sensor_id(b, i) \
381 ((i) >= 0 && (i) < (b)->conf->sensor_count)
382static inline int omap_bandgap_validate(struct omap_bandgap *bg_ptr, int id)
383{
384 if (!bandgap_is_valid(bg_ptr)) {
385 pr_err("%s: invalid bandgap pointer\n", __func__);
386 return -EINVAL;
387 }
388
389 if (!bandgap_is_valid_sensor_id(bg_ptr, id)) {
390 dev_err(bg_ptr->dev, "%s: sensor id out of range (%d)\n",
391 __func__, id);
392 return -ERANGE;
393 }
394
395 return 0;
396}
397
398/* Exposed APIs */
399/**
400 * omap_bandgap_read_thot() - reads sensor current thot
401 * @bg_ptr - pointer to bandgap instance
402 * @id - sensor id
403 * @thot - resulting current thot value
404 *
405 * returns 0 on success or the proper error code
406 */
407int omap_bandgap_read_thot(struct omap_bandgap *bg_ptr, int id,
408 int *thot)
409{
410 struct temp_sensor_registers *tsr;
411 u32 temp;
412 int ret;
413
414 ret = omap_bandgap_validate(bg_ptr, id);
415 if (ret)
416 return ret;
417
418 if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT))
419 return -ENOTSUPP;
420
421 tsr = bg_ptr->conf->sensors[id].registers;
422 temp = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
423 temp = (temp & tsr->threshold_thot_mask) >>
424 __ffs(tsr->threshold_thot_mask);
425 ret |= adc_to_temp_conversion(bg_ptr, id, temp, &temp);
426 if (ret) {
427 dev_err(bg_ptr->dev, "failed to read thot\n");
428 return -EIO;
429 }
430
431 *thot = temp;
432
433 return 0;
434}
435
436/**
437 * omap_bandgap_write_thot() - sets sensor current thot
438 * @bg_ptr - pointer to bandgap instance
439 * @id - sensor id
440 * @val - desired thot value
441 *
442 * returns 0 on success or the proper error code
443 */
444int omap_bandgap_write_thot(struct omap_bandgap *bg_ptr, int id, int val)
445{
446 struct temp_sensor_data *ts_data;
447 struct temp_sensor_registers *tsr;
448 u32 t_hot;
449 int ret;
450
451 ret = omap_bandgap_validate(bg_ptr, id);
452 if (ret)
453 return ret;
454
455 if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT))
456 return -ENOTSUPP;
457
458 ts_data = bg_ptr->conf->sensors[id].ts_data;
459 tsr = bg_ptr->conf->sensors[id].registers;
460
461 if (val < ts_data->min_temp + ts_data->hyst_val)
462 return -EINVAL;
463 ret = temp_to_adc_conversion(val, bg_ptr, id, &t_hot);
464 if (ret < 0)
465 return ret;
466
467 mutex_lock(&bg_ptr->bg_mutex);
468 temp_sensor_configure_thot(bg_ptr, id, t_hot);
469 mutex_unlock(&bg_ptr->bg_mutex);
470
471 return 0;
472}
473
474/**
475 * omap_bandgap_read_tcold() - reads sensor current tcold
476 * @bg_ptr - pointer to bandgap instance
477 * @id - sensor id
478 * @tcold - resulting current tcold value
479 *
480 * returns 0 on success or the proper error code
481 */
482int omap_bandgap_read_tcold(struct omap_bandgap *bg_ptr, int id,
483 int *tcold)
484{
485 struct temp_sensor_registers *tsr;
486 u32 temp;
487 int ret;
488
489 ret = omap_bandgap_validate(bg_ptr, id);
490 if (ret)
491 return ret;
492
493 if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT))
494 return -ENOTSUPP;
495
496 tsr = bg_ptr->conf->sensors[id].registers;
497 temp = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
498 temp = (temp & tsr->threshold_tcold_mask)
499 >> __ffs(tsr->threshold_tcold_mask);
500 ret |= adc_to_temp_conversion(bg_ptr, id, temp, &temp);
501 if (ret)
502 return -EIO;
503
504 *tcold = temp;
505
506 return 0;
507}
508
509/**
510 * omap_bandgap_write_tcold() - sets the sensor tcold
511 * @bg_ptr - pointer to bandgap instance
512 * @id - sensor id
513 * @val - desired tcold value
514 *
515 * returns 0 on success or the proper error code
516 */
517int omap_bandgap_write_tcold(struct omap_bandgap *bg_ptr, int id, int val)
518{
519 struct temp_sensor_data *ts_data;
520 struct temp_sensor_registers *tsr;
521 u32 t_cold;
522 int ret;
523
524 ret = omap_bandgap_validate(bg_ptr, id);
525 if (ret)
526 return ret;
527
528 if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT))
529 return -ENOTSUPP;
530
531 ts_data = bg_ptr->conf->sensors[id].ts_data;
532 tsr = bg_ptr->conf->sensors[id].registers;
533 if (val > ts_data->max_temp + ts_data->hyst_val)
534 return -EINVAL;
535
536 ret = temp_to_adc_conversion(val, bg_ptr, id, &t_cold);
537 if (ret < 0)
538 return ret;
539
540 mutex_lock(&bg_ptr->bg_mutex);
541 temp_sensor_configure_tcold(bg_ptr, id, t_cold);
542 mutex_unlock(&bg_ptr->bg_mutex);
543
544 return 0;
545}
546
547/**
548 * omap_bandgap_read_update_interval() - read the sensor update interval
549 * @bg_ptr - pointer to bandgap instance
550 * @id - sensor id
551 * @interval - resulting update interval in miliseconds
552 *
553 * returns 0 on success or the proper error code
554 */
555int omap_bandgap_read_update_interval(struct omap_bandgap *bg_ptr, int id,
556 int *interval)
557{
558 struct temp_sensor_registers *tsr;
559 u32 time;
560 int ret;
561
562 ret = omap_bandgap_validate(bg_ptr, id);
563 if (ret)
564 return ret;
565
566 if (!OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
567 return -ENOTSUPP;
568
569 tsr = bg_ptr->conf->sensors[id].registers;
570 time = omap_bandgap_readl(bg_ptr, tsr->bgap_counter);
8feaf0ce
EV
571 time = (time & tsr->counter_mask) >> __ffs(tsr->counter_mask);
572 time = time * 1000 / bg_ptr->clk_rate;
573
574 *interval = time;
575
576 return 0;
577}
578
579/**
580 * omap_bandgap_write_update_interval() - set the update interval
581 * @bg_ptr - pointer to bandgap instance
582 * @id - sensor id
583 * @interval - desired update interval in miliseconds
584 *
585 * returns 0 on success or the proper error code
586 */
587int omap_bandgap_write_update_interval(struct omap_bandgap *bg_ptr,
588 int id, u32 interval)
589{
590 int ret = omap_bandgap_validate(bg_ptr, id);
591 if (ret)
592 return ret;
593
594 if (!OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
595 return -ENOTSUPP;
596
597 interval = interval * bg_ptr->clk_rate / 1000;
598 mutex_lock(&bg_ptr->bg_mutex);
599 configure_temp_sensor_counter(bg_ptr, id, interval);
600 mutex_unlock(&bg_ptr->bg_mutex);
601
602 return 0;
603}
604
605/**
606 * omap_bandgap_read_temperature() - report current temperature
607 * @bg_ptr - pointer to bandgap instance
608 * @id - sensor id
609 * @temperature - resulting temperature
610 *
611 * returns 0 on success or the proper error code
612 */
613int omap_bandgap_read_temperature(struct omap_bandgap *bg_ptr, int id,
614 int *temperature)
615{
616 struct temp_sensor_registers *tsr;
617 u32 temp;
618 int ret;
619
620 ret = omap_bandgap_validate(bg_ptr, id);
621 if (ret)
622 return ret;
623
624 tsr = bg_ptr->conf->sensors[id].registers;
625 temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
626 temp &= tsr->bgap_dtemp_mask;
627
628 ret |= adc_to_temp_conversion(bg_ptr, id, temp, &temp);
629 if (ret)
630 return -EIO;
631
632 *temperature = temp;
633
634 return 0;
635}
636
637/**
638 * omap_bandgap_set_sensor_data() - helper function to store thermal
639 * framework related data.
640 * @bg_ptr - pointer to bandgap instance
641 * @id - sensor id
642 * @data - thermal framework related data to be stored
643 *
644 * returns 0 on success or the proper error code
645 */
646int omap_bandgap_set_sensor_data(struct omap_bandgap *bg_ptr, int id,
647 void *data)
648{
649 int ret = omap_bandgap_validate(bg_ptr, id);
650 if (ret)
651 return ret;
652
653 bg_ptr->conf->sensors[id].data = data;
654
655 return 0;
656}
657
658/**
659 * omap_bandgap_get_sensor_data() - helper function to get thermal
660 * framework related data.
661 * @bg_ptr - pointer to bandgap instance
662 * @id - sensor id
663 *
664 * returns data stored by set function with sensor id on success or NULL
665 */
666void *omap_bandgap_get_sensor_data(struct omap_bandgap *bg_ptr, int id)
667{
668 int ret = omap_bandgap_validate(bg_ptr, id);
669 if (ret)
670 return ERR_PTR(ret);
671
672 return bg_ptr->conf->sensors[id].data;
673}
674
675static int
676omap_bandgap_force_single_read(struct omap_bandgap *bg_ptr, int id)
677{
678 struct temp_sensor_registers *tsr;
679 u32 temp = 0, counter = 1000;
680
681 tsr = bg_ptr->conf->sensors[id].registers;
682 /* Select single conversion mode */
683 if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG)) {
684 temp = omap_bandgap_readl(bg_ptr, tsr->bgap_mode_ctrl);
685 temp &= ~(1 << __ffs(tsr->mode_ctrl_mask));
686 omap_bandgap_writel(bg_ptr, temp, tsr->bgap_mode_ctrl);
687 }
688
689 /* Start of Conversion = 1 */
690 temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
691 temp |= 1 << __ffs(tsr->bgap_soc_mask);
692 omap_bandgap_writel(bg_ptr, temp, tsr->temp_sensor_ctrl);
693 /* Wait until DTEMP is updated */
694 temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
695 temp &= (tsr->bgap_dtemp_mask);
696 while ((temp == 0) && --counter) {
697 temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
698 temp &= (tsr->bgap_dtemp_mask);
699 }
700 /* Start of Conversion = 0 */
701 temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
702 temp &= ~(1 << __ffs(tsr->bgap_soc_mask));
703 omap_bandgap_writel(bg_ptr, temp, tsr->temp_sensor_ctrl);
704
705 return 0;
706}
707
708/**
709 * enable_continuous_mode() - One time enabling of continuous conversion mode
710 * @bg_ptr - pointer to scm instance
711 *
712 * Call this function only if HAS(MODE_CONFIG) is set
713 */
714static int enable_continuous_mode(struct omap_bandgap *bg_ptr)
715{
716 struct temp_sensor_registers *tsr;
717 int i;
718 u32 val;
719
720 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
721 /* Perform a single read just before enabling continuous */
722 omap_bandgap_force_single_read(bg_ptr, i);
723 tsr = bg_ptr->conf->sensors[i].registers;
724 val = omap_bandgap_readl(bg_ptr, tsr->bgap_mode_ctrl);
725 val |= 1 << __ffs(tsr->mode_ctrl_mask);
726 omap_bandgap_writel(bg_ptr, val, tsr->bgap_mode_ctrl);
727 }
728
729 return 0;
730}
731
732static int omap_bandgap_tshut_init(struct omap_bandgap *bg_ptr,
733 struct platform_device *pdev)
734{
735 int gpio_nr = bg_ptr->tshut_gpio;
736 int status;
737
738 /* Request for gpio_86 line */
739 status = gpio_request(gpio_nr, "tshut");
740 if (status < 0) {
741 dev_err(bg_ptr->dev,
742 "Could not request for TSHUT GPIO:%i\n", 86);
743 return status;
744 }
745 status = gpio_direction_input(gpio_nr);
746 if (status) {
747 dev_err(bg_ptr->dev,
748 "Cannot set input TSHUT GPIO %d\n", gpio_nr);
749 return status;
750 }
751
752 status = request_irq(gpio_to_irq(gpio_nr),
753 omap_bandgap_tshut_irq_handler,
754 IRQF_TRIGGER_RISING, "tshut",
755 NULL);
756 if (status) {
757 gpio_free(gpio_nr);
758 dev_err(bg_ptr->dev, "request irq failed for TSHUT");
759 }
760
761 return 0;
762}
763
764/* Initialization of Talert. Call it only if HAS(TALERT) is set */
765static int omap_bandgap_talert_init(struct omap_bandgap *bg_ptr,
766 struct platform_device *pdev)
767{
768 int ret;
769
770 bg_ptr->irq = platform_get_irq(pdev, 0);
771 if (bg_ptr->irq < 0) {
772 dev_err(&pdev->dev, "get_irq failed\n");
773 return bg_ptr->irq;
774 }
775 ret = request_threaded_irq(bg_ptr->irq, NULL,
776 talert_irq_handler,
777 IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
778 "talert", bg_ptr);
779 if (ret) {
780 dev_err(&pdev->dev, "Request threaded irq failed.\n");
781 return ret;
782 }
783
784 return 0;
785}
786
787static const struct of_device_id of_omap_bandgap_match[];
788static struct omap_bandgap *omap_bandgap_build(struct platform_device *pdev)
789{
790 struct device_node *node = pdev->dev.of_node;
791 const struct of_device_id *of_id;
792 struct omap_bandgap *bg_ptr;
793 struct resource *res;
794 u32 prop;
795 int i;
796
797 /* just for the sake */
798 if (!node) {
799 dev_err(&pdev->dev, "no platform information available\n");
800 return ERR_PTR(-EINVAL);
801 }
802
803 bg_ptr = devm_kzalloc(&pdev->dev, sizeof(struct omap_bandgap),
804 GFP_KERNEL);
805 if (!bg_ptr) {
806 dev_err(&pdev->dev, "Unable to allocate mem for driver ref\n");
807 return ERR_PTR(-ENOMEM);
808 }
809
810 of_id = of_match_device(of_omap_bandgap_match, &pdev->dev);
811 if (of_id)
812 bg_ptr->conf = of_id->data;
813
814 i = 0;
815 do {
816 void __iomem *chunk;
817
818 res = platform_get_resource(pdev, IORESOURCE_MEM, i);
819 if (!res)
820 break;
97f4be60 821 chunk = devm_ioremap_resource(&pdev->dev, res);
8feaf0ce
EV
822 if (i == 0)
823 bg_ptr->base = chunk;
97f4be60
TR
824 if (IS_ERR(chunk))
825 return ERR_CAST(chunk);
826
8feaf0ce
EV
827 i++;
828 } while (res);
829
830 if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) {
831 if (of_property_read_u32(node, "ti,tshut-gpio", &prop) < 0) {
832 dev_err(&pdev->dev, "missing tshut gpio in device tree\n");
833 return ERR_PTR(-EINVAL);
834 }
835 bg_ptr->tshut_gpio = prop;
836 if (!gpio_is_valid(bg_ptr->tshut_gpio)) {
837 dev_err(&pdev->dev, "invalid gpio for tshut (%d)\n",
838 bg_ptr->tshut_gpio);
839 return ERR_PTR(-EINVAL);
840 }
841 }
842
843 return bg_ptr;
844}
845
846static
db53ac71 847int omap_bandgap_probe(struct platform_device *pdev)
8feaf0ce
EV
848{
849 struct omap_bandgap *bg_ptr;
850 int clk_rate, ret = 0, i;
851
852 bg_ptr = omap_bandgap_build(pdev);
853 if (IS_ERR_OR_NULL(bg_ptr)) {
854 dev_err(&pdev->dev, "failed to fetch platform data\n");
855 return PTR_ERR(bg_ptr);
856 }
857 bg_ptr->dev = &pdev->dev;
858
859 if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) {
860 ret = omap_bandgap_tshut_init(bg_ptr, pdev);
861 if (ret) {
862 dev_err(&pdev->dev,
863 "failed to initialize system tshut IRQ\n");
864 return ret;
865 }
866 }
867
868 bg_ptr->fclock = clk_get(NULL, bg_ptr->conf->fclock_name);
869 ret = IS_ERR_OR_NULL(bg_ptr->fclock);
870 if (ret) {
871 dev_err(&pdev->dev, "failed to request fclock reference\n");
872 goto free_irqs;
873 }
874
875 bg_ptr->div_clk = clk_get(NULL, bg_ptr->conf->div_ck_name);
876 ret = IS_ERR_OR_NULL(bg_ptr->div_clk);
877 if (ret) {
878 dev_err(&pdev->dev,
879 "failed to request div_ts_ck clock ref\n");
880 goto free_irqs;
881 }
882
883 bg_ptr->conv_table = bg_ptr->conf->conv_table;
884 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
885 struct temp_sensor_registers *tsr;
886 u32 val;
887
888 tsr = bg_ptr->conf->sensors[i].registers;
889 /*
890 * check if the efuse has a non-zero value if not
891 * it is an untrimmed sample and the temperatures
892 * may not be accurate
893 */
894 val = omap_bandgap_readl(bg_ptr, tsr->bgap_efuse);
895 if (ret || !val)
896 dev_info(&pdev->dev,
897 "Non-trimmed BGAP, Temp not accurate\n");
898 }
899
900 clk_rate = clk_round_rate(bg_ptr->div_clk,
901 bg_ptr->conf->sensors[0].ts_data->max_freq);
902 if (clk_rate < bg_ptr->conf->sensors[0].ts_data->min_freq ||
903 clk_rate == 0xffffffff) {
904 ret = -ENODEV;
905 dev_err(&pdev->dev, "wrong clock rate (%d)\n", clk_rate);
906 goto put_clks;
907 }
908
909 ret = clk_set_rate(bg_ptr->div_clk, clk_rate);
910 if (ret)
911 dev_err(&pdev->dev, "Cannot re-set clock rate. Continuing\n");
912
913 bg_ptr->clk_rate = clk_rate;
914 clk_enable(bg_ptr->fclock);
915
916 mutex_init(&bg_ptr->bg_mutex);
917 bg_ptr->dev = &pdev->dev;
918 platform_set_drvdata(pdev, bg_ptr);
919
920 omap_bandgap_power(bg_ptr, true);
921
922 /* Set default counter to 1 for now */
923 if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
924 for (i = 0; i < bg_ptr->conf->sensor_count; i++)
925 configure_temp_sensor_counter(bg_ptr, i, 1);
926
927 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
928 struct temp_sensor_data *ts_data;
929
930 ts_data = bg_ptr->conf->sensors[i].ts_data;
931
932 if (OMAP_BANDGAP_HAS(bg_ptr, TALERT))
933 temp_sensor_init_talert_thresholds(bg_ptr, i,
934 ts_data->t_hot,
935 ts_data->t_cold);
936 if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG)) {
937 temp_sensor_configure_tshut_hot(bg_ptr, i,
938 ts_data->tshut_hot);
939 temp_sensor_configure_tshut_cold(bg_ptr, i,
940 ts_data->tshut_cold);
941 }
942 }
943
944 if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG))
945 enable_continuous_mode(bg_ptr);
946
947 /* Set .250 seconds time as default counter */
948 if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
949 for (i = 0; i < bg_ptr->conf->sensor_count; i++)
950 configure_temp_sensor_counter(bg_ptr, i,
951 bg_ptr->clk_rate / 4);
952
953 /* Every thing is good? Then expose the sensors */
954 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
955 char *domain;
956
04a4d10d
EV
957 if (bg_ptr->conf->sensors[i].register_cooling)
958 bg_ptr->conf->sensors[i].register_cooling(bg_ptr, i);
959
8feaf0ce
EV
960 domain = bg_ptr->conf->sensors[i].domain;
961 if (bg_ptr->conf->expose_sensor)
962 bg_ptr->conf->expose_sensor(bg_ptr, i, domain);
8feaf0ce
EV
963 }
964
965 /*
966 * Enable the Interrupts once everything is set. Otherwise irq handler
967 * might be called as soon as it is enabled where as rest of framework
968 * is still getting initialised.
969 */
970 if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) {
971 ret = omap_bandgap_talert_init(bg_ptr, pdev);
972 if (ret) {
973 dev_err(&pdev->dev, "failed to initialize Talert IRQ\n");
974 i = bg_ptr->conf->sensor_count;
975 goto disable_clk;
976 }
977 }
978
979 return 0;
980
981disable_clk:
982 clk_disable(bg_ptr->fclock);
983put_clks:
984 clk_put(bg_ptr->fclock);
985 clk_put(bg_ptr->div_clk);
986free_irqs:
987 if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) {
988 free_irq(gpio_to_irq(bg_ptr->tshut_gpio), NULL);
989 gpio_free(bg_ptr->tshut_gpio);
990 }
991
992 return ret;
993}
994
995static
434bd035 996int omap_bandgap_remove(struct platform_device *pdev)
8feaf0ce
EV
997{
998 struct omap_bandgap *bg_ptr = platform_get_drvdata(pdev);
999 int i;
1000
1001 /* First thing is to remove sensor interfaces */
1002 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
1003 if (bg_ptr->conf->sensors[i].register_cooling)
1004 bg_ptr->conf->sensors[i].unregister_cooling(bg_ptr, i);
1005
1006 if (bg_ptr->conf->remove_sensor)
1007 bg_ptr->conf->remove_sensor(bg_ptr, i);
1008 }
1009
1010 omap_bandgap_power(bg_ptr, false);
1011
1012 clk_disable(bg_ptr->fclock);
1013 clk_put(bg_ptr->fclock);
1014 clk_put(bg_ptr->div_clk);
1015
1016 if (OMAP_BANDGAP_HAS(bg_ptr, TALERT))
1017 free_irq(bg_ptr->irq, bg_ptr);
1018
1019 if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) {
1020 free_irq(gpio_to_irq(bg_ptr->tshut_gpio), NULL);
1021 gpio_free(bg_ptr->tshut_gpio);
1022 }
1023
1024 return 0;
1025}
1026
1027#ifdef CONFIG_PM
1028static int omap_bandgap_save_ctxt(struct omap_bandgap *bg_ptr)
1029{
1030 int i;
1031
1032 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
1033 struct temp_sensor_registers *tsr;
1034 struct temp_sensor_regval *rval;
1035
1036 rval = &bg_ptr->conf->sensors[i].regval;
1037 tsr = bg_ptr->conf->sensors[i].registers;
1038
1039 if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG))
1040 rval->bg_mode_ctrl = omap_bandgap_readl(bg_ptr,
76d2cd30 1041 tsr->bgap_mode_ctrl);
8feaf0ce
EV
1042 if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
1043 rval->bg_counter = omap_bandgap_readl(bg_ptr,
76d2cd30 1044 tsr->bgap_counter);
8feaf0ce
EV
1045 if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) {
1046 rval->bg_threshold = omap_bandgap_readl(bg_ptr,
76d2cd30 1047 tsr->bgap_threshold);
8feaf0ce 1048 rval->bg_ctrl = omap_bandgap_readl(bg_ptr,
76d2cd30 1049 tsr->bgap_mask_ctrl);
8feaf0ce
EV
1050 }
1051
1052 if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG))
1053 rval->tshut_threshold = omap_bandgap_readl(bg_ptr,
76d2cd30 1054 tsr->tshut_threshold);
8feaf0ce
EV
1055 }
1056
1057 return 0;
1058}
1059
1060static int omap_bandgap_restore_ctxt(struct omap_bandgap *bg_ptr)
1061{
1062 int i;
8feaf0ce
EV
1063
1064 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
1065 struct temp_sensor_registers *tsr;
1066 struct temp_sensor_regval *rval;
1067 u32 val = 0;
1068
1069 rval = &bg_ptr->conf->sensors[i].regval;
1070 tsr = bg_ptr->conf->sensors[i].registers;
1071
1072 if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
1073 val = omap_bandgap_readl(bg_ptr, tsr->bgap_counter);
1074
b87ea759
RF
1075 if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG))
1076 omap_bandgap_writel(bg_ptr,
1077 rval->tshut_threshold,
1078 tsr->tshut_threshold);
1079 /* Force immediate temperature measurement and update
1080 * of the DTEMP field
1081 */
1082 omap_bandgap_force_single_read(bg_ptr, i);
1083
1084 if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
1085 omap_bandgap_writel(bg_ptr, rval->bg_counter,
1086 tsr->bgap_counter);
1087 if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG))
1088 omap_bandgap_writel(bg_ptr, rval->bg_mode_ctrl,
1089 tsr->bgap_mode_ctrl);
1090 if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) {
1091 omap_bandgap_writel(bg_ptr,
1092 rval->bg_threshold,
1093 tsr->bgap_threshold);
1094 omap_bandgap_writel(bg_ptr, rval->bg_ctrl,
1095 tsr->bgap_mask_ctrl);
8feaf0ce
EV
1096 }
1097 }
1098
1099 return 0;
1100}
1101
1102static int omap_bandgap_suspend(struct device *dev)
1103{
1104 struct omap_bandgap *bg_ptr = dev_get_drvdata(dev);
1105 int err;
1106
1107 err = omap_bandgap_save_ctxt(bg_ptr);
1108 omap_bandgap_power(bg_ptr, false);
1109 clk_disable(bg_ptr->fclock);
1110
1111 return err;
1112}
1113
1114static int omap_bandgap_resume(struct device *dev)
1115{
1116 struct omap_bandgap *bg_ptr = dev_get_drvdata(dev);
1117
1118 clk_enable(bg_ptr->fclock);
1119 omap_bandgap_power(bg_ptr, true);
1120
1121 return omap_bandgap_restore_ctxt(bg_ptr);
1122}
1123static const struct dev_pm_ops omap_bandgap_dev_pm_ops = {
1124 SET_SYSTEM_SLEEP_PM_OPS(omap_bandgap_suspend,
1125 omap_bandgap_resume)
1126};
1127
1128#define DEV_PM_OPS (&omap_bandgap_dev_pm_ops)
1129#else
1130#define DEV_PM_OPS NULL
1131#endif
1132
1133static const struct of_device_id of_omap_bandgap_match[] = {
1a31270e
EV
1134#ifdef CONFIG_OMAP4_THERMAL
1135 {
1136 .compatible = "ti,omap4430-bandgap",
1137 .data = (void *)&omap4430_data,
1138 },
1139 {
1140 .compatible = "ti,omap4460-bandgap",
1141 .data = (void *)&omap4460_data,
1142 },
1143 {
1144 .compatible = "ti,omap4470-bandgap",
1145 .data = (void *)&omap4470_data,
1146 },
949f5a50
EV
1147#endif
1148#ifdef CONFIG_OMAP5_THERMAL
1149 {
1150 .compatible = "ti,omap5430-bandgap",
1151 .data = (void *)&omap5430_data,
1152 },
1a31270e 1153#endif
8feaf0ce
EV
1154 /* Sentinel */
1155 { },
1156};
1157MODULE_DEVICE_TABLE(of, of_omap_bandgap_match);
1158
1159static struct platform_driver omap_bandgap_sensor_driver = {
1160 .probe = omap_bandgap_probe,
1161 .remove = omap_bandgap_remove,
1162 .driver = {
1163 .name = "omap-bandgap",
1164 .pm = DEV_PM_OPS,
1165 .of_match_table = of_omap_bandgap_match,
1166 },
1167};
1168
1169module_platform_driver(omap_bandgap_sensor_driver);
1170
1171MODULE_DESCRIPTION("OMAP4+ bandgap temperature sensor driver");
1172MODULE_LICENSE("GPL v2");
1173MODULE_ALIAS("platform:omap-bandgap");
1174MODULE_AUTHOR("Texas Instrument Inc.");
This page took 0.155878 seconds and 5 git commands to generate.