blob: d50eb5770e754548847f71c6a70534aca53747a4 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
David Collins197c31e2012-09-10 13:15:17 -07002 * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14/*
15 * Qualcomm PMIC PM8xxx Thermal Manager driver
16 */
17
18#define pr_fmt(fmt) "%s: " fmt, __func__
19
20#include <linux/module.h>
21#include <linux/err.h>
22#include <linux/string.h>
23#include <linux/kernel.h>
24#include <linux/slab.h>
25#include <linux/mutex.h>
26#include <linux/thermal.h>
27#include <linux/interrupt.h>
28#include <linux/platform_device.h>
29#include <linux/mfd/pm8xxx/core.h>
30#include <linux/mfd/pm8xxx/tm.h>
31#include <linux/completion.h>
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -070032#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
Anirudh Ghayalc2019332011-11-12 06:29:10 +053033#include <linux/msm_adc.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070034
35/* Register TEMP_ALARM_CTRL bits */
36#define TEMP_ALARM_CTRL_ST3_SD 0x80
37#define TEMP_ALARM_CTRL_ST2_SD 0x40
38#define TEMP_ALARM_CTRL_STATUS_MASK 0x30
39#define TEMP_ALARM_CTRL_STATUS_SHIFT 4
40#define TEMP_ALARM_CTRL_THRESH_MASK 0x0C
41#define TEMP_ALARM_CTRL_THRESH_SHIFT 2
42#define TEMP_ALARM_CTRL_OVRD_ST3 0x02
43#define TEMP_ALARM_CTRL_OVRD_ST2 0x01
44#define TEMP_ALARM_CTRL_OVRD_MASK 0x03
45
46#define TEMP_STAGE_STEP 20000 /* Stage step: 20.000 C */
47#define TEMP_STAGE_HYSTERESIS 2000
48
49#define TEMP_THRESH_MIN 105000 /* Threshold Min: 105 C */
50#define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
51
52/* Register TEMP_ALARM_PWM bits */
53#define TEMP_ALARM_PWM_EN_MASK 0xC0
54#define TEMP_ALARM_PWM_EN_SHIFT 6
55#define TEMP_ALARM_PWM_PER_PRE_MASK 0x38
56#define TEMP_ALARM_PWM_PER_PRE_SHIFT 3
57#define TEMP_ALARM_PWM_PER_DIV_MASK 0x07
58#define TEMP_ALARM_PWM_PER_DIV_SHIFT 0
59
60/* Trips: from critical to less critical */
61#define TRIP_STAGE3 0
62#define TRIP_STAGE2 1
63#define TRIP_STAGE1 2
64#define TRIP_NUM 3
65
66struct pm8xxx_tm_chip {
67 struct pm8xxx_tm_core_data cdata;
68 struct work_struct irq_work;
69 struct device *dev;
70 struct thermal_zone_device *tz_dev;
71 unsigned long temp;
72 enum thermal_device_mode mode;
73 unsigned int thresh;
74 unsigned int stage;
75 unsigned int tempstat_irq;
76 unsigned int overtemp_irq;
77 void *adc_handle;
78};
79
80enum pmic_thermal_override_mode {
81 SOFTWARE_OVERRIDE_DISABLED = 0,
82 SOFTWARE_OVERRIDE_ENABLED,
83};
84
85static inline int pm8xxx_tm_read_ctrl(struct pm8xxx_tm_chip *chip, u8 *reg)
86{
87 int rc;
88
89 rc = pm8xxx_readb(chip->dev->parent,
90 chip->cdata.reg_addr_temp_alarm_ctrl, reg);
91 if (rc)
92 pr_err("%s: pm8xxx_readb(0x%03X) failed, rc=%d\n",
93 chip->cdata.tm_name,
94 chip->cdata.reg_addr_temp_alarm_ctrl, rc);
95
96 return rc;
97}
98
99static inline int pm8xxx_tm_write_ctrl(struct pm8xxx_tm_chip *chip, u8 reg)
100{
101 int rc;
102
103 rc = pm8xxx_writeb(chip->dev->parent,
104 chip->cdata.reg_addr_temp_alarm_ctrl, reg);
105 if (rc)
106 pr_err("%s: pm8xxx_writeb(0x%03X)=0x%02X failed, rc=%d\n",
107 chip->cdata.tm_name,
108 chip->cdata.reg_addr_temp_alarm_ctrl, reg, rc);
109
110 return rc;
111}
112
113static inline int pm8xxx_tm_write_pwm(struct pm8xxx_tm_chip *chip, u8 reg)
114{
115 int rc;
116
117 rc = pm8xxx_writeb(chip->dev->parent,
118 chip->cdata.reg_addr_temp_alarm_pwm, reg);
119 if (rc)
120 pr_err("%s: pm8xxx_writeb(0x%03X)=0x%02X failed, rc=%d\n",
121 chip->cdata.tm_name,
122 chip->cdata.reg_addr_temp_alarm_pwm, reg, rc);
123
124 return rc;
125}
126
127static inline int
128pm8xxx_tm_shutdown_override(struct pm8xxx_tm_chip *chip,
129 enum pmic_thermal_override_mode mode)
130{
131 int rc;
132 u8 reg;
133
134 rc = pm8xxx_tm_read_ctrl(chip, &reg);
135 if (rc < 0)
136 return rc;
137
138 reg &= ~(TEMP_ALARM_CTRL_OVRD_MASK | TEMP_ALARM_CTRL_STATUS_MASK);
139 if (mode == SOFTWARE_OVERRIDE_ENABLED)
140 reg |= (TEMP_ALARM_CTRL_OVRD_ST3 | TEMP_ALARM_CTRL_OVRD_ST2) &
141 TEMP_ALARM_CTRL_OVRD_MASK;
142
143 rc = pm8xxx_tm_write_ctrl(chip, reg);
144
145 return rc;
146}
147
148/*
149 * This function initializes the internal temperature value based on only the
150 * current thermal stage and threshold.
151 */
152static int pm8xxx_tm_init_temp_no_adc(struct pm8xxx_tm_chip *chip)
153{
154 int rc;
155 u8 reg;
156
157 rc = pm8xxx_tm_read_ctrl(chip, &reg);
158 if (rc < 0)
159 return rc;
160
161 chip->stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
162 >> TEMP_ALARM_CTRL_STATUS_SHIFT;
163 chip->thresh = (reg & TEMP_ALARM_CTRL_THRESH_MASK)
164 >> TEMP_ALARM_CTRL_THRESH_SHIFT;
165
166 if (chip->stage)
167 chip->temp = chip->thresh * TEMP_THRESH_MIN +
168 (chip->stage - 1) * TEMP_STAGE_STEP +
169 TEMP_THRESH_MIN;
170 else
171 chip->temp = chip->cdata.default_no_adc_temp;
172
173 return 0;
174}
175
176/*
177 * This function updates the internal temperature value based on the
178 * current thermal stage and threshold as well as the previous stage
179 */
180static int pm8xxx_tm_update_temp_no_adc(struct pm8xxx_tm_chip *chip)
181{
182 unsigned int stage;
183 int rc;
184 u8 reg;
185
186 rc = pm8xxx_tm_read_ctrl(chip, &reg);
187 if (rc < 0)
188 return rc;
189
190 stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
191 >> TEMP_ALARM_CTRL_STATUS_SHIFT;
192 chip->thresh = (reg & TEMP_ALARM_CTRL_THRESH_MASK)
193 >> TEMP_ALARM_CTRL_THRESH_SHIFT;
194
195 if (stage > chip->stage) {
196 /* increasing stage, use lower bound */
197 chip->temp = (stage - 1) * TEMP_STAGE_STEP
198 + chip->thresh * TEMP_THRESH_STEP
199 + TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
200 } else if (stage < chip->stage) {
201 /* decreasing stage, use upper bound */
202 chip->temp = stage * TEMP_STAGE_STEP
203 + chip->thresh * TEMP_THRESH_STEP
204 - TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
205 }
206
207 chip->stage = stage;
208
209 return 0;
210}
211
212static int pm8xxx_tz_get_temp_no_adc(struct thermal_zone_device *thermal,
213 unsigned long *temp)
214{
215 struct pm8xxx_tm_chip *chip = thermal->devdata;
216 int rc;
217
218 if (!chip || !temp)
219 return -EINVAL;
220
221 rc = pm8xxx_tm_update_temp_no_adc(chip);
222 if (rc < 0)
223 return rc;
224
225 *temp = chip->temp;
226
227 return 0;
228}
229
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530230static int pm8xxx_tz_get_temp_pm8058_adc(struct thermal_zone_device *thermal,
231 unsigned long *temp)
232{
233 struct pm8xxx_tm_chip *chip = thermal->devdata;
234 DECLARE_COMPLETION_ONSTACK(wait);
235 struct adc_chan_result adc_result = {
236 .physical = 0lu,
237 };
238 int rc;
239
240 if (!chip || !temp)
241 return -EINVAL;
242
243 *temp = chip->temp;
244
245 rc = adc_channel_request_conv(chip->adc_handle, &wait);
246 if (rc < 0) {
247 pr_err("%s: adc_channel_request_conv() failed, rc = %d\n",
248 __func__, rc);
249 return rc;
250 }
251
252 wait_for_completion(&wait);
253
254 rc = adc_channel_read_result(chip->adc_handle, &adc_result);
255 if (rc < 0) {
256 pr_err("%s: adc_channel_read_result() failed, rc = %d\n",
257 __func__, rc);
258 return rc;
259 }
260
261 *temp = adc_result.physical;
262 chip->temp = adc_result.physical;
263
264 return 0;
265}
266
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -0700267static int pm8xxx_tz_get_temp_pm8xxx_adc(struct thermal_zone_device *thermal,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700268 unsigned long *temp)
269{
270 struct pm8xxx_tm_chip *chip = thermal->devdata;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -0700271 struct pm8xxx_adc_chan_result result = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272 .physical = 0lu,
273 };
274 int rc;
275
276 if (!chip || !temp)
277 return -EINVAL;
278
279 *temp = chip->temp;
280
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -0700281 rc = pm8xxx_adc_read(chip->cdata.adc_channel, &result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700282 if (rc < 0) {
283 pr_err("%s: adc_channel_read_result() failed, rc = %d\n",
284 chip->cdata.tm_name, rc);
285 return rc;
286 }
287
288 *temp = result.physical;
289 chip->temp = result.physical;
290
291 return 0;
292}
293
294static int pm8xxx_tz_get_mode(struct thermal_zone_device *thermal,
295 enum thermal_device_mode *mode)
296{
297 struct pm8xxx_tm_chip *chip = thermal->devdata;
298
299 if (!chip || !mode)
300 return -EINVAL;
301
302 *mode = chip->mode;
303
304 return 0;
305}
306
307static int pm8xxx_tz_set_mode(struct thermal_zone_device *thermal,
308 enum thermal_device_mode mode)
309{
310 struct pm8xxx_tm_chip *chip = thermal->devdata;
311
312 if (!chip)
313 return -EINVAL;
314
315 if (mode != chip->mode) {
316 if (mode == THERMAL_DEVICE_ENABLED)
317 pm8xxx_tm_shutdown_override(chip,
318 SOFTWARE_OVERRIDE_ENABLED);
319 else
320 pm8xxx_tm_shutdown_override(chip,
321 SOFTWARE_OVERRIDE_DISABLED);
322 }
323 chip->mode = mode;
324
325 return 0;
326}
327
328static int pm8xxx_tz_get_trip_type(struct thermal_zone_device *thermal,
329 int trip, enum thermal_trip_type *type)
330{
331 if (trip < 0 || !type)
332 return -EINVAL;
333
334 switch (trip) {
335 case TRIP_STAGE3:
336 *type = THERMAL_TRIP_CRITICAL;
337 break;
338 case TRIP_STAGE2:
339 *type = THERMAL_TRIP_HOT;
340 break;
341 case TRIP_STAGE1:
342 *type = THERMAL_TRIP_HOT;
343 break;
344 default:
345 return -EINVAL;
346 }
347
348 return 0;
349}
350
351static int pm8xxx_tz_get_trip_temp(struct thermal_zone_device *thermal,
352 int trip, unsigned long *temp)
353{
354 struct pm8xxx_tm_chip *chip = thermal->devdata;
355 int thresh_temp;
356
357 if (!chip || trip < 0 || !temp)
358 return -EINVAL;
359
360 thresh_temp = chip->thresh * TEMP_THRESH_STEP +
361 TEMP_THRESH_MIN;
362
363 switch (trip) {
364 case TRIP_STAGE3:
365 thresh_temp += 2 * TEMP_STAGE_STEP;
366 break;
367 case TRIP_STAGE2:
368 thresh_temp += TEMP_STAGE_STEP;
369 break;
370 case TRIP_STAGE1:
371 break;
372 default:
373 return -EINVAL;
374 }
375
376 *temp = thresh_temp;
377
378 return 0;
379}
380
381static int pm8xxx_tz_get_crit_temp(struct thermal_zone_device *thermal,
382 unsigned long *temp)
383{
384 struct pm8xxx_tm_chip *chip = thermal->devdata;
385
386 if (!chip || !temp)
387 return -EINVAL;
388
389 *temp = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN +
390 2 * TEMP_STAGE_STEP;
391
392 return 0;
393}
394
395static struct thermal_zone_device_ops pm8xxx_thermal_zone_ops_no_adc = {
396 .get_temp = pm8xxx_tz_get_temp_no_adc,
397 .get_mode = pm8xxx_tz_get_mode,
398 .set_mode = pm8xxx_tz_set_mode,
399 .get_trip_type = pm8xxx_tz_get_trip_type,
400 .get_trip_temp = pm8xxx_tz_get_trip_temp,
401 .get_crit_temp = pm8xxx_tz_get_crit_temp,
402};
403
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -0700404static struct thermal_zone_device_ops pm8xxx_thermal_zone_ops_pm8xxx_adc = {
405 .get_temp = pm8xxx_tz_get_temp_pm8xxx_adc,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 .get_mode = pm8xxx_tz_get_mode,
407 .set_mode = pm8xxx_tz_set_mode,
408 .get_trip_type = pm8xxx_tz_get_trip_type,
409 .get_trip_temp = pm8xxx_tz_get_trip_temp,
410 .get_crit_temp = pm8xxx_tz_get_crit_temp,
411};
412
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530413static struct thermal_zone_device_ops pm8xxx_thermal_zone_ops_pm8058_adc = {
414 .get_temp = pm8xxx_tz_get_temp_pm8058_adc,
415 .get_mode = pm8xxx_tz_get_mode,
416 .set_mode = pm8xxx_tz_set_mode,
417 .get_trip_type = pm8xxx_tz_get_trip_type,
418 .get_trip_temp = pm8xxx_tz_get_trip_temp,
419 .get_crit_temp = pm8xxx_tz_get_crit_temp,
420};
421
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700422static void pm8xxx_tm_work(struct work_struct *work)
423{
424 struct pm8xxx_tm_chip *chip
425 = container_of(work, struct pm8xxx_tm_chip, irq_work);
426 int rc;
427 u8 reg;
428
429 rc = pm8xxx_tm_read_ctrl(chip, &reg);
430 if (rc < 0)
431 goto bail;
432
433 if (chip->cdata.adc_type == PM8XXX_TM_ADC_NONE) {
434 rc = pm8xxx_tm_update_temp_no_adc(chip);
435 if (rc < 0)
436 goto bail;
437 pr_info("%s: Temp Alarm - stage=%u, threshold=%u, "
438 "temp=%lu mC\n", chip->cdata.tm_name, chip->stage,
439 chip->thresh, chip->temp);
440 } else {
441 chip->stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
442 >> TEMP_ALARM_CTRL_STATUS_SHIFT;
443 chip->thresh = (reg & TEMP_ALARM_CTRL_THRESH_MASK)
444 >> TEMP_ALARM_CTRL_THRESH_SHIFT;
445 pr_info("%s: Temp Alarm - stage=%u, threshold=%u\n",
446 chip->cdata.tm_name, chip->stage, chip->thresh);
447 }
448
449 /* Clear status bits. */
450 if (reg & (TEMP_ALARM_CTRL_ST2_SD | TEMP_ALARM_CTRL_ST3_SD)) {
451 reg &= ~(TEMP_ALARM_CTRL_ST2_SD | TEMP_ALARM_CTRL_ST3_SD
452 | TEMP_ALARM_CTRL_STATUS_MASK);
453
454 pm8xxx_tm_write_ctrl(chip, reg);
455 }
456
457 thermal_zone_device_update(chip->tz_dev);
458
459 /* Notify user space */
David Collins197c31e2012-09-10 13:15:17 -0700460 sysfs_notify(&chip->tz_dev->device.kobj, NULL, "type");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700461
462bail:
463 enable_irq(chip->tempstat_irq);
464 enable_irq(chip->overtemp_irq);
465}
466
467static irqreturn_t pm8xxx_tm_isr(int irq, void *data)
468{
469 struct pm8xxx_tm_chip *chip = data;
470
471 disable_irq_nosync(chip->tempstat_irq);
472 disable_irq_nosync(chip->overtemp_irq);
473 schedule_work(&chip->irq_work);
474
475 return IRQ_HANDLED;
476}
477
478static int pm8xxx_tm_init_reg(struct pm8xxx_tm_chip *chip)
479{
480 int rc;
481 u8 reg;
482
483 rc = pm8xxx_tm_read_ctrl(chip, &reg);
484 if (rc < 0)
485 return rc;
486
487 chip->stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
488 >> TEMP_ALARM_CTRL_STATUS_SHIFT;
489 chip->temp = 0;
490
491 /* Use temperature threshold set 0: (105, 125, 145) */
492 chip->thresh = 0;
493 reg = (chip->thresh << TEMP_ALARM_CTRL_THRESH_SHIFT)
494 & TEMP_ALARM_CTRL_THRESH_MASK;
495 rc = pm8xxx_tm_write_ctrl(chip, reg);
496 if (rc < 0)
497 return rc;
498
499 /*
500 * Set the PMIC alarm module PWM to have a frequency of 8 Hz. This
501 * helps cut down on the number of unnecessary interrupts fired when
502 * changing between thermal stages. Also, Enable the over temperature
503 * PWM whenever the PMIC is enabled.
504 */
505 reg = (1 << TEMP_ALARM_PWM_EN_SHIFT)
506 | (3 << TEMP_ALARM_PWM_PER_PRE_SHIFT)
507 | (3 << TEMP_ALARM_PWM_PER_DIV_SHIFT);
508
509 rc = pm8xxx_tm_write_pwm(chip, reg);
510
511 return rc;
512}
513
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530514static int pm8xxx_init_adc(struct pm8xxx_tm_chip *chip, bool enable)
515{
516 int rc = 0;
517
518 if (chip->cdata.adc_type == PM8XXX_TM_ADC_PM8058_ADC) {
519 if (enable) {
520 rc = adc_channel_open(chip->cdata.adc_channel,
521 &(chip->adc_handle));
522 if (rc < 0)
523 pr_err("adc_channel_open() failed.\n");
524 } else {
525 adc_channel_close(chip->adc_handle);
526 }
527 }
528
529 return rc;
530}
531
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700532static int __devinit pm8xxx_tm_probe(struct platform_device *pdev)
533{
534 const struct pm8xxx_tm_core_data *cdata = pdev->dev.platform_data;
535 struct thermal_zone_device_ops *tz_ops;
536 struct pm8xxx_tm_chip *chip;
537 struct resource *res;
538 int rc = 0;
539
540 if (!cdata) {
541 pr_err("missing core data\n");
542 return -EINVAL;
543 }
544
545 chip = kzalloc(sizeof(struct pm8xxx_tm_chip), GFP_KERNEL);
546 if (chip == NULL) {
547 pr_err("kzalloc() failed.\n");
548 return -ENOMEM;
549 }
550
551 chip->dev = &pdev->dev;
552 memcpy(&(chip->cdata), cdata, sizeof(struct pm8xxx_tm_core_data));
553
554 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
555 chip->cdata.irq_name_temp_stat);
556 if (res) {
557 chip->tempstat_irq = res->start;
558 } else {
559 pr_err("temp stat IRQ not specified\n");
560 goto err_free_chip;
561 }
562
563 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
564 chip->cdata.irq_name_over_temp);
565 if (res) {
566 chip->overtemp_irq = res->start;
567 } else {
568 pr_err("over temp IRQ not specified\n");
569 goto err_free_chip;
570 }
571
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530572 rc = pm8xxx_init_adc(chip, true);
573 if (rc < 0) {
574 pr_err("Unable to initialize adc\n");
575 goto err_free_chip;
576 }
577
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700578 /* Select proper thermal zone ops functions based on ADC type. */
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -0700579 if (chip->cdata.adc_type == PM8XXX_TM_ADC_PM8XXX_ADC)
580 tz_ops = &pm8xxx_thermal_zone_ops_pm8xxx_adc;
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530581 else if (chip->cdata.adc_type == PM8XXX_TM_ADC_PM8058_ADC)
582 tz_ops = &pm8xxx_thermal_zone_ops_pm8058_adc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700583 else
584 tz_ops = &pm8xxx_thermal_zone_ops_no_adc;
585
586 chip->tz_dev = thermal_zone_device_register(chip->cdata.tm_name,
587 TRIP_NUM, chip, tz_ops, 0, 0, 0, 0);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530588
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700589 if (chip->tz_dev == NULL) {
590 pr_err("thermal_zone_device_register() failed.\n");
591 rc = -ENODEV;
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530592 goto err_fail_adc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700593 }
594
595 rc = pm8xxx_tm_init_reg(chip);
596 if (rc < 0)
597 goto err_free_tz;
598 rc = pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
599 if (rc < 0)
600 goto err_free_tz;
601
602 if (chip->cdata.adc_type == PM8XXX_TM_ADC_NONE) {
603 rc = pm8xxx_tm_init_temp_no_adc(chip);
604 if (rc < 0)
605 goto err_free_tz;
606 }
607
608 /* Start in HW control; switch to SW control when user changes mode. */
609 chip->mode = THERMAL_DEVICE_DISABLED;
610 thermal_zone_device_update(chip->tz_dev);
611
612 INIT_WORK(&chip->irq_work, pm8xxx_tm_work);
613
614 rc = request_irq(chip->tempstat_irq, pm8xxx_tm_isr, IRQF_TRIGGER_RISING,
615 chip->cdata.irq_name_temp_stat, chip);
616 if (rc < 0) {
617 pr_err("request_irq(%d) failed: %d\n", chip->tempstat_irq, rc);
618 goto err_cancel_work;
619 }
620
621 rc = request_irq(chip->overtemp_irq, pm8xxx_tm_isr, IRQF_TRIGGER_RISING,
622 chip->cdata.irq_name_over_temp, chip);
623 if (rc < 0) {
624 pr_err("request_irq(%d) failed: %d\n", chip->overtemp_irq, rc);
625 goto err_free_irq_tempstat;
626 }
627
628 platform_set_drvdata(pdev, chip);
629
630 pr_info("OK\n");
631
632 return 0;
633
634err_free_irq_tempstat:
635 free_irq(chip->tempstat_irq, chip);
636err_cancel_work:
637 cancel_work_sync(&chip->irq_work);
638err_free_tz:
639 thermal_zone_device_unregister(chip->tz_dev);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530640err_fail_adc:
641 pm8xxx_init_adc(chip, false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700642err_free_chip:
643 kfree(chip);
644 return rc;
645}
646
647static int __devexit pm8xxx_tm_remove(struct platform_device *pdev)
648{
649 struct pm8xxx_tm_chip *chip = platform_get_drvdata(pdev);
650
651 if (chip) {
652 platform_set_drvdata(pdev, NULL);
653 cancel_work_sync(&chip->irq_work);
654 free_irq(chip->overtemp_irq, chip);
655 free_irq(chip->tempstat_irq, chip);
656 pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530657 pm8xxx_init_adc(chip, false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700658 thermal_zone_device_unregister(chip->tz_dev);
659 kfree(chip);
660 }
661 return 0;
662}
663
664#ifdef CONFIG_PM
665static int pm8xxx_tm_suspend(struct device *dev)
666{
667 struct platform_device *pdev = to_platform_device(dev);
668 struct pm8xxx_tm_chip *chip = platform_get_drvdata(pdev);
669
670 /* Clear override bits in suspend to allow hardware control */
671 pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
672
673 return 0;
674}
675
676static int pm8xxx_tm_resume(struct device *dev)
677{
678 struct platform_device *pdev = to_platform_device(dev);
679 struct pm8xxx_tm_chip *chip = platform_get_drvdata(pdev);
680
681 /* Override hardware actions so software can control */
682 if (chip->mode == THERMAL_DEVICE_ENABLED)
683 pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_ENABLED);
684
685 return 0;
686}
687
688static const struct dev_pm_ops pm8xxx_tm_pm_ops = {
689 .suspend = pm8xxx_tm_suspend,
690 .resume = pm8xxx_tm_resume,
691};
692
693#define PM8XXX_TM_PM_OPS (&pm8xxx_tm_pm_ops)
694#else
695#define PM8XXX_TM_PM_OPS NULL
696#endif
697
698static struct platform_driver pm8xxx_tm_driver = {
699 .probe = pm8xxx_tm_probe,
700 .remove = __devexit_p(pm8xxx_tm_remove),
701 .driver = {
702 .name = PM8XXX_TM_DEV_NAME,
703 .owner = THIS_MODULE,
704 .pm = PM8XXX_TM_PM_OPS,
705 },
706};
707
708static int __init pm8xxx_tm_init(void)
709{
710 return platform_driver_register(&pm8xxx_tm_driver);
711}
712
713static void __exit pm8xxx_tm_exit(void)
714{
715 platform_driver_unregister(&pm8xxx_tm_driver);
716}
717
718module_init(pm8xxx_tm_init);
719module_exit(pm8xxx_tm_exit);
720
721MODULE_LICENSE("GPL v2");
722MODULE_DESCRIPTION("PM8xxx Thermal Manager driver");
723MODULE_VERSION("1.0");
724MODULE_ALIAS("platform:" PM8XXX_TM_DEV_NAME);