blob: 7927c591716f09c8ccf3a9d24832e89ab08d928f [file] [log] [blame]
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +03001/*
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>
Eduardo Valentin2aeeb8a2012-11-13 14:10:00 -040041#include <linux/io.h>
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +030042
43#include "omap-bandgap.h"
44
Eduardo Valentin8abbe712013-03-15 09:00:05 -040045/*** Helper functions to access registers and their bitfields ***/
46
Eduardo Valentin9c468aa2013-03-15 08:59:56 -040047/**
48 * omap_bandgap_readl() - simple read helper function
49 * @bg_ptr: pointer to omap_bandgap structure
50 * @reg: desired register (offset) to be read
51 *
52 * Helper function to read bandgap registers. It uses the io remapped area.
53 * Returns the register value.
54 */
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +030055static u32 omap_bandgap_readl(struct omap_bandgap *bg_ptr, u32 reg)
56{
57 return readl(bg_ptr->base + reg);
58}
59
Eduardo Valentin9c468aa2013-03-15 08:59:56 -040060/**
61 * omap_bandgap_writel() - simple write helper function
62 * @bg_ptr: pointer to omap_bandgap structure
63 * @val: desired register value to be written
64 * @reg: desired register (offset) to be written
65 *
66 * Helper function to write bandgap registers. It uses the io remapped area.
67 */
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +030068static void omap_bandgap_writel(struct omap_bandgap *bg_ptr, u32 val, u32 reg)
69{
70 writel(val, bg_ptr->base + reg);
71}
72
Eduardo Valentin9c468aa2013-03-15 08:59:56 -040073/**
74 * DOC: macro to update bits.
75 *
76 * RMW_BITS() - used to read, modify and update bandgap bitfields.
77 * The value passed will be shifted.
78 */
Eduardo Valentind3c291a2013-03-15 08:59:55 -040079#define RMW_BITS(bg_ptr, id, reg, mask, val) \
80do { \
81 struct temp_sensor_registers *t; \
82 u32 r; \
83 \
84 t = bg_ptr->conf->sensors[(id)].registers; \
85 r = omap_bandgap_readl(bg_ptr, t->reg); \
86 r &= ~t->mask; \
87 r |= (val) << __ffs(t->mask); \
88 omap_bandgap_writel(bg_ptr, r, t->reg); \
89} while (0)
90
Eduardo Valentin6ab52402013-03-15 09:00:06 -040091/*** Basic helper functions ***/
92
Eduardo Valentin7a556e62013-03-15 08:59:58 -040093/**
94 * omap_bandgap_power() - controls the power state of a bandgap device
95 * @bg_ptr: pointer to omap_bandgap structure
96 * @on: desired power state (1 - on, 0 - off)
97 *
98 * Used to power on/off a bandgap device instance. Only used on those
99 * that features tempsoff bit.
100 */
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300101static int omap_bandgap_power(struct omap_bandgap *bg_ptr, bool on)
102{
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300103 int i;
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300104
105 if (!OMAP_BANDGAP_HAS(bg_ptr, POWER_SWITCH))
Eduardo Valentin3d84e522013-03-15 08:59:57 -0400106 goto exit;
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300107
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400108 for (i = 0; i < bg_ptr->conf->sensor_count; i++)
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300109 /* active on 0 */
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400110 RMW_BITS(bg_ptr, i, temp_sensor_ctrl, bgap_tempsoff_mask, !on);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300111
Eduardo Valentin3d84e522013-03-15 08:59:57 -0400112exit:
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300113 return 0;
114}
115
Eduardo Valentin4a6554e2013-03-15 08:59:59 -0400116/**
117 * omap_bandgap_read_temp() - helper function to read sensor temperature
118 * @bg_ptr: pointer to omap_bandgap structure
119 * @id: bandgap sensor id
120 *
121 * Function to concentrate the steps to read sensor temperature register.
122 * This function is desired because, depending on bandgap device version,
123 * it might be needed to freeze the bandgap state machine, before fetching
124 * the register value.
125 */
Eduardo Valentin194a54f2013-02-26 18:53:33 -0400126static u32 omap_bandgap_read_temp(struct omap_bandgap *bg_ptr, int id)
127{
128 struct temp_sensor_registers *tsr;
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400129 u32 temp, reg;
Eduardo Valentin194a54f2013-02-26 18:53:33 -0400130
131 tsr = bg_ptr->conf->sensors[id].registers;
132 reg = tsr->temp_sensor_ctrl;
133
134 if (OMAP_BANDGAP_HAS(bg_ptr, FREEZE_BIT)) {
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400135 RMW_BITS(bg_ptr, id, bgap_mask_ctrl, mask_freeze_mask, 1);
Eduardo Valentin194a54f2013-02-26 18:53:33 -0400136 /*
137 * In case we cannot read from cur_dtemp / dtemp_0,
138 * then we read from the last valid temp read
139 */
140 reg = tsr->ctrl_dtemp_1;
141 }
142
143 /* read temperature */
144 temp = omap_bandgap_readl(bg_ptr, reg);
145 temp &= tsr->bgap_dtemp_mask;
146
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400147 if (OMAP_BANDGAP_HAS(bg_ptr, FREEZE_BIT))
148 RMW_BITS(bg_ptr, id, bgap_mask_ctrl, mask_freeze_mask, 0);
Eduardo Valentin194a54f2013-02-26 18:53:33 -0400149
150 return temp;
151}
152
Eduardo Valentinee07d552013-03-15 09:00:01 -0400153/**
154 * omap_bandgap_talert_irq_handler() - handles Temperature alert IRQs
155 * @irq: IRQ number
156 * @data: private data (struct omap_bandgap *)
157 *
158 * This is the Talert handler. Use it only if bandgap device features
159 * HAS(TALERT). This handler goes over all sensors and checks their
160 * conditions and acts accordingly. In case there are events pending,
161 * it will reset the event mask to wait for the opposite event (next event).
162 * Every time there is a new event, it will be reported to thermal layer.
163 */
Eduardo Valentinf2304272013-03-15 09:00:00 -0400164static irqreturn_t omap_bandgap_talert_irq_handler(int irq, void *data)
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300165{
166 struct omap_bandgap *bg_ptr = data;
167 struct temp_sensor_registers *tsr;
Eduardo Valentin194a54f2013-02-26 18:53:33 -0400168 u32 t_hot = 0, t_cold = 0, ctrl;
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300169 int i;
170
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300171 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
172 tsr = bg_ptr->conf->sensors[i].registers;
Eduardo Valentine555c952013-03-15 09:00:04 -0400173 ctrl = omap_bandgap_readl(bg_ptr, tsr->bgap_status);
174
175 /* Read the status of t_hot */
176 t_hot = ctrl & tsr->status_hot_mask;
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300177
178 /* Read the status of t_cold */
Eduardo Valentine555c952013-03-15 09:00:04 -0400179 t_cold = ctrl & tsr->status_cold_mask;
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300180
181 if (!t_cold && !t_hot)
182 continue;
183
184 ctrl = omap_bandgap_readl(bg_ptr, tsr->bgap_mask_ctrl);
185 /*
186 * One TALERT interrupt: Two sources
187 * If the interrupt is due to t_hot then mask t_hot and
188 * and unmask t_cold else mask t_cold and unmask t_hot
189 */
190 if (t_hot) {
191 ctrl &= ~tsr->mask_hot_mask;
192 ctrl |= tsr->mask_cold_mask;
193 } else if (t_cold) {
194 ctrl &= ~tsr->mask_cold_mask;
195 ctrl |= tsr->mask_hot_mask;
196 }
197
198 omap_bandgap_writel(bg_ptr, ctrl, tsr->bgap_mask_ctrl);
199
Eduardo Valentin71e303f2012-11-13 14:10:03 -0400200 dev_dbg(bg_ptr->dev,
201 "%s: IRQ from %s sensor: hotevent %d coldevent %d\n",
202 __func__, bg_ptr->conf->sensors[i].domain,
203 t_hot, t_cold);
204
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300205 /* report temperature to whom may concern */
206 if (bg_ptr->conf->report_temperature)
207 bg_ptr->conf->report_temperature(bg_ptr, i);
208 }
209
210 return IRQ_HANDLED;
211}
212
Eduardo Valentin79857cd22013-03-15 09:00:02 -0400213/**
214 * omap_bandgap_tshut_irq_handler() - handles Temperature shutdown signal
215 * @irq: IRQ number
216 * @data: private data (unused)
217 *
218 * This is the Tshut handler. Use it only if bandgap device features
219 * HAS(TSHUT). If any sensor fires the Tshut signal, we simply shutdown
220 * the system.
221 */
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300222static irqreturn_t omap_bandgap_tshut_irq_handler(int irq, void *data)
223{
Ruslan Ruslichenkob3bf0e92013-02-26 18:53:24 -0400224 pr_emerg("%s: TSHUT temperature reached. Needs shut down...\n",
225 __func__);
226
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300227 orderly_poweroff(true);
228
229 return IRQ_HANDLED;
230}
231
232static
233int adc_to_temp_conversion(struct omap_bandgap *bg_ptr, int id, int adc_val,
234 int *t)
235{
236 struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[id].ts_data;
237
238 /* look up for temperature in the table and return the temperature */
239 if (adc_val < ts_data->adc_start_val || adc_val > ts_data->adc_end_val)
240 return -ERANGE;
241
Eduardo Valentinc8a8f842013-02-26 18:53:36 -0400242 *t = bg_ptr->conf->conv_table[adc_val - ts_data->adc_start_val];
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300243
244 return 0;
245}
246
247static int temp_to_adc_conversion(long temp, struct omap_bandgap *bg_ptr, int i,
248 int *adc)
249{
250 struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[i].ts_data;
Eduardo Valentinc8a8f842013-02-26 18:53:36 -0400251 const int *conv_table = bg_ptr->conf->conv_table;
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300252 int high, low, mid;
253
254 low = 0;
255 high = ts_data->adc_end_val - ts_data->adc_start_val;
256 mid = (high + low) / 2;
257
Eduardo Valentinc8a8f842013-02-26 18:53:36 -0400258 if (temp < conv_table[low] || temp > conv_table[high])
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300259 return -EINVAL;
260
261 while (low < high) {
Eduardo Valentinc8a8f842013-02-26 18:53:36 -0400262 if (temp < conv_table[mid])
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300263 high = mid - 1;
264 else
265 low = mid + 1;
266 mid = (low + high) / 2;
267 }
268
269 *adc = ts_data->adc_start_val + low;
270
271 return 0;
272}
273
274/* Talert masks. Call it only if HAS(TALERT) is set */
275static int temp_sensor_unmask_interrupts(struct omap_bandgap *bg_ptr, int id,
276 u32 t_hot, u32 t_cold)
277{
278 struct temp_sensor_registers *tsr;
279 u32 temp, reg_val;
280
281 /* Read the current on die temperature */
Eduardo Valentin194a54f2013-02-26 18:53:33 -0400282 temp = omap_bandgap_read_temp(bg_ptr, id);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300283
Eduardo Valentin194a54f2013-02-26 18:53:33 -0400284 tsr = bg_ptr->conf->sensors[id].registers;
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300285 reg_val = omap_bandgap_readl(bg_ptr, tsr->bgap_mask_ctrl);
Eduardo Valentin194a54f2013-02-26 18:53:33 -0400286
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300287 if (temp < t_hot)
288 reg_val |= tsr->mask_hot_mask;
289 else
290 reg_val &= ~tsr->mask_hot_mask;
291
292 if (t_cold < temp)
293 reg_val |= tsr->mask_cold_mask;
294 else
295 reg_val &= ~tsr->mask_cold_mask;
296 omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_mask_ctrl);
297
298 return 0;
299}
300
301static
302int add_hyst(int adc_val, int hyst_val, struct omap_bandgap *bg_ptr, int i,
303 u32 *sum)
304{
305 int temp, ret;
306
307 ret = adc_to_temp_conversion(bg_ptr, i, adc_val, &temp);
308 if (ret < 0)
309 return ret;
310
311 temp += hyst_val;
312
313 return temp_to_adc_conversion(temp, bg_ptr, i, sum);
314}
315
316/* Talert Thot threshold. Call it only if HAS(TALERT) is set */
317static
318int temp_sensor_configure_thot(struct omap_bandgap *bg_ptr, int id, int t_hot)
319{
320 struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[id].ts_data;
321 struct temp_sensor_registers *tsr;
322 u32 thresh_val, reg_val;
323 int cold, err = 0;
324
325 tsr = bg_ptr->conf->sensors[id].registers;
326
327 /* obtain the T cold value */
328 thresh_val = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
329 cold = (thresh_val & tsr->threshold_tcold_mask) >>
330 __ffs(tsr->threshold_tcold_mask);
331 if (t_hot <= cold) {
332 /* change the t_cold to t_hot - 5000 millidegrees */
333 err |= add_hyst(t_hot, -ts_data->hyst_val, bg_ptr, id, &cold);
334 /* write the new t_cold value */
335 reg_val = thresh_val & (~tsr->threshold_tcold_mask);
336 reg_val |= cold << __ffs(tsr->threshold_tcold_mask);
337 omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
338 thresh_val = reg_val;
339 }
340
341 /* write the new t_hot value */
342 reg_val = thresh_val & ~tsr->threshold_thot_mask;
343 reg_val |= (t_hot << __ffs(tsr->threshold_thot_mask));
344 omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
345 if (err) {
346 dev_err(bg_ptr->dev, "failed to reprogram thot threshold\n");
347 return -EIO;
348 }
349
350 return temp_sensor_unmask_interrupts(bg_ptr, id, t_hot, cold);
351}
352
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300353/* Talert Tcold threshold. Call it only if HAS(TALERT) is set */
354static
355int temp_sensor_configure_tcold(struct omap_bandgap *bg_ptr, int id,
356 int t_cold)
357{
358 struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[id].ts_data;
359 struct temp_sensor_registers *tsr;
360 u32 thresh_val, reg_val;
361 int hot, err = 0;
362
363 tsr = bg_ptr->conf->sensors[id].registers;
364 /* obtain the T cold value */
365 thresh_val = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
366 hot = (thresh_val & tsr->threshold_thot_mask) >>
367 __ffs(tsr->threshold_thot_mask);
368
369 if (t_cold >= hot) {
370 /* change the t_hot to t_cold + 5000 millidegrees */
371 err |= add_hyst(t_cold, ts_data->hyst_val, bg_ptr, id, &hot);
372 /* write the new t_hot value */
373 reg_val = thresh_val & (~tsr->threshold_thot_mask);
374 reg_val |= hot << __ffs(tsr->threshold_thot_mask);
375 omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
376 thresh_val = reg_val;
377 }
378
379 /* write the new t_cold value */
380 reg_val = thresh_val & ~tsr->threshold_tcold_mask;
381 reg_val |= (t_cold << __ffs(tsr->threshold_tcold_mask));
382 omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
383 if (err) {
384 dev_err(bg_ptr->dev, "failed to reprogram tcold threshold\n");
385 return -EIO;
386 }
387
388 return temp_sensor_unmask_interrupts(bg_ptr, id, hot, t_cold);
389}
390
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300391#define bandgap_is_valid(b) \
392 (!IS_ERR_OR_NULL(b))
393#define bandgap_is_valid_sensor_id(b, i) \
394 ((i) >= 0 && (i) < (b)->conf->sensor_count)
395static inline int omap_bandgap_validate(struct omap_bandgap *bg_ptr, int id)
396{
397 if (!bandgap_is_valid(bg_ptr)) {
398 pr_err("%s: invalid bandgap pointer\n", __func__);
399 return -EINVAL;
400 }
401
402 if (!bandgap_is_valid_sensor_id(bg_ptr, id)) {
403 dev_err(bg_ptr->dev, "%s: sensor id out of range (%d)\n",
404 __func__, id);
405 return -ERANGE;
406 }
407
408 return 0;
409}
410
411/* Exposed APIs */
412/**
413 * omap_bandgap_read_thot() - reads sensor current thot
414 * @bg_ptr - pointer to bandgap instance
415 * @id - sensor id
416 * @thot - resulting current thot value
417 *
418 * returns 0 on success or the proper error code
419 */
420int omap_bandgap_read_thot(struct omap_bandgap *bg_ptr, int id,
Eduardo Valentin24796e12013-03-15 08:59:53 -0400421 int *thot)
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300422{
423 struct temp_sensor_registers *tsr;
424 u32 temp;
425 int ret;
426
427 ret = omap_bandgap_validate(bg_ptr, id);
428 if (ret)
429 return ret;
430
431 if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT))
432 return -ENOTSUPP;
433
434 tsr = bg_ptr->conf->sensors[id].registers;
435 temp = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
436 temp = (temp & tsr->threshold_thot_mask) >>
437 __ffs(tsr->threshold_thot_mask);
438 ret |= adc_to_temp_conversion(bg_ptr, id, temp, &temp);
439 if (ret) {
440 dev_err(bg_ptr->dev, "failed to read thot\n");
441 return -EIO;
442 }
443
444 *thot = temp;
445
446 return 0;
447}
448
449/**
450 * omap_bandgap_write_thot() - sets sensor current thot
451 * @bg_ptr - pointer to bandgap instance
452 * @id - sensor id
453 * @val - desired thot value
454 *
455 * returns 0 on success or the proper error code
456 */
457int omap_bandgap_write_thot(struct omap_bandgap *bg_ptr, int id, int val)
458{
459 struct temp_sensor_data *ts_data;
460 struct temp_sensor_registers *tsr;
461 u32 t_hot;
462 int ret;
463
464 ret = omap_bandgap_validate(bg_ptr, id);
465 if (ret)
466 return ret;
467
468 if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT))
469 return -ENOTSUPP;
470
471 ts_data = bg_ptr->conf->sensors[id].ts_data;
472 tsr = bg_ptr->conf->sensors[id].registers;
473
474 if (val < ts_data->min_temp + ts_data->hyst_val)
475 return -EINVAL;
476 ret = temp_to_adc_conversion(val, bg_ptr, id, &t_hot);
477 if (ret < 0)
478 return ret;
479
480 mutex_lock(&bg_ptr->bg_mutex);
481 temp_sensor_configure_thot(bg_ptr, id, t_hot);
482 mutex_unlock(&bg_ptr->bg_mutex);
483
484 return 0;
485}
486
487/**
488 * omap_bandgap_read_tcold() - reads sensor current tcold
489 * @bg_ptr - pointer to bandgap instance
490 * @id - sensor id
491 * @tcold - resulting current tcold value
492 *
493 * returns 0 on success or the proper error code
494 */
495int omap_bandgap_read_tcold(struct omap_bandgap *bg_ptr, int id,
Eduardo Valentin24796e12013-03-15 08:59:53 -0400496 int *tcold)
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300497{
498 struct temp_sensor_registers *tsr;
499 u32 temp;
500 int ret;
501
502 ret = omap_bandgap_validate(bg_ptr, id);
503 if (ret)
504 return ret;
505
506 if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT))
507 return -ENOTSUPP;
508
509 tsr = bg_ptr->conf->sensors[id].registers;
510 temp = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
511 temp = (temp & tsr->threshold_tcold_mask)
512 >> __ffs(tsr->threshold_tcold_mask);
513 ret |= adc_to_temp_conversion(bg_ptr, id, temp, &temp);
514 if (ret)
515 return -EIO;
516
517 *tcold = temp;
518
519 return 0;
520}
521
522/**
523 * omap_bandgap_write_tcold() - sets the sensor tcold
524 * @bg_ptr - pointer to bandgap instance
525 * @id - sensor id
526 * @val - desired tcold value
527 *
528 * returns 0 on success or the proper error code
529 */
530int omap_bandgap_write_tcold(struct omap_bandgap *bg_ptr, int id, int val)
531{
532 struct temp_sensor_data *ts_data;
533 struct temp_sensor_registers *tsr;
534 u32 t_cold;
535 int ret;
536
537 ret = omap_bandgap_validate(bg_ptr, id);
538 if (ret)
539 return ret;
540
541 if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT))
542 return -ENOTSUPP;
543
544 ts_data = bg_ptr->conf->sensors[id].ts_data;
545 tsr = bg_ptr->conf->sensors[id].registers;
546 if (val > ts_data->max_temp + ts_data->hyst_val)
547 return -EINVAL;
548
549 ret = temp_to_adc_conversion(val, bg_ptr, id, &t_cold);
550 if (ret < 0)
551 return ret;
552
553 mutex_lock(&bg_ptr->bg_mutex);
554 temp_sensor_configure_tcold(bg_ptr, id, t_cold);
555 mutex_unlock(&bg_ptr->bg_mutex);
556
557 return 0;
558}
559
560/**
561 * omap_bandgap_read_update_interval() - read the sensor update interval
562 * @bg_ptr - pointer to bandgap instance
563 * @id - sensor id
564 * @interval - resulting update interval in miliseconds
565 *
566 * returns 0 on success or the proper error code
567 */
568int omap_bandgap_read_update_interval(struct omap_bandgap *bg_ptr, int id,
569 int *interval)
570{
571 struct temp_sensor_registers *tsr;
572 u32 time;
573 int ret;
574
575 ret = omap_bandgap_validate(bg_ptr, id);
576 if (ret)
577 return ret;
578
579 if (!OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
580 return -ENOTSUPP;
581
582 tsr = bg_ptr->conf->sensors[id].registers;
583 time = omap_bandgap_readl(bg_ptr, tsr->bgap_counter);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300584 time = (time & tsr->counter_mask) >> __ffs(tsr->counter_mask);
585 time = time * 1000 / bg_ptr->clk_rate;
586
587 *interval = time;
588
589 return 0;
590}
591
592/**
593 * omap_bandgap_write_update_interval() - set the update interval
594 * @bg_ptr - pointer to bandgap instance
595 * @id - sensor id
596 * @interval - desired update interval in miliseconds
597 *
598 * returns 0 on success or the proper error code
599 */
600int omap_bandgap_write_update_interval(struct omap_bandgap *bg_ptr,
Eduardo Valentin24796e12013-03-15 08:59:53 -0400601 int id, u32 interval)
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300602{
603 int ret = omap_bandgap_validate(bg_ptr, id);
604 if (ret)
605 return ret;
606
607 if (!OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
608 return -ENOTSUPP;
609
610 interval = interval * bg_ptr->clk_rate / 1000;
611 mutex_lock(&bg_ptr->bg_mutex);
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400612 RMW_BITS(bg_ptr, id, bgap_counter, counter_mask, interval);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300613 mutex_unlock(&bg_ptr->bg_mutex);
614
615 return 0;
616}
617
618/**
619 * omap_bandgap_read_temperature() - report current temperature
620 * @bg_ptr - pointer to bandgap instance
621 * @id - sensor id
622 * @temperature - resulting temperature
623 *
624 * returns 0 on success or the proper error code
625 */
626int omap_bandgap_read_temperature(struct omap_bandgap *bg_ptr, int id,
Eduardo Valentin24796e12013-03-15 08:59:53 -0400627 int *temperature)
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300628{
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300629 u32 temp;
630 int ret;
631
632 ret = omap_bandgap_validate(bg_ptr, id);
633 if (ret)
634 return ret;
635
Eduardo Valentin194a54f2013-02-26 18:53:33 -0400636 mutex_lock(&bg_ptr->bg_mutex);
637 temp = omap_bandgap_read_temp(bg_ptr, id);
638 mutex_unlock(&bg_ptr->bg_mutex);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300639
640 ret |= adc_to_temp_conversion(bg_ptr, id, temp, &temp);
641 if (ret)
642 return -EIO;
643
644 *temperature = temp;
645
646 return 0;
647}
648
649/**
650 * omap_bandgap_set_sensor_data() - helper function to store thermal
651 * framework related data.
652 * @bg_ptr - pointer to bandgap instance
653 * @id - sensor id
654 * @data - thermal framework related data to be stored
655 *
656 * returns 0 on success or the proper error code
657 */
658int omap_bandgap_set_sensor_data(struct omap_bandgap *bg_ptr, int id,
Eduardo Valentin24796e12013-03-15 08:59:53 -0400659 void *data)
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300660{
661 int ret = omap_bandgap_validate(bg_ptr, id);
662 if (ret)
663 return ret;
664
665 bg_ptr->conf->sensors[id].data = data;
666
667 return 0;
668}
669
670/**
671 * omap_bandgap_get_sensor_data() - helper function to get thermal
672 * framework related data.
673 * @bg_ptr - pointer to bandgap instance
674 * @id - sensor id
675 *
676 * returns data stored by set function with sensor id on success or NULL
677 */
678void *omap_bandgap_get_sensor_data(struct omap_bandgap *bg_ptr, int id)
679{
680 int ret = omap_bandgap_validate(bg_ptr, id);
681 if (ret)
682 return ERR_PTR(ret);
683
684 return bg_ptr->conf->sensors[id].data;
685}
686
687static int
688omap_bandgap_force_single_read(struct omap_bandgap *bg_ptr, int id)
689{
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300690 u32 temp = 0, counter = 1000;
691
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300692 /* Select single conversion mode */
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400693 if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG))
694 RMW_BITS(bg_ptr, id, bgap_mode_ctrl, mode_ctrl_mask, 0);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300695
696 /* Start of Conversion = 1 */
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400697 RMW_BITS(bg_ptr, id, temp_sensor_ctrl, bgap_soc_mask, 1);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300698 /* Wait until DTEMP is updated */
Eduardo Valentin194a54f2013-02-26 18:53:33 -0400699 temp = omap_bandgap_read_temp(bg_ptr, id);
700
701 while ((temp == 0) && --counter)
702 temp = omap_bandgap_read_temp(bg_ptr, id);
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400703 /* REVISIT: Check correct condition for end of conversion */
Eduardo Valentin194a54f2013-02-26 18:53:33 -0400704
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300705 /* Start of Conversion = 0 */
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400706 RMW_BITS(bg_ptr, id, temp_sensor_ctrl, bgap_soc_mask, 0);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300707
708 return 0;
709}
710
711/**
712 * enable_continuous_mode() - One time enabling of continuous conversion mode
713 * @bg_ptr - pointer to scm instance
714 *
715 * Call this function only if HAS(MODE_CONFIG) is set
716 */
717static int enable_continuous_mode(struct omap_bandgap *bg_ptr)
718{
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300719 int i;
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300720
721 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
722 /* Perform a single read just before enabling continuous */
723 omap_bandgap_force_single_read(bg_ptr, i);
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400724 RMW_BITS(bg_ptr, i, bgap_mode_ctrl, mode_ctrl_mask, 1);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300725 }
726
727 return 0;
728}
729
730static int omap_bandgap_tshut_init(struct omap_bandgap *bg_ptr,
Eduardo Valentin24796e12013-03-15 08:59:53 -0400731 struct platform_device *pdev)
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300732{
733 int gpio_nr = bg_ptr->tshut_gpio;
734 int status;
735
736 /* Request for gpio_86 line */
737 status = gpio_request(gpio_nr, "tshut");
738 if (status < 0) {
739 dev_err(bg_ptr->dev,
740 "Could not request for TSHUT GPIO:%i\n", 86);
741 return status;
742 }
743 status = gpio_direction_input(gpio_nr);
744 if (status) {
745 dev_err(bg_ptr->dev,
746 "Cannot set input TSHUT GPIO %d\n", gpio_nr);
747 return status;
748 }
749
750 status = request_irq(gpio_to_irq(gpio_nr),
751 omap_bandgap_tshut_irq_handler,
752 IRQF_TRIGGER_RISING, "tshut",
753 NULL);
754 if (status) {
755 gpio_free(gpio_nr);
756 dev_err(bg_ptr->dev, "request irq failed for TSHUT");
757 }
758
759 return 0;
760}
761
762/* Initialization of Talert. Call it only if HAS(TALERT) is set */
763static int omap_bandgap_talert_init(struct omap_bandgap *bg_ptr,
Eduardo Valentin24796e12013-03-15 08:59:53 -0400764 struct platform_device *pdev)
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300765{
766 int ret;
767
768 bg_ptr->irq = platform_get_irq(pdev, 0);
769 if (bg_ptr->irq < 0) {
770 dev_err(&pdev->dev, "get_irq failed\n");
771 return bg_ptr->irq;
772 }
773 ret = request_threaded_irq(bg_ptr->irq, NULL,
Eduardo Valentinf2304272013-03-15 09:00:00 -0400774 omap_bandgap_talert_irq_handler,
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300775 IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
776 "talert", bg_ptr);
777 if (ret) {
778 dev_err(&pdev->dev, "Request threaded irq failed.\n");
779 return ret;
780 }
781
782 return 0;
783}
784
785static const struct of_device_id of_omap_bandgap_match[];
786static struct omap_bandgap *omap_bandgap_build(struct platform_device *pdev)
787{
788 struct device_node *node = pdev->dev.of_node;
789 const struct of_device_id *of_id;
790 struct omap_bandgap *bg_ptr;
791 struct resource *res;
792 u32 prop;
793 int i;
794
795 /* just for the sake */
796 if (!node) {
797 dev_err(&pdev->dev, "no platform information available\n");
798 return ERR_PTR(-EINVAL);
799 }
800
801 bg_ptr = devm_kzalloc(&pdev->dev, sizeof(struct omap_bandgap),
802 GFP_KERNEL);
803 if (!bg_ptr) {
804 dev_err(&pdev->dev, "Unable to allocate mem for driver ref\n");
805 return ERR_PTR(-ENOMEM);
806 }
807
808 of_id = of_match_device(of_omap_bandgap_match, &pdev->dev);
809 if (of_id)
810 bg_ptr->conf = of_id->data;
811
812 i = 0;
813 do {
814 void __iomem *chunk;
815
816 res = platform_get_resource(pdev, IORESOURCE_MEM, i);
817 if (!res)
818 break;
Thierry Reding97f4be602013-01-21 11:09:19 +0100819 chunk = devm_ioremap_resource(&pdev->dev, res);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300820 if (i == 0)
821 bg_ptr->base = chunk;
Thierry Reding97f4be602013-01-21 11:09:19 +0100822 if (IS_ERR(chunk))
823 return ERR_CAST(chunk);
Eduardo Valentin24796e12013-03-15 08:59:53 -0400824
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300825 i++;
826 } while (res);
827
828 if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) {
829 if (of_property_read_u32(node, "ti,tshut-gpio", &prop) < 0) {
830 dev_err(&pdev->dev, "missing tshut gpio in device tree\n");
831 return ERR_PTR(-EINVAL);
832 }
833 bg_ptr->tshut_gpio = prop;
834 if (!gpio_is_valid(bg_ptr->tshut_gpio)) {
835 dev_err(&pdev->dev, "invalid gpio for tshut (%d)\n",
836 bg_ptr->tshut_gpio);
837 return ERR_PTR(-EINVAL);
838 }
839 }
840
841 return bg_ptr;
842}
843
844static
Bill Pembertondb53ac712012-11-19 13:22:13 -0500845int omap_bandgap_probe(struct platform_device *pdev)
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300846{
847 struct omap_bandgap *bg_ptr;
848 int clk_rate, ret = 0, i;
849
850 bg_ptr = omap_bandgap_build(pdev);
851 if (IS_ERR_OR_NULL(bg_ptr)) {
852 dev_err(&pdev->dev, "failed to fetch platform data\n");
853 return PTR_ERR(bg_ptr);
854 }
855 bg_ptr->dev = &pdev->dev;
856
857 if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) {
858 ret = omap_bandgap_tshut_init(bg_ptr, pdev);
859 if (ret) {
860 dev_err(&pdev->dev,
861 "failed to initialize system tshut IRQ\n");
862 return ret;
863 }
864 }
865
866 bg_ptr->fclock = clk_get(NULL, bg_ptr->conf->fclock_name);
867 ret = IS_ERR_OR_NULL(bg_ptr->fclock);
868 if (ret) {
869 dev_err(&pdev->dev, "failed to request fclock reference\n");
870 goto free_irqs;
871 }
872
873 bg_ptr->div_clk = clk_get(NULL, bg_ptr->conf->div_ck_name);
874 ret = IS_ERR_OR_NULL(bg_ptr->div_clk);
875 if (ret) {
876 dev_err(&pdev->dev,
877 "failed to request div_ts_ck clock ref\n");
878 goto free_irqs;
879 }
880
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300881 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
882 struct temp_sensor_registers *tsr;
883 u32 val;
884
885 tsr = bg_ptr->conf->sensors[i].registers;
886 /*
887 * check if the efuse has a non-zero value if not
888 * it is an untrimmed sample and the temperatures
889 * may not be accurate
890 */
891 val = omap_bandgap_readl(bg_ptr, tsr->bgap_efuse);
892 if (ret || !val)
893 dev_info(&pdev->dev,
894 "Non-trimmed BGAP, Temp not accurate\n");
895 }
896
897 clk_rate = clk_round_rate(bg_ptr->div_clk,
898 bg_ptr->conf->sensors[0].ts_data->max_freq);
899 if (clk_rate < bg_ptr->conf->sensors[0].ts_data->min_freq ||
900 clk_rate == 0xffffffff) {
901 ret = -ENODEV;
902 dev_err(&pdev->dev, "wrong clock rate (%d)\n", clk_rate);
903 goto put_clks;
904 }
905
906 ret = clk_set_rate(bg_ptr->div_clk, clk_rate);
907 if (ret)
908 dev_err(&pdev->dev, "Cannot re-set clock rate. Continuing\n");
909
910 bg_ptr->clk_rate = clk_rate;
Radhesh Fadnis6c9c1d62013-02-26 18:53:25 -0400911 if (OMAP_BANDGAP_HAS(bg_ptr, CLK_CTRL))
Eduardo Valentinf1d07f32013-02-26 18:53:38 -0400912 clk_prepare_enable(bg_ptr->fclock);
Radhesh Fadnis6c9c1d62013-02-26 18:53:25 -0400913
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300914
915 mutex_init(&bg_ptr->bg_mutex);
916 bg_ptr->dev = &pdev->dev;
917 platform_set_drvdata(pdev, bg_ptr);
918
919 omap_bandgap_power(bg_ptr, true);
920
921 /* Set default counter to 1 for now */
922 if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
923 for (i = 0; i < bg_ptr->conf->sensor_count; i++)
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400924 RMW_BITS(bg_ptr, i, bgap_counter, counter_mask, 1);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300925
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400926 /* Set default thresholds for alert and shutdown */
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300927 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
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400932 if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) {
933 /* Set initial Talert thresholds */
934 RMW_BITS(bg_ptr, i, bgap_threshold,
935 threshold_tcold_mask, ts_data->t_cold);
936 RMW_BITS(bg_ptr, i, bgap_threshold,
937 threshold_thot_mask, ts_data->t_hot);
938 /* Enable the alert events */
939 RMW_BITS(bg_ptr, i, bgap_mask_ctrl, mask_hot_mask, 1);
940 RMW_BITS(bg_ptr, i, bgap_mask_ctrl, mask_cold_mask, 1);
941 }
942
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300943 if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG)) {
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400944 /* Set initial Tshut thresholds */
945 RMW_BITS(bg_ptr, i, tshut_threshold,
946 tshut_hot_mask, ts_data->tshut_hot);
947 RMW_BITS(bg_ptr, i, tshut_threshold,
948 tshut_cold_mask, ts_data->tshut_cold);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300949 }
950 }
951
952 if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG))
953 enable_continuous_mode(bg_ptr);
954
955 /* Set .250 seconds time as default counter */
956 if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
957 for (i = 0; i < bg_ptr->conf->sensor_count; i++)
Eduardo Valentind3c291a2013-03-15 08:59:55 -0400958 RMW_BITS(bg_ptr, i, bgap_counter, counter_mask,
959 bg_ptr->clk_rate / 4);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300960
961 /* Every thing is good? Then expose the sensors */
962 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
963 char *domain;
964
Eduardo Valentin04a4d102012-09-11 19:06:55 +0300965 if (bg_ptr->conf->sensors[i].register_cooling)
966 bg_ptr->conf->sensors[i].register_cooling(bg_ptr, i);
967
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300968 domain = bg_ptr->conf->sensors[i].domain;
969 if (bg_ptr->conf->expose_sensor)
970 bg_ptr->conf->expose_sensor(bg_ptr, i, domain);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300971 }
972
973 /*
974 * Enable the Interrupts once everything is set. Otherwise irq handler
975 * might be called as soon as it is enabled where as rest of framework
976 * is still getting initialised.
977 */
978 if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) {
979 ret = omap_bandgap_talert_init(bg_ptr, pdev);
980 if (ret) {
981 dev_err(&pdev->dev, "failed to initialize Talert IRQ\n");
982 i = bg_ptr->conf->sensor_count;
983 goto disable_clk;
984 }
985 }
986
987 return 0;
988
989disable_clk:
Radhesh Fadnis6c9c1d62013-02-26 18:53:25 -0400990 if (OMAP_BANDGAP_HAS(bg_ptr, CLK_CTRL))
Eduardo Valentinf1d07f32013-02-26 18:53:38 -0400991 clk_disable_unprepare(bg_ptr->fclock);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +0300992put_clks:
993 clk_put(bg_ptr->fclock);
994 clk_put(bg_ptr->div_clk);
995free_irqs:
996 if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) {
997 free_irq(gpio_to_irq(bg_ptr->tshut_gpio), NULL);
998 gpio_free(bg_ptr->tshut_gpio);
999 }
1000
1001 return ret;
1002}
1003
1004static
Bill Pemberton434bd032012-11-19 13:26:43 -05001005int omap_bandgap_remove(struct platform_device *pdev)
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +03001006{
1007 struct omap_bandgap *bg_ptr = platform_get_drvdata(pdev);
1008 int i;
1009
1010 /* First thing is to remove sensor interfaces */
1011 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
1012 if (bg_ptr->conf->sensors[i].register_cooling)
1013 bg_ptr->conf->sensors[i].unregister_cooling(bg_ptr, i);
1014
1015 if (bg_ptr->conf->remove_sensor)
1016 bg_ptr->conf->remove_sensor(bg_ptr, i);
1017 }
1018
1019 omap_bandgap_power(bg_ptr, false);
1020
Radhesh Fadnis6c9c1d62013-02-26 18:53:25 -04001021 if (OMAP_BANDGAP_HAS(bg_ptr, CLK_CTRL))
Eduardo Valentinf1d07f32013-02-26 18:53:38 -04001022 clk_disable_unprepare(bg_ptr->fclock);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +03001023 clk_put(bg_ptr->fclock);
1024 clk_put(bg_ptr->div_clk);
1025
1026 if (OMAP_BANDGAP_HAS(bg_ptr, TALERT))
1027 free_irq(bg_ptr->irq, bg_ptr);
1028
1029 if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) {
1030 free_irq(gpio_to_irq(bg_ptr->tshut_gpio), NULL);
1031 gpio_free(bg_ptr->tshut_gpio);
1032 }
1033
1034 return 0;
1035}
1036
1037#ifdef CONFIG_PM
1038static int omap_bandgap_save_ctxt(struct omap_bandgap *bg_ptr)
1039{
1040 int i;
1041
1042 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
1043 struct temp_sensor_registers *tsr;
1044 struct temp_sensor_regval *rval;
1045
1046 rval = &bg_ptr->conf->sensors[i].regval;
1047 tsr = bg_ptr->conf->sensors[i].registers;
1048
1049 if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG))
1050 rval->bg_mode_ctrl = omap_bandgap_readl(bg_ptr,
J Keerthy76d2cd32012-09-11 19:06:52 +03001051 tsr->bgap_mode_ctrl);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +03001052 if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
1053 rval->bg_counter = omap_bandgap_readl(bg_ptr,
J Keerthy76d2cd32012-09-11 19:06:52 +03001054 tsr->bgap_counter);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +03001055 if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) {
1056 rval->bg_threshold = omap_bandgap_readl(bg_ptr,
J Keerthy76d2cd32012-09-11 19:06:52 +03001057 tsr->bgap_threshold);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +03001058 rval->bg_ctrl = omap_bandgap_readl(bg_ptr,
J Keerthy76d2cd32012-09-11 19:06:52 +03001059 tsr->bgap_mask_ctrl);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +03001060 }
1061
1062 if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG))
1063 rval->tshut_threshold = omap_bandgap_readl(bg_ptr,
J Keerthy76d2cd32012-09-11 19:06:52 +03001064 tsr->tshut_threshold);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +03001065 }
1066
1067 return 0;
1068}
1069
1070static int omap_bandgap_restore_ctxt(struct omap_bandgap *bg_ptr)
1071{
1072 int i;
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +03001073
1074 for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
1075 struct temp_sensor_registers *tsr;
1076 struct temp_sensor_regval *rval;
1077 u32 val = 0;
1078
1079 rval = &bg_ptr->conf->sensors[i].regval;
1080 tsr = bg_ptr->conf->sensors[i].registers;
1081
1082 if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
1083 val = omap_bandgap_readl(bg_ptr, tsr->bgap_counter);
1084
Radhesh Fadnisb87ea752012-11-13 14:10:04 -04001085 if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG))
Eduardo Valentin24796e12013-03-15 08:59:53 -04001086 omap_bandgap_writel(bg_ptr, rval->tshut_threshold,
1087 tsr->tshut_threshold);
Radhesh Fadnisb87ea752012-11-13 14:10:04 -04001088 /* Force immediate temperature measurement and update
1089 * of the DTEMP field
1090 */
1091 omap_bandgap_force_single_read(bg_ptr, i);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +03001092
Radhesh Fadnisb87ea752012-11-13 14:10:04 -04001093 if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
1094 omap_bandgap_writel(bg_ptr, rval->bg_counter,
Eduardo Valentin24796e12013-03-15 08:59:53 -04001095 tsr->bgap_counter);
Radhesh Fadnisb87ea752012-11-13 14:10:04 -04001096 if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG))
1097 omap_bandgap_writel(bg_ptr, rval->bg_mode_ctrl,
Eduardo Valentin24796e12013-03-15 08:59:53 -04001098 tsr->bgap_mode_ctrl);
Radhesh Fadnisb87ea752012-11-13 14:10:04 -04001099 if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) {
Eduardo Valentin24796e12013-03-15 08:59:53 -04001100 omap_bandgap_writel(bg_ptr, rval->bg_threshold,
1101 tsr->bgap_threshold);
Radhesh Fadnisb87ea752012-11-13 14:10:04 -04001102 omap_bandgap_writel(bg_ptr, rval->bg_ctrl,
Eduardo Valentin24796e12013-03-15 08:59:53 -04001103 tsr->bgap_mask_ctrl);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +03001104 }
1105 }
1106
1107 return 0;
1108}
1109
1110static int omap_bandgap_suspend(struct device *dev)
1111{
1112 struct omap_bandgap *bg_ptr = dev_get_drvdata(dev);
1113 int err;
1114
1115 err = omap_bandgap_save_ctxt(bg_ptr);
1116 omap_bandgap_power(bg_ptr, false);
Radhesh Fadnis6c9c1d62013-02-26 18:53:25 -04001117
1118 if (OMAP_BANDGAP_HAS(bg_ptr, CLK_CTRL))
Eduardo Valentinf1d07f32013-02-26 18:53:38 -04001119 clk_disable_unprepare(bg_ptr->fclock);
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +03001120
1121 return err;
1122}
1123
1124static int omap_bandgap_resume(struct device *dev)
1125{
1126 struct omap_bandgap *bg_ptr = dev_get_drvdata(dev);
1127
Radhesh Fadnis6c9c1d62013-02-26 18:53:25 -04001128 if (OMAP_BANDGAP_HAS(bg_ptr, CLK_CTRL))
Eduardo Valentinf1d07f32013-02-26 18:53:38 -04001129 clk_prepare_enable(bg_ptr->fclock);
Radhesh Fadnis6c9c1d62013-02-26 18:53:25 -04001130
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +03001131 omap_bandgap_power(bg_ptr, true);
1132
1133 return omap_bandgap_restore_ctxt(bg_ptr);
1134}
1135static const struct dev_pm_ops omap_bandgap_dev_pm_ops = {
1136 SET_SYSTEM_SLEEP_PM_OPS(omap_bandgap_suspend,
1137 omap_bandgap_resume)
1138};
1139
1140#define DEV_PM_OPS (&omap_bandgap_dev_pm_ops)
1141#else
1142#define DEV_PM_OPS NULL
1143#endif
1144
1145static const struct of_device_id of_omap_bandgap_match[] = {
Eduardo Valentin1a312702012-07-12 19:02:31 +03001146#ifdef CONFIG_OMAP4_THERMAL
1147 {
1148 .compatible = "ti,omap4430-bandgap",
1149 .data = (void *)&omap4430_data,
1150 },
1151 {
1152 .compatible = "ti,omap4460-bandgap",
1153 .data = (void *)&omap4460_data,
1154 },
1155 {
1156 .compatible = "ti,omap4470-bandgap",
1157 .data = (void *)&omap4470_data,
1158 },
1159#endif
Eduardo Valentin949f5a52012-07-12 19:02:32 +03001160#ifdef CONFIG_OMAP5_THERMAL
1161 {
1162 .compatible = "ti,omap5430-bandgap",
1163 .data = (void *)&omap5430_data,
1164 },
1165#endif
Eduardo Valentin8feaf0ce2012-07-12 19:02:29 +03001166 /* Sentinel */
1167 { },
1168};
1169MODULE_DEVICE_TABLE(of, of_omap_bandgap_match);
1170
1171static struct platform_driver omap_bandgap_sensor_driver = {
1172 .probe = omap_bandgap_probe,
1173 .remove = omap_bandgap_remove,
1174 .driver = {
1175 .name = "omap-bandgap",
1176 .pm = DEV_PM_OPS,
1177 .of_match_table = of_omap_bandgap_match,
1178 },
1179};
1180
1181module_platform_driver(omap_bandgap_sensor_driver);
1182
1183MODULE_DESCRIPTION("OMAP4+ bandgap temperature sensor driver");
1184MODULE_LICENSE("GPL v2");
1185MODULE_ALIAS("platform:omap-bandgap");
1186MODULE_AUTHOR("Texas Instrument Inc.");