blob: b068a064dd6c35c9ece71a70b000afdefc4377c3 [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;
David Collinsb2451ea2012-09-12 14:58:54 -070068 struct delayed_work irq_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069 struct device *dev;
70 struct thermal_zone_device *tz_dev;
71 unsigned long temp;
David Collinsb42b2942012-09-12 14:47:03 -070072 unsigned int prev_stage;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070073 enum thermal_device_mode mode;
74 unsigned int thresh;
75 unsigned int stage;
76 unsigned int tempstat_irq;
77 unsigned int overtemp_irq;
78 void *adc_handle;
79};
80
81enum pmic_thermal_override_mode {
82 SOFTWARE_OVERRIDE_DISABLED = 0,
83 SOFTWARE_OVERRIDE_ENABLED,
84};
85
David Collinsb2451ea2012-09-12 14:58:54 -070086/* Delay between TEMP_STAT IRQ going high and status value changing in ms. */
87#define STATUS_REGISTER_DELAY_MS 40
88
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070089static inline int pm8xxx_tm_read_ctrl(struct pm8xxx_tm_chip *chip, u8 *reg)
90{
91 int rc;
92
93 rc = pm8xxx_readb(chip->dev->parent,
94 chip->cdata.reg_addr_temp_alarm_ctrl, reg);
95 if (rc)
96 pr_err("%s: pm8xxx_readb(0x%03X) failed, rc=%d\n",
97 chip->cdata.tm_name,
98 chip->cdata.reg_addr_temp_alarm_ctrl, rc);
99
100 return rc;
101}
102
103static inline int pm8xxx_tm_write_ctrl(struct pm8xxx_tm_chip *chip, u8 reg)
104{
105 int rc;
106
107 rc = pm8xxx_writeb(chip->dev->parent,
108 chip->cdata.reg_addr_temp_alarm_ctrl, reg);
109 if (rc)
110 pr_err("%s: pm8xxx_writeb(0x%03X)=0x%02X failed, rc=%d\n",
111 chip->cdata.tm_name,
112 chip->cdata.reg_addr_temp_alarm_ctrl, reg, rc);
113
114 return rc;
115}
116
117static inline int pm8xxx_tm_write_pwm(struct pm8xxx_tm_chip *chip, u8 reg)
118{
119 int rc;
120
121 rc = pm8xxx_writeb(chip->dev->parent,
122 chip->cdata.reg_addr_temp_alarm_pwm, reg);
123 if (rc)
124 pr_err("%s: pm8xxx_writeb(0x%03X)=0x%02X failed, rc=%d\n",
125 chip->cdata.tm_name,
126 chip->cdata.reg_addr_temp_alarm_pwm, reg, rc);
127
128 return rc;
129}
130
131static inline int
132pm8xxx_tm_shutdown_override(struct pm8xxx_tm_chip *chip,
133 enum pmic_thermal_override_mode mode)
134{
135 int rc;
136 u8 reg;
137
138 rc = pm8xxx_tm_read_ctrl(chip, &reg);
139 if (rc < 0)
140 return rc;
141
142 reg &= ~(TEMP_ALARM_CTRL_OVRD_MASK | TEMP_ALARM_CTRL_STATUS_MASK);
143 if (mode == SOFTWARE_OVERRIDE_ENABLED)
144 reg |= (TEMP_ALARM_CTRL_OVRD_ST3 | TEMP_ALARM_CTRL_OVRD_ST2) &
145 TEMP_ALARM_CTRL_OVRD_MASK;
146
147 rc = pm8xxx_tm_write_ctrl(chip, reg);
148
149 return rc;
150}
151
152/*
153 * This function initializes the internal temperature value based on only the
154 * current thermal stage and threshold.
155 */
156static int pm8xxx_tm_init_temp_no_adc(struct pm8xxx_tm_chip *chip)
157{
158 int rc;
159 u8 reg;
160
161 rc = pm8xxx_tm_read_ctrl(chip, &reg);
162 if (rc < 0)
163 return rc;
164
165 chip->stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
166 >> TEMP_ALARM_CTRL_STATUS_SHIFT;
167 chip->thresh = (reg & TEMP_ALARM_CTRL_THRESH_MASK)
168 >> TEMP_ALARM_CTRL_THRESH_SHIFT;
169
170 if (chip->stage)
171 chip->temp = chip->thresh * TEMP_THRESH_MIN +
172 (chip->stage - 1) * TEMP_STAGE_STEP +
173 TEMP_THRESH_MIN;
174 else
175 chip->temp = chip->cdata.default_no_adc_temp;
176
177 return 0;
178}
179
180/*
181 * This function updates the internal temperature value based on the
182 * current thermal stage and threshold as well as the previous stage
183 */
184static int pm8xxx_tm_update_temp_no_adc(struct pm8xxx_tm_chip *chip)
185{
186 unsigned int stage;
187 int rc;
188 u8 reg;
189
190 rc = pm8xxx_tm_read_ctrl(chip, &reg);
191 if (rc < 0)
192 return rc;
193
194 stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
195 >> TEMP_ALARM_CTRL_STATUS_SHIFT;
196 chip->thresh = (reg & TEMP_ALARM_CTRL_THRESH_MASK)
197 >> TEMP_ALARM_CTRL_THRESH_SHIFT;
198
199 if (stage > chip->stage) {
200 /* increasing stage, use lower bound */
201 chip->temp = (stage - 1) * TEMP_STAGE_STEP
202 + chip->thresh * TEMP_THRESH_STEP
203 + TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
204 } else if (stage < chip->stage) {
205 /* decreasing stage, use upper bound */
206 chip->temp = stage * TEMP_STAGE_STEP
207 + chip->thresh * TEMP_THRESH_STEP
208 - TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
209 }
210
211 chip->stage = stage;
212
213 return 0;
214}
215
216static int pm8xxx_tz_get_temp_no_adc(struct thermal_zone_device *thermal,
217 unsigned long *temp)
218{
219 struct pm8xxx_tm_chip *chip = thermal->devdata;
220 int rc;
221
222 if (!chip || !temp)
223 return -EINVAL;
224
225 rc = pm8xxx_tm_update_temp_no_adc(chip);
226 if (rc < 0)
227 return rc;
228
229 *temp = chip->temp;
230
231 return 0;
232}
233
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530234static int pm8xxx_tz_get_temp_pm8058_adc(struct thermal_zone_device *thermal,
235 unsigned long *temp)
236{
237 struct pm8xxx_tm_chip *chip = thermal->devdata;
238 DECLARE_COMPLETION_ONSTACK(wait);
239 struct adc_chan_result adc_result = {
240 .physical = 0lu,
241 };
242 int rc;
243
244 if (!chip || !temp)
245 return -EINVAL;
246
247 *temp = chip->temp;
248
249 rc = adc_channel_request_conv(chip->adc_handle, &wait);
250 if (rc < 0) {
251 pr_err("%s: adc_channel_request_conv() failed, rc = %d\n",
252 __func__, rc);
253 return rc;
254 }
255
256 wait_for_completion(&wait);
257
258 rc = adc_channel_read_result(chip->adc_handle, &adc_result);
259 if (rc < 0) {
260 pr_err("%s: adc_channel_read_result() failed, rc = %d\n",
261 __func__, rc);
262 return rc;
263 }
264
265 *temp = adc_result.physical;
266 chip->temp = adc_result.physical;
267
268 return 0;
269}
270
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -0700271static int pm8xxx_tz_get_temp_pm8xxx_adc(struct thermal_zone_device *thermal,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272 unsigned long *temp)
273{
274 struct pm8xxx_tm_chip *chip = thermal->devdata;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -0700275 struct pm8xxx_adc_chan_result result = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700276 .physical = 0lu,
277 };
278 int rc;
279
280 if (!chip || !temp)
281 return -EINVAL;
282
283 *temp = chip->temp;
284
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -0700285 rc = pm8xxx_adc_read(chip->cdata.adc_channel, &result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700286 if (rc < 0) {
287 pr_err("%s: adc_channel_read_result() failed, rc = %d\n",
288 chip->cdata.tm_name, rc);
289 return rc;
290 }
291
292 *temp = result.physical;
293 chip->temp = result.physical;
294
295 return 0;
296}
297
298static int pm8xxx_tz_get_mode(struct thermal_zone_device *thermal,
299 enum thermal_device_mode *mode)
300{
301 struct pm8xxx_tm_chip *chip = thermal->devdata;
302
303 if (!chip || !mode)
304 return -EINVAL;
305
306 *mode = chip->mode;
307
308 return 0;
309}
310
311static int pm8xxx_tz_set_mode(struct thermal_zone_device *thermal,
312 enum thermal_device_mode mode)
313{
314 struct pm8xxx_tm_chip *chip = thermal->devdata;
315
316 if (!chip)
317 return -EINVAL;
318
319 if (mode != chip->mode) {
320 if (mode == THERMAL_DEVICE_ENABLED)
321 pm8xxx_tm_shutdown_override(chip,
322 SOFTWARE_OVERRIDE_ENABLED);
323 else
324 pm8xxx_tm_shutdown_override(chip,
325 SOFTWARE_OVERRIDE_DISABLED);
326 }
327 chip->mode = mode;
328
329 return 0;
330}
331
332static int pm8xxx_tz_get_trip_type(struct thermal_zone_device *thermal,
333 int trip, enum thermal_trip_type *type)
334{
335 if (trip < 0 || !type)
336 return -EINVAL;
337
338 switch (trip) {
339 case TRIP_STAGE3:
340 *type = THERMAL_TRIP_CRITICAL;
341 break;
342 case TRIP_STAGE2:
343 *type = THERMAL_TRIP_HOT;
344 break;
345 case TRIP_STAGE1:
346 *type = THERMAL_TRIP_HOT;
347 break;
348 default:
349 return -EINVAL;
350 }
351
352 return 0;
353}
354
355static int pm8xxx_tz_get_trip_temp(struct thermal_zone_device *thermal,
356 int trip, unsigned long *temp)
357{
358 struct pm8xxx_tm_chip *chip = thermal->devdata;
359 int thresh_temp;
360
361 if (!chip || trip < 0 || !temp)
362 return -EINVAL;
363
364 thresh_temp = chip->thresh * TEMP_THRESH_STEP +
365 TEMP_THRESH_MIN;
366
367 switch (trip) {
368 case TRIP_STAGE3:
369 thresh_temp += 2 * TEMP_STAGE_STEP;
370 break;
371 case TRIP_STAGE2:
372 thresh_temp += TEMP_STAGE_STEP;
373 break;
374 case TRIP_STAGE1:
375 break;
376 default:
377 return -EINVAL;
378 }
379
380 *temp = thresh_temp;
381
382 return 0;
383}
384
385static int pm8xxx_tz_get_crit_temp(struct thermal_zone_device *thermal,
386 unsigned long *temp)
387{
388 struct pm8xxx_tm_chip *chip = thermal->devdata;
389
390 if (!chip || !temp)
391 return -EINVAL;
392
393 *temp = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN +
394 2 * TEMP_STAGE_STEP;
395
396 return 0;
397}
398
399static struct thermal_zone_device_ops pm8xxx_thermal_zone_ops_no_adc = {
400 .get_temp = pm8xxx_tz_get_temp_no_adc,
401 .get_mode = pm8xxx_tz_get_mode,
402 .set_mode = pm8xxx_tz_set_mode,
403 .get_trip_type = pm8xxx_tz_get_trip_type,
404 .get_trip_temp = pm8xxx_tz_get_trip_temp,
405 .get_crit_temp = pm8xxx_tz_get_crit_temp,
406};
407
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -0700408static struct thermal_zone_device_ops pm8xxx_thermal_zone_ops_pm8xxx_adc = {
409 .get_temp = pm8xxx_tz_get_temp_pm8xxx_adc,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700410 .get_mode = pm8xxx_tz_get_mode,
411 .set_mode = pm8xxx_tz_set_mode,
412 .get_trip_type = pm8xxx_tz_get_trip_type,
413 .get_trip_temp = pm8xxx_tz_get_trip_temp,
414 .get_crit_temp = pm8xxx_tz_get_crit_temp,
415};
416
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530417static struct thermal_zone_device_ops pm8xxx_thermal_zone_ops_pm8058_adc = {
418 .get_temp = pm8xxx_tz_get_temp_pm8058_adc,
419 .get_mode = pm8xxx_tz_get_mode,
420 .set_mode = pm8xxx_tz_set_mode,
421 .get_trip_type = pm8xxx_tz_get_trip_type,
422 .get_trip_temp = pm8xxx_tz_get_trip_temp,
423 .get_crit_temp = pm8xxx_tz_get_crit_temp,
424};
425
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700426static void pm8xxx_tm_work(struct work_struct *work)
427{
David Collinsb2451ea2012-09-12 14:58:54 -0700428 struct delayed_work *dwork
429 = container_of(work, struct delayed_work, work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700430 struct pm8xxx_tm_chip *chip
David Collinsb2451ea2012-09-12 14:58:54 -0700431 = container_of(dwork, struct pm8xxx_tm_chip, irq_work);
David Collinsb42b2942012-09-12 14:47:03 -0700432 unsigned long temp = 0;
433 int rc, stage, thresh;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700434 u8 reg;
435
436 rc = pm8xxx_tm_read_ctrl(chip, &reg);
437 if (rc < 0)
438 goto bail;
439
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700440 /* Clear status bits. */
441 if (reg & (TEMP_ALARM_CTRL_ST2_SD | TEMP_ALARM_CTRL_ST3_SD)) {
442 reg &= ~(TEMP_ALARM_CTRL_ST2_SD | TEMP_ALARM_CTRL_ST3_SD
443 | TEMP_ALARM_CTRL_STATUS_MASK);
444
445 pm8xxx_tm_write_ctrl(chip, reg);
446 }
447
David Collinsb42b2942012-09-12 14:47:03 -0700448 stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
449 >> TEMP_ALARM_CTRL_STATUS_SHIFT;
450 thresh = (reg & TEMP_ALARM_CTRL_THRESH_MASK)
451 >> TEMP_ALARM_CTRL_THRESH_SHIFT;
452
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700453 thermal_zone_device_update(chip->tz_dev);
454
David Collinsb42b2942012-09-12 14:47:03 -0700455 if (stage != chip->prev_stage) {
456 chip->prev_stage = stage;
457
458 switch (chip->cdata.adc_type) {
459 case PM8XXX_TM_ADC_NONE:
460 rc = pm8xxx_tz_get_temp_no_adc(chip->tz_dev, &temp);
461 break;
462 case PM8XXX_TM_ADC_PM8058_ADC:
463 rc = pm8xxx_tz_get_temp_pm8058_adc(chip->tz_dev, &temp);
464 break;
465 case PM8XXX_TM_ADC_PM8XXX_ADC:
466 rc = pm8xxx_tz_get_temp_pm8xxx_adc(chip->tz_dev, &temp);
467 break;
468 }
469 if (rc < 0)
470 goto bail;
471
472 pr_crit("%s: PMIC Temp Alarm - stage=%u, threshold=%u, temp=%lu mC\n",
473 chip->cdata.tm_name, stage, thresh, temp);
474
475 /* Notify user space */
476 sysfs_notify(&chip->tz_dev->device.kobj, NULL, "type");
477 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700478
479bail:
David Collinsb2451ea2012-09-12 14:58:54 -0700480 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700481}
482
483static irqreturn_t pm8xxx_tm_isr(int irq, void *data)
484{
485 struct pm8xxx_tm_chip *chip = data;
486
David Collinsb2451ea2012-09-12 14:58:54 -0700487 schedule_delayed_work(&chip->irq_work,
488 msecs_to_jiffies(STATUS_REGISTER_DELAY_MS) + 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700489
490 return IRQ_HANDLED;
491}
492
493static int pm8xxx_tm_init_reg(struct pm8xxx_tm_chip *chip)
494{
495 int rc;
496 u8 reg;
497
498 rc = pm8xxx_tm_read_ctrl(chip, &reg);
499 if (rc < 0)
500 return rc;
501
502 chip->stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
503 >> TEMP_ALARM_CTRL_STATUS_SHIFT;
504 chip->temp = 0;
505
506 /* Use temperature threshold set 0: (105, 125, 145) */
507 chip->thresh = 0;
508 reg = (chip->thresh << TEMP_ALARM_CTRL_THRESH_SHIFT)
509 & TEMP_ALARM_CTRL_THRESH_MASK;
510 rc = pm8xxx_tm_write_ctrl(chip, reg);
511 if (rc < 0)
512 return rc;
513
514 /*
515 * Set the PMIC alarm module PWM to have a frequency of 8 Hz. This
516 * helps cut down on the number of unnecessary interrupts fired when
517 * changing between thermal stages. Also, Enable the over temperature
518 * PWM whenever the PMIC is enabled.
519 */
520 reg = (1 << TEMP_ALARM_PWM_EN_SHIFT)
521 | (3 << TEMP_ALARM_PWM_PER_PRE_SHIFT)
522 | (3 << TEMP_ALARM_PWM_PER_DIV_SHIFT);
523
524 rc = pm8xxx_tm_write_pwm(chip, reg);
525
526 return rc;
527}
528
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530529static int pm8xxx_init_adc(struct pm8xxx_tm_chip *chip, bool enable)
530{
531 int rc = 0;
532
533 if (chip->cdata.adc_type == PM8XXX_TM_ADC_PM8058_ADC) {
534 if (enable) {
535 rc = adc_channel_open(chip->cdata.adc_channel,
536 &(chip->adc_handle));
537 if (rc < 0)
538 pr_err("adc_channel_open() failed.\n");
539 } else {
540 adc_channel_close(chip->adc_handle);
541 }
542 }
543
544 return rc;
545}
546
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547static int __devinit pm8xxx_tm_probe(struct platform_device *pdev)
548{
549 const struct pm8xxx_tm_core_data *cdata = pdev->dev.platform_data;
550 struct thermal_zone_device_ops *tz_ops;
551 struct pm8xxx_tm_chip *chip;
552 struct resource *res;
553 int rc = 0;
554
555 if (!cdata) {
556 pr_err("missing core data\n");
557 return -EINVAL;
558 }
559
560 chip = kzalloc(sizeof(struct pm8xxx_tm_chip), GFP_KERNEL);
561 if (chip == NULL) {
562 pr_err("kzalloc() failed.\n");
563 return -ENOMEM;
564 }
565
566 chip->dev = &pdev->dev;
567 memcpy(&(chip->cdata), cdata, sizeof(struct pm8xxx_tm_core_data));
568
569 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
570 chip->cdata.irq_name_temp_stat);
571 if (res) {
572 chip->tempstat_irq = res->start;
573 } else {
574 pr_err("temp stat IRQ not specified\n");
575 goto err_free_chip;
576 }
577
578 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
579 chip->cdata.irq_name_over_temp);
580 if (res) {
581 chip->overtemp_irq = res->start;
582 } else {
583 pr_err("over temp IRQ not specified\n");
584 goto err_free_chip;
585 }
586
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530587 rc = pm8xxx_init_adc(chip, true);
588 if (rc < 0) {
589 pr_err("Unable to initialize adc\n");
590 goto err_free_chip;
591 }
592
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700593 /* Select proper thermal zone ops functions based on ADC type. */
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -0700594 if (chip->cdata.adc_type == PM8XXX_TM_ADC_PM8XXX_ADC)
595 tz_ops = &pm8xxx_thermal_zone_ops_pm8xxx_adc;
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530596 else if (chip->cdata.adc_type == PM8XXX_TM_ADC_PM8058_ADC)
597 tz_ops = &pm8xxx_thermal_zone_ops_pm8058_adc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700598 else
599 tz_ops = &pm8xxx_thermal_zone_ops_no_adc;
600
601 chip->tz_dev = thermal_zone_device_register(chip->cdata.tm_name,
602 TRIP_NUM, chip, tz_ops, 0, 0, 0, 0);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530603
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700604 if (chip->tz_dev == NULL) {
605 pr_err("thermal_zone_device_register() failed.\n");
606 rc = -ENODEV;
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530607 goto err_fail_adc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700608 }
609
610 rc = pm8xxx_tm_init_reg(chip);
611 if (rc < 0)
612 goto err_free_tz;
613 rc = pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
614 if (rc < 0)
615 goto err_free_tz;
616
617 if (chip->cdata.adc_type == PM8XXX_TM_ADC_NONE) {
618 rc = pm8xxx_tm_init_temp_no_adc(chip);
619 if (rc < 0)
620 goto err_free_tz;
621 }
622
623 /* Start in HW control; switch to SW control when user changes mode. */
624 chip->mode = THERMAL_DEVICE_DISABLED;
625 thermal_zone_device_update(chip->tz_dev);
626
David Collinsb2451ea2012-09-12 14:58:54 -0700627 INIT_DELAYED_WORK(&chip->irq_work, pm8xxx_tm_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700628
629 rc = request_irq(chip->tempstat_irq, pm8xxx_tm_isr, IRQF_TRIGGER_RISING,
630 chip->cdata.irq_name_temp_stat, chip);
631 if (rc < 0) {
632 pr_err("request_irq(%d) failed: %d\n", chip->tempstat_irq, rc);
633 goto err_cancel_work;
634 }
635
636 rc = request_irq(chip->overtemp_irq, pm8xxx_tm_isr, IRQF_TRIGGER_RISING,
637 chip->cdata.irq_name_over_temp, chip);
638 if (rc < 0) {
639 pr_err("request_irq(%d) failed: %d\n", chip->overtemp_irq, rc);
640 goto err_free_irq_tempstat;
641 }
642
643 platform_set_drvdata(pdev, chip);
644
645 pr_info("OK\n");
646
647 return 0;
648
649err_free_irq_tempstat:
650 free_irq(chip->tempstat_irq, chip);
651err_cancel_work:
David Collinsb2451ea2012-09-12 14:58:54 -0700652 cancel_delayed_work_sync(&chip->irq_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700653err_free_tz:
654 thermal_zone_device_unregister(chip->tz_dev);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530655err_fail_adc:
656 pm8xxx_init_adc(chip, false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700657err_free_chip:
658 kfree(chip);
659 return rc;
660}
661
662static int __devexit pm8xxx_tm_remove(struct platform_device *pdev)
663{
664 struct pm8xxx_tm_chip *chip = platform_get_drvdata(pdev);
665
666 if (chip) {
667 platform_set_drvdata(pdev, NULL);
David Collinsb2451ea2012-09-12 14:58:54 -0700668 cancel_delayed_work_sync(&chip->irq_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700669 free_irq(chip->overtemp_irq, chip);
670 free_irq(chip->tempstat_irq, chip);
671 pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530672 pm8xxx_init_adc(chip, false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700673 thermal_zone_device_unregister(chip->tz_dev);
674 kfree(chip);
675 }
676 return 0;
677}
678
679#ifdef CONFIG_PM
680static int pm8xxx_tm_suspend(struct device *dev)
681{
682 struct platform_device *pdev = to_platform_device(dev);
683 struct pm8xxx_tm_chip *chip = platform_get_drvdata(pdev);
684
685 /* Clear override bits in suspend to allow hardware control */
686 pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
687
688 return 0;
689}
690
691static int pm8xxx_tm_resume(struct device *dev)
692{
693 struct platform_device *pdev = to_platform_device(dev);
694 struct pm8xxx_tm_chip *chip = platform_get_drvdata(pdev);
695
696 /* Override hardware actions so software can control */
697 if (chip->mode == THERMAL_DEVICE_ENABLED)
698 pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_ENABLED);
699
700 return 0;
701}
702
703static const struct dev_pm_ops pm8xxx_tm_pm_ops = {
704 .suspend = pm8xxx_tm_suspend,
705 .resume = pm8xxx_tm_resume,
706};
707
708#define PM8XXX_TM_PM_OPS (&pm8xxx_tm_pm_ops)
709#else
710#define PM8XXX_TM_PM_OPS NULL
711#endif
712
713static struct platform_driver pm8xxx_tm_driver = {
714 .probe = pm8xxx_tm_probe,
715 .remove = __devexit_p(pm8xxx_tm_remove),
716 .driver = {
717 .name = PM8XXX_TM_DEV_NAME,
718 .owner = THIS_MODULE,
719 .pm = PM8XXX_TM_PM_OPS,
720 },
721};
722
723static int __init pm8xxx_tm_init(void)
724{
725 return platform_driver_register(&pm8xxx_tm_driver);
726}
727
728static void __exit pm8xxx_tm_exit(void)
729{
730 platform_driver_unregister(&pm8xxx_tm_driver);
731}
732
733module_init(pm8xxx_tm_init);
734module_exit(pm8xxx_tm_exit);
735
736MODULE_LICENSE("GPL v2");
737MODULE_DESCRIPTION("PM8xxx Thermal Manager driver");
738MODULE_VERSION("1.0");
739MODULE_ALIAS("platform:" PM8XXX_TM_DEV_NAME);