blob: cc98f379f22fe887d1d5c7cb068da7f1e0dbfd0f [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13/*
14 * Qualcomm PMIC8058 Thermal Manager driver
15 *
16 */
17
18#include <linux/module.h>
19#include <linux/slab.h>
20#include <linux/platform_device.h>
21#include <linux/err.h>
22#include <linux/thermal.h>
23#include <linux/interrupt.h>
24#include <linux/mfd/pmic8058.h>
25#include <linux/completion.h>
26
27#include <linux/msm_adc.h>
28
29/* PMIC8058 TEMP_ALRM registers */
30#define SSBI_REG_TEMP_ALRM_CTRL 0x1B
31#define SSBI_REG_TEMP_ALRM_PWM 0x9B
32#define SSBI_REG_TEMP_ALRM_TEST1 0x7A
33#define SSBI_REG_TEMP_ALRM_TEST2 0xAB
34
35/* TEMP_ALRM_CTRL */
36#define PM8058_TEMP_ST3_SD 0x80
37#define PM8058_TEMP_ST2_SD 0x40
38#define PM8058_TEMP_STATUS_MASK 0x30
39#define PM8058_TEMP_STATUS_SHIFT 4
40#define PM8058_TEMP_THRESH_MASK 0x0C
41#define PM8058_TEMP_THRESH_SHIFT 2
42#define PM8058_TEMP_OVRD_ST3 0x02
43#define PM8058_TEMP_OVRD_ST2 0x01
44#define PM8058_TEMP_OVRD_MASK 0x03
45
46#define PM8058_TEMP_STAGE_STEP 20000 /* Stage step: 20 C */
47#define PM8058_TEMP_STAGE_HYSTERESIS 2000
48
49#define PM8058_TEMP_THRESH_MIN 105000 /* Threshold Min: 105 C */
50#define PM8058_TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
51
52/* TEMP_ALRM_PWM */
53#define PM8058_TEMP_PWM_EN_MASK 0xC0
54#define PM8058_TEMP_PWM_EN_SHIFT 6
55#define PM8058_TEMP_PWM_PER_PRE_MASK 0x38
56#define PM8058_TEMP_PWM_PER_PRE_SHIFT 3
57#define PM8058_TEMP_PWM_PER_DIV_MASK 0x07
58#define PM8058_TEMP_PWM_PER_DIV_SHIFT 0
59
60/* Trips: from critical to less critical */
61#define PM8058_TRIP_STAGE3 0
62#define PM8058_TRIP_STAGE2 1
63#define PM8058_TRIP_STAGE1 2
64#define PM8058_TRIP_NUM 3
65
66#define PM8058_TEMP_ADC_CH CHANNEL_ADC_DIE_TEMP
67
68struct pm8058_tm_device {
69 struct pm8058_chip *pm_chip;
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 irq;
76 void *adc_handle;
77};
78
79enum pmic_thermal_override_mode {
80 SOFTWARE_OVERRIDE_DISABLED = 0,
81 SOFTWARE_OVERRIDE_ENABLED,
82};
83
84static inline int pm8058_tm_read_ctrl(struct pm8058_chip *chip, u8 *reg)
85{
86 int rc;
87
88 rc = pm8058_read(chip, SSBI_REG_TEMP_ALRM_CTRL, reg, 1);
89 if (rc)
90 pr_err("%s: pm8058_read FAIL: rc=%d\n", __func__, rc);
91
92 return rc;
93}
94
95static inline int pm8058_tm_write_ctrl(struct pm8058_chip *chip, u8 reg)
96{
97 int rc;
98
99 rc = pm8058_write(chip, SSBI_REG_TEMP_ALRM_CTRL, &reg, 1);
100 if (rc)
101 pr_err("%s: pm8058_write FAIL: rc=%d\n", __func__, rc);
102
103 return rc;
104}
105
106static inline int pm8058_tm_write_pwm(struct pm8058_chip *chip, u8 reg)
107{
108 int rc;
109
110 rc = pm8058_write(chip, SSBI_REG_TEMP_ALRM_PWM, &reg, 1);
111 if (rc)
112 pr_err("%s: pm8058_write FAIL: rc=%d\n", __func__, rc);
113
114 return rc;
115}
116
117static inline int
118pm8058_tm_shutdown_override(struct pm8058_chip *chip,
119 enum pmic_thermal_override_mode mode)
120{
121 int rc;
122 u8 reg;
123
124 rc = pm8058_tm_read_ctrl(chip, &reg);
125 if (rc < 0)
126 return rc;
127
128 reg &= ~(PM8058_TEMP_OVRD_MASK | PM8058_TEMP_STATUS_MASK);
129 if (mode == SOFTWARE_OVERRIDE_ENABLED)
130 reg |= (PM8058_TEMP_OVRD_ST3 | PM8058_TEMP_OVRD_ST2) &
131 PM8058_TEMP_OVRD_MASK;
132
133 rc = pm8058_tm_write_ctrl(chip, reg);
134
135 return rc;
136}
137
138static int pm8058_tz_get_temp(struct thermal_zone_device *thermal,
139 unsigned long *temp)
140{
141 struct pm8058_tm_device *tm = thermal->devdata;
142 DECLARE_COMPLETION_ONSTACK(wait);
143 struct adc_chan_result adc_result = {
144 .physical = 0lu,
145 };
146 int rc;
147
148 if (!tm || !temp)
149 return -EINVAL;
150
151 *temp = tm->temp;
152
153 rc = adc_channel_request_conv(tm->adc_handle, &wait);
154 if (rc < 0) {
155 pr_err("%s: adc_channel_request_conv() failed, rc = %d\n",
156 __func__, rc);
157 return rc;
158 }
159
160 wait_for_completion(&wait);
161
162 rc = adc_channel_read_result(tm->adc_handle, &adc_result);
163 if (rc < 0) {
164 pr_err("%s: adc_channel_read_result() failed, rc = %d\n",
165 __func__, rc);
166 return rc;
167 }
168
169 *temp = adc_result.physical;
170 tm->temp = adc_result.physical;
171
172 return 0;
173}
174
175static int pm8058_tz_get_mode(struct thermal_zone_device *thermal,
176 enum thermal_device_mode *mode)
177{
178 struct pm8058_tm_device *tm = thermal->devdata;
179
180 if (!tm || !mode)
181 return -EINVAL;
182
183 *mode = tm->mode;
184
185 return 0;
186}
187
188static int pm8058_tz_set_mode(struct thermal_zone_device *thermal,
189 enum thermal_device_mode mode)
190{
191 struct pm8058_tm_device *tm = thermal->devdata;
192
193 if (!tm)
194 return -EINVAL;
195
196 if (mode != tm->mode) {
197 if (mode == THERMAL_DEVICE_ENABLED)
198 pm8058_tm_shutdown_override(tm->pm_chip,
199 SOFTWARE_OVERRIDE_ENABLED);
200 else
201 pm8058_tm_shutdown_override(tm->pm_chip,
202 SOFTWARE_OVERRIDE_DISABLED);
203 }
204 tm->mode = mode;
205
206 return 0;
207}
208
209static int pm8058_tz_get_trip_type(struct thermal_zone_device *thermal,
210 int trip, enum thermal_trip_type *type)
211{
212 struct pm8058_tm_device *tm = thermal->devdata;
213
214 if (!tm || trip < 0 || !type)
215 return -EINVAL;
216
217 switch (trip) {
218 case PM8058_TRIP_STAGE3:
219 *type = THERMAL_TRIP_CRITICAL;
220 break;
221 case PM8058_TRIP_STAGE2:
222 *type = THERMAL_TRIP_HOT;
223 break;
224 case PM8058_TRIP_STAGE1:
225 *type = THERMAL_TRIP_HOT;
226 break;
227 default:
228 return -EINVAL;
229 }
230
231 return 0;
232}
233
234static int pm8058_tz_get_trip_temp(struct thermal_zone_device *thermal,
235 int trip, unsigned long *temp)
236{
237 struct pm8058_tm_device *tm = thermal->devdata;
238 int thresh_temp;
239
240 if (!tm || trip < 0 || !temp)
241 return -EINVAL;
242
243 thresh_temp = tm->thresh * PM8058_TEMP_THRESH_STEP +
244 PM8058_TEMP_THRESH_MIN;
245
246 switch (trip) {
247 case PM8058_TRIP_STAGE3:
248 thresh_temp += 2 * PM8058_TEMP_STAGE_STEP;
249 break;
250 case PM8058_TRIP_STAGE2:
251 thresh_temp += PM8058_TEMP_STAGE_STEP;
252 break;
253 case PM8058_TRIP_STAGE1:
254 break;
255 default:
256 return -EINVAL;
257 }
258
259 *temp = thresh_temp;
260
261 return 0;
262}
263
264static int pm8058_tz_get_crit_temp(struct thermal_zone_device *thermal,
265 unsigned long *temp)
266{
267 struct pm8058_tm_device *tm = thermal->devdata;
268
269 if (!tm || !temp)
270 return -EINVAL;
271
272 *temp = tm->thresh * PM8058_TEMP_THRESH_STEP + PM8058_TEMP_THRESH_MIN +
273 2 * PM8058_TEMP_STAGE_STEP;
274
275 return 0;
276}
277
278static struct thermal_zone_device_ops pm8058_thermal_zone_ops = {
279 .get_temp = pm8058_tz_get_temp,
280 .get_mode = pm8058_tz_get_mode,
281 .set_mode = pm8058_tz_set_mode,
282 .get_trip_type = pm8058_tz_get_trip_type,
283 .get_trip_temp = pm8058_tz_get_trip_temp,
284 .get_crit_temp = pm8058_tz_get_crit_temp,
285};
286
287static irqreturn_t pm8058_tm_isr(int irq, void *data)
288{
289 struct pm8058_tm_device *tm = data;
290 int rc;
291 u8 reg;
292
293 rc = pm8058_tm_read_ctrl(tm->pm_chip, &reg);
294 if (rc < 0)
295 goto isr_handled;
296
297 tm->stage = (reg & PM8058_TEMP_STATUS_MASK) >> PM8058_TEMP_STATUS_SHIFT;
298 tm->thresh = (reg & PM8058_TEMP_THRESH_MASK) >>
299 PM8058_TEMP_THRESH_SHIFT;
300
301 if (reg & (PM8058_TEMP_ST2_SD | PM8058_TEMP_ST3_SD)) {
302 reg &= ~(PM8058_TEMP_ST2_SD | PM8058_TEMP_ST3_SD |
303 PM8058_TEMP_STATUS_MASK);
304 pm8058_tm_write_ctrl(tm->pm_chip, reg);
305 }
306
307 thermal_zone_device_update(tm->tz_dev);
308
309 /* Notify user space */
310 if (tm->mode == THERMAL_DEVICE_ENABLED)
311 kobject_uevent(&tm->tz_dev->device.kobj, KOBJ_CHANGE);
312
313isr_handled:
314 return IRQ_HANDLED;
315}
316
317static int pm8058_tm_init_reg(struct pm8058_tm_device *tm)
318{
319 int rc;
320 u8 reg;
321
322 rc = pm8058_tm_read_ctrl(tm->pm_chip, &reg);
323 if (rc < 0)
324 return rc;
325
326 tm->stage = (reg & PM8058_TEMP_STATUS_MASK) >> PM8058_TEMP_STATUS_SHIFT;
327 tm->temp = 0;
328
329 /* Use temperature threshold set 0: (105, 125, 145) */
330 tm->thresh = 0;
331 reg = (tm->thresh << PM8058_TEMP_THRESH_SHIFT) &
332 PM8058_TEMP_THRESH_MASK;
333 rc = pm8058_tm_write_ctrl(tm->pm_chip, reg);
334 if (rc < 0)
335 return rc;
336
337 /*
338 * Set the PMIC alarm module PWM to have a frequency of 8 Hz. This
339 * helps cut down on the number of unnecessary interrupts fired when
340 * changing between thermal stages. Also, Enable the over temperature
341 * PWM whenever the PMIC is enabled.
342 */
343 reg = 1 << PM8058_TEMP_PWM_EN_SHIFT |
344 3 << PM8058_TEMP_PWM_PER_PRE_SHIFT |
345 3 << PM8058_TEMP_PWM_PER_DIV_SHIFT;
346
347 rc = pm8058_tm_write_pwm(tm->pm_chip, reg);
348
349 return rc;
350}
351
352static int __devinit pmic8058_tm_probe(struct platform_device *pdev)
353{
354 DECLARE_COMPLETION_ONSTACK(wait);
355 struct pm8058_tm_device *tmdev;
356 struct pm8058_chip *pm_chip;
357 unsigned int irq;
358 int rc;
359
360 pm_chip = dev_get_drvdata(pdev->dev.parent);
361 if (pm_chip == NULL) {
362 pr_err("%s: no driver data passed in.\n", __func__);
363 return -EFAULT;
364 }
365
366 irq = platform_get_irq(pdev, 0);
367 if (!irq) {
368 pr_err("%s: no IRQ passed in.\n", __func__);
369 return -EFAULT;
370 }
371
372 tmdev = kzalloc(sizeof *tmdev, GFP_KERNEL);
373 if (tmdev == NULL) {
374 pr_err("%s: kzalloc() failed.\n", __func__);
375 return -ENOMEM;
376 }
377
378 rc = adc_channel_open(PM8058_TEMP_ADC_CH, &(tmdev->adc_handle));
379 if (rc < 0) {
380 pr_err("%s: adc_channel_open() failed.\n", __func__);
381 kfree(tmdev);
382 return rc;
383 }
384
385 /* calibrate the die temperature sensor */
386 if (adc_calib_request(tmdev->adc_handle, &wait) == CALIB_STARTED)
387 wait_for_completion(&wait);
388
389 tmdev->pm_chip = pm_chip;
390 tmdev->tz_dev = thermal_zone_device_register("pm8058_tz",
391 PM8058_TRIP_NUM, tmdev,
392 &pm8058_thermal_zone_ops,
393 0, 0, 0, 0);
394 if (tmdev->tz_dev == NULL) {
395 pr_err("%s: thermal_zone_device_register() failed.\n",
396 __func__);
397 adc_channel_close(tmdev->adc_handle);
398 kfree(tmdev);
399 return -ENODEV;
400 }
401
402 rc = pm8058_tm_init_reg(tmdev);
403 pm8058_tm_shutdown_override(tmdev->pm_chip, SOFTWARE_OVERRIDE_DISABLED);
404 if (rc < 0) {
405 thermal_zone_device_unregister(tmdev->tz_dev);
406 adc_channel_close(tmdev->adc_handle);
407 kfree(tmdev);
408 return rc;
409 }
410
411 /* start in HW control, switch to SW control when user changes mode */
412 tmdev->mode = THERMAL_DEVICE_DISABLED;
413 thermal_zone_device_update(tmdev->tz_dev);
414
415 platform_set_drvdata(pdev, tmdev);
416
417 rc = request_threaded_irq(irq, NULL, pm8058_tm_isr,
418 IRQF_TRIGGER_RISING | IRQF_DISABLED,
419 "pm8058-tm-irq", tmdev);
420 if (rc < 0) {
421 pr_err("%s: request_irq(%d) FAIL: %d\n", __func__, irq, rc);
422 thermal_zone_device_unregister(tmdev->tz_dev);
423 platform_set_drvdata(pdev, tmdev->pm_chip);
424 adc_channel_close(tmdev->adc_handle);
425 kfree(tmdev);
426 return rc;
427 }
428 tmdev->irq = irq;
429
430 pr_notice("%s: OK\n", __func__);
431 return 0;
432}
433
434static int __devexit pmic8058_tm_remove(struct platform_device *pdev)
435{
436 struct pm8058_tm_device *tmdev = platform_get_drvdata(pdev);
437
438 thermal_zone_device_unregister(tmdev->tz_dev);
439 platform_set_drvdata(pdev, tmdev->pm_chip);
440 pm8058_tm_shutdown_override(tmdev->pm_chip, THERMAL_DEVICE_DISABLED);
441 adc_channel_close(tmdev->adc_handle);
442 free_irq(tmdev->irq, tmdev);
443 kfree(tmdev);
444
445 return 0;
446}
447
448#ifdef CONFIG_PM
449static int pmic8058_tm_suspend(struct device *dev)
450{
451 struct platform_device *pdev = to_platform_device(dev);
452 struct pm8058_tm_device *tm = platform_get_drvdata(pdev);
453
454 /* Clear override bits in suspend to allow hardware control */
455 pm8058_tm_shutdown_override(tm->pm_chip, SOFTWARE_OVERRIDE_DISABLED);
456
457 return 0;
458}
459
460static int pmic8058_tm_resume(struct device *dev)
461{
462 struct platform_device *pdev = to_platform_device(dev);
463 struct pm8058_tm_device *tm = platform_get_drvdata(pdev);
464
465 /* Override hardware actions so software can control */
466 if (tm->mode == THERMAL_DEVICE_ENABLED)
467 pm8058_tm_shutdown_override(tm->pm_chip,
468 SOFTWARE_OVERRIDE_ENABLED);
469
470 return 0;
471}
472
473static const struct dev_pm_ops pmic8058_tm_pm_ops = {
474 .suspend = pmic8058_tm_suspend,
475 .resume = pmic8058_tm_resume,
476};
477
478#define PM8058_TM_PM_OPS (&pmic8058_tm_pm_ops)
479#else
480#define PM8058_TM_PM_OPS NULL
481#endif
482
483static struct platform_driver pmic8058_tm_driver = {
484 .probe = pmic8058_tm_probe,
485 .remove = __devexit_p(pmic8058_tm_remove),
486 .driver = {
487 .name = "pm8058-tm",
488 .owner = THIS_MODULE,
489 .pm = PM8058_TM_PM_OPS,
490 },
491};
492
493static int __init pm8058_tm_init(void)
494{
495 return platform_driver_register(&pmic8058_tm_driver);
496}
497
498static void __exit pm8058_tm_exit(void)
499{
500 platform_driver_unregister(&pmic8058_tm_driver);
501}
502
503module_init(pm8058_tm_init);
504module_exit(pm8058_tm_exit);
505
506MODULE_LICENSE("GPL v2");
507MODULE_DESCRIPTION("PMIC8058 Thermal Manager driver");
508MODULE_VERSION("1.0");
509MODULE_ALIAS("platform:pmic8058-tm");