blob: c53bcd23e2db44fc8697acdacfb7a90348d49c97 [file] [log] [blame]
Abhijeet Dharmapurikarf154bfc2013-01-23 00:24:58 -08001/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08002 *
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#define pr_fmt(fmt) "%s: " fmt, __func__
14
15#include <linux/module.h>
16#include <linux/moduleparam.h>
17#include <linux/platform_device.h>
18#include <linux/errno.h>
19#include <linux/mfd/pm8xxx/core.h>
Xiaozhe Shid035f6a2012-12-18 16:16:33 -080020#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080021#include <linux/mfd/pm8xxx/ccadc.h>
22#include <linux/interrupt.h>
Anirudh Ghayal1d117472012-12-04 12:41:53 +053023#include <linux/irq.h>
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080024#include <linux/ioport.h>
25#include <linux/debugfs.h>
26#include <linux/slab.h>
27#include <linux/delay.h>
Xiaozhe Shid035f6a2012-12-18 16:16:33 -080028#include <linux/rtc.h>
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080029
30#define CCADC_ANA_PARAM 0x240
31#define CCADC_DIG_PARAM 0x241
32#define CCADC_RSV 0x242
33#define CCADC_DATA0 0x244
34#define CCADC_DATA1 0x245
35#define CCADC_OFFSET_TRIM1 0x34A
36#define CCADC_OFFSET_TRIM0 0x34B
37#define CCADC_FULLSCALE_TRIM1 0x34C
38#define CCADC_FULLSCALE_TRIM0 0x34D
39
40/* note : TRIM1 is the msb and TRIM0 is the lsb */
41#define ADC_ARB_SECP_CNTRL 0x190
42#define ADC_ARB_SECP_AMUX_CNTRL 0x191
43#define ADC_ARB_SECP_ANA_PARAM 0x192
44#define ADC_ARB_SECP_DIG_PARAM 0x193
45#define ADC_ARB_SECP_RSV 0x194
46#define ADC_ARB_SECP_DATA1 0x195
47#define ADC_ARB_SECP_DATA0 0x196
48
49#define ADC_ARB_BMS_CNTRL 0x18D
50
51#define START_CONV_BIT BIT(7)
52#define EOC_CONV_BIT BIT(6)
53#define SEL_CCADC_BIT BIT(1)
54#define EN_ARB_BIT BIT(0)
55
56#define CCADC_CALIB_DIG_PARAM 0xE3
57#define CCADC_CALIB_RSV_GND 0x40
58#define CCADC_CALIB_RSV_25MV 0x80
59#define CCADC_CALIB_ANA_PARAM 0x1B
60#define SAMPLE_COUNT 16
61#define ADC_WAIT_COUNT 10
62
63#define CCADC_MAX_25MV 30000
64#define CCADC_MIN_25MV 20000
65#define CCADC_MAX_0UV -4000
66#define CCADC_MIN_0UV -7000
67
68#define CCADC_INTRINSIC_OFFSET 0xC000
69
70struct pm8xxx_ccadc_chip {
71 struct device *dev;
72 struct dentry *dent;
Xiaozhe Shid035f6a2012-12-18 16:16:33 -080073 unsigned int batt_temp_channel;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080074 u16 ccadc_offset;
75 int ccadc_gain_uv;
76 unsigned int revision;
David Keitel3c378822012-06-07 13:43:22 -070077 unsigned int calib_delay_ms;
Xiaozhe Shid035f6a2012-12-18 16:16:33 -080078 unsigned long last_calib_time;
79 int last_calib_temp;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080080 int eoc_irq;
Xiaozhe Shid69c91e2012-11-06 10:00:38 -080081 int r_sense_uohm;
David Keitel3c378822012-06-07 13:43:22 -070082 struct delayed_work calib_ccadc_work;
Abhijeet Dharmapurikarf154bfc2013-01-23 00:24:58 -080083 struct mutex calib_mutex;
Abhijeet Dharmapurikar5389ebc2013-03-10 06:55:43 -070084 bool periodic_wakeup;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080085};
86
87static struct pm8xxx_ccadc_chip *the_chip;
88
Abhijeet Dharmapurikared8e5fb2011-12-07 14:31:26 -080089#ifdef DEBUG
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080090static s64 microvolt_to_ccadc_reading(struct pm8xxx_ccadc_chip *chip, s64 cc)
91{
Abhijeet Dharmapurikar305c5292012-06-21 16:15:43 -070092 return div_s64(uv * CCADC_READING_RESOLUTION_D,
93 CCADC_READING_RESOLUTION_N);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080094}
Abhijeet Dharmapurikared8e5fb2011-12-07 14:31:26 -080095#endif
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080096
97static int cc_adjust_for_offset(u16 raw)
98{
99 /* this has the intrinsic offset */
100 return (int)raw - the_chip->ccadc_offset;
101}
102
103#define GAIN_REFERENCE_UV 25000
104/*
105 * gain compensation for ccadc readings - common for vsense based and
106 * couloumb counter based readings
107 */
108s64 pm8xxx_cc_adjust_for_gain(s64 uv)
109{
110 if (the_chip == NULL || the_chip->ccadc_gain_uv == 0)
111 return uv;
112
113 return div_s64(uv * GAIN_REFERENCE_UV, the_chip->ccadc_gain_uv);
114}
115EXPORT_SYMBOL(pm8xxx_cc_adjust_for_gain);
116
117static int pm_ccadc_masked_write(struct pm8xxx_ccadc_chip *chip, u16 addr,
118 u8 mask, u8 val)
119{
120 int rc;
121 u8 reg;
122
123 rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
124 if (rc) {
125 pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
126 return rc;
127 }
128 reg &= ~mask;
129 reg |= val & mask;
130 rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
131 if (rc) {
132 pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
133 return rc;
134 }
135 return 0;
136}
137
138#define REG_SBI_CONFIG 0x04F
139#define PAGE3_ENABLE_MASK 0x6
140static int calib_ccadc_enable_trim_access(struct pm8xxx_ccadc_chip *chip,
141 u8 *sbi_config)
142{
143 u8 reg;
144 int rc;
145
146 rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
147 if (rc) {
148 pr_err("error = %d reading sbi config reg\n", rc);
149 return rc;
150 }
151
152 reg = *sbi_config | PAGE3_ENABLE_MASK;
153 return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
154}
155
156static int calib_ccadc_restore_trim_access(struct pm8xxx_ccadc_chip *chip,
157 u8 sbi_config)
158{
159 return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
160}
161
162static int calib_ccadc_enable_arbiter(struct pm8xxx_ccadc_chip *chip)
163{
164 int rc;
165
166 /* enable Arbiter, must be sent twice */
167 rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
168 SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
169 if (rc < 0) {
170 pr_err("error = %d enabling arbiter for offset\n", rc);
171 return rc;
172 }
173 rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
174 SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
175 if (rc < 0) {
176 pr_err("error = %d writing ADC_ARB_SECP_CNTRL\n", rc);
177 return rc;
178 }
179 return 0;
180}
181
182static int calib_start_conv(struct pm8xxx_ccadc_chip *chip,
183 u16 *result)
184{
185 int rc, i;
186 u8 data_msb, data_lsb, reg;
187
188 /* Start conversion */
189 rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
190 START_CONV_BIT, START_CONV_BIT);
191 if (rc < 0) {
192 pr_err("error = %d starting offset meas\n", rc);
193 return rc;
194 }
195
196 /* Wait for End of conversion */
197 for (i = 0; i < ADC_WAIT_COUNT; i++) {
198 rc = pm8xxx_readb(chip->dev->parent,
199 ADC_ARB_SECP_CNTRL, &reg);
200 if (rc < 0) {
201 pr_err("error = %d read eoc for offset\n", rc);
202 return rc;
203 }
204 if ((reg & (START_CONV_BIT | EOC_CONV_BIT)) != EOC_CONV_BIT)
Abhijeet Dharmapurikarccfc4f32012-01-16 17:35:18 -0800205 msleep(20);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800206 else
207 break;
208 }
209 if (i == ADC_WAIT_COUNT) {
David Keiteleb380812012-04-09 18:34:12 -0700210 pr_err("waited too long for offset eoc returning -EBUSY\n");
211 return -EBUSY;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800212 }
213
214 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA0, &data_lsb);
215 if (rc < 0) {
216 pr_err("error = %d reading offset lsb\n", rc);
217 return rc;
218 }
219
220 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA1, &data_msb);
221 if (rc < 0) {
222 pr_err("error = %d reading offset msb\n", rc);
223 return rc;
224 }
225
226 *result = (data_msb << 8) | data_lsb;
227 return 0;
228}
229
230static int calib_ccadc_read_trim(struct pm8xxx_ccadc_chip *chip,
231 int addr, u8 *data_msb, u8 *data_lsb)
232{
233 int rc;
234 u8 sbi_config;
235
236 calib_ccadc_enable_trim_access(chip, &sbi_config);
237 rc = pm8xxx_readb(chip->dev->parent, addr, data_msb);
238 if (rc < 0) {
239 pr_err("error = %d read msb\n", rc);
240 return rc;
241 }
242 rc = pm8xxx_readb(chip->dev->parent, addr + 1, data_lsb);
243 if (rc < 0) {
244 pr_err("error = %d read lsb\n", rc);
245 return rc;
246 }
247 calib_ccadc_restore_trim_access(chip, sbi_config);
248 return 0;
249}
250
251static void calib_ccadc_read_offset_and_gain(struct pm8xxx_ccadc_chip *chip,
252 int *gain, u16 *offset)
253{
Abhijeet Dharmapurikar034a0342011-12-08 11:12:54 -0800254 u8 data_msb;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800255 u8 data_lsb;
256 int rc;
257
258 rc = calib_ccadc_read_trim(chip, CCADC_FULLSCALE_TRIM1,
259 &data_msb, &data_lsb);
260 *gain = (data_msb << 8) | data_lsb;
261
262 rc = calib_ccadc_read_trim(chip, CCADC_OFFSET_TRIM1,
263 &data_msb, &data_lsb);
264 *offset = (data_msb << 8) | data_lsb;
265
266 pr_debug("raw gain trim = 0x%x offset trim =0x%x\n", *gain, *offset);
267 *gain = pm8xxx_ccadc_reading_to_microvolt(chip->revision,
268 (s64)*gain - *offset);
269 pr_debug("gain uv = %duV offset=0x%x\n", *gain, *offset);
270}
271
272#define CCADC_PROGRAM_TRIM_COUNT 2
273#define ADC_ARB_BMS_CNTRL_CCADC_SHIFT 4
274#define ADC_ARB_BMS_CNTRL_CONV_MASK 0x03
275#define BMS_CONV_IN_PROGRESS 0x2
276
277static int calib_ccadc_program_trim(struct pm8xxx_ccadc_chip *chip,
278 int addr, u8 data_msb, u8 data_lsb,
279 int wait)
280{
281 int i, rc, loop;
282 u8 cntrl, sbi_config;
283 bool in_progress = 0;
284
285 loop = wait ? CCADC_PROGRAM_TRIM_COUNT : 0;
286
287 calib_ccadc_enable_trim_access(chip, &sbi_config);
288
289 for (i = 0; i < loop; i++) {
290 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_BMS_CNTRL, &cntrl);
291 if (rc < 0) {
292 pr_err("error = %d reading ADC_ARB_BMS_CNTRL\n", rc);
293 return rc;
294 }
295
296 /* break if a ccadc conversion is not happening */
297 in_progress = (((cntrl >> ADC_ARB_BMS_CNTRL_CCADC_SHIFT)
298 & ADC_ARB_BMS_CNTRL_CONV_MASK) == BMS_CONV_IN_PROGRESS);
299
300 if (!in_progress)
301 break;
302 }
303
304 if (in_progress) {
305 pr_debug("conv in progress cannot write trim,returing EBUSY\n");
306 return -EBUSY;
307 }
308
309 rc = pm8xxx_writeb(chip->dev->parent, addr, data_msb);
310 if (rc < 0) {
311 pr_err("error = %d write msb = 0x%x\n", rc, data_msb);
312 return rc;
313 }
314 rc = pm8xxx_writeb(chip->dev->parent, addr + 1, data_lsb);
315 if (rc < 0) {
316 pr_err("error = %d write lsb = 0x%x\n", rc, data_lsb);
317 return rc;
318 }
319 calib_ccadc_restore_trim_access(chip, sbi_config);
320 return 0;
321}
322
Xiaozhe Shid035f6a2012-12-18 16:16:33 -0800323static int get_batt_temp(struct pm8xxx_ccadc_chip *chip, int *batt_temp)
324{
325 int rc;
326 struct pm8xxx_adc_chan_result result;
327
328 rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
329 if (rc) {
330 pr_err("error reading batt_temp_channel = %d, rc = %d\n",
331 chip->batt_temp_channel, rc);
332 return rc;
333 }
334 *batt_temp = result.physical;
335 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
336 result.measurement);
337 return 0;
338}
339
340static int get_current_time(unsigned long *now_tm_sec)
341{
342 struct rtc_time tm;
343 struct rtc_device *rtc;
344 int rc;
345
346 rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
347 if (rtc == NULL) {
348 pr_err("%s: unable to open rtc device (%s)\n",
349 __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
350 return -EINVAL;
351 }
352
353 rc = rtc_read_time(rtc, &tm);
354 if (rc) {
355 pr_err("Error reading rtc device (%s) : %d\n",
356 CONFIG_RTC_HCTOSYS_DEVICE, rc);
357 return rc;
358 }
359
360 rc = rtc_valid_tm(&tm);
361 if (rc) {
362 pr_err("Invalid RTC time (%s): %d\n",
363 CONFIG_RTC_HCTOSYS_DEVICE, rc);
364 return rc;
365 }
366 rtc_tm_to_time(&tm, now_tm_sec);
367
368 return 0;
369}
370
Abhijeet Dharmapurikar5389ebc2013-03-10 06:55:43 -0700371static void __pm8xxx_calib_ccadc(int sample_count)
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800372{
373 u8 data_msb, data_lsb, sec_cntrl;
Abhijeet Dharmapurikared8e5fb2011-12-07 14:31:26 -0800374 int result_offset, result_gain;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800375 u16 result;
376 int i, rc;
377
David Keitel3c378822012-06-07 13:43:22 -0700378 if (!the_chip) {
379 pr_err("chip not initialized\n");
380 return;
381 }
382
Abhijeet Dharmapurikar5389ebc2013-03-10 06:55:43 -0700383 pr_debug("sample_count = %d\n", sample_count);
384
Abhijeet Dharmapurikarf154bfc2013-01-23 00:24:58 -0800385 mutex_lock(&the_chip->calib_mutex);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800386 rc = pm8xxx_readb(the_chip->dev->parent,
387 ADC_ARB_SECP_CNTRL, &sec_cntrl);
388 if (rc < 0) {
389 pr_err("error = %d reading ADC_ARB_SECP_CNTRL\n", rc);
Abhijeet Dharmapurikarf154bfc2013-01-23 00:24:58 -0800390 goto calibration_unlock;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800391 }
392
393 rc = calib_ccadc_enable_arbiter(the_chip);
394 if (rc < 0) {
395 pr_err("error = %d enabling arbiter for offset\n", rc);
396 goto bail;
397 }
398
399 /*
400 * Set decimation ratio to 4k, lower ratio may be used in order to speed
401 * up, pending verification through bench
402 */
403 rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
404 CCADC_CALIB_DIG_PARAM);
405 if (rc < 0) {
406 pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
407 goto bail;
408 }
409
410 result_offset = 0;
Abhijeet Dharmapurikar5389ebc2013-03-10 06:55:43 -0700411 for (i = 0; i < sample_count; i++) {
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800412 /* Short analog inputs to CCADC internally to ground */
413 rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_RSV,
414 CCADC_CALIB_RSV_GND);
415 if (rc < 0) {
416 pr_err("error = %d selecting gnd voltage\n", rc);
417 goto bail;
418 }
419
420 /* Enable CCADC */
421 rc = pm8xxx_writeb(the_chip->dev->parent,
422 ADC_ARB_SECP_ANA_PARAM, CCADC_CALIB_ANA_PARAM);
423 if (rc < 0) {
424 pr_err("error = %d enabling ccadc\n", rc);
425 goto bail;
426 }
427
428 rc = calib_start_conv(the_chip, &result);
429 if (rc < 0) {
430 pr_err("error = %d for zero volt measurement\n", rc);
431 goto bail;
432 }
433
434 result_offset += result;
435 }
436
Abhijeet Dharmapurikar5389ebc2013-03-10 06:55:43 -0700437 result_offset = result_offset / sample_count;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800438
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800439
Abhijeet Dharmapurikared8e5fb2011-12-07 14:31:26 -0800440 pr_debug("offset result_offset = 0x%x, voltage = %llduV\n",
441 result_offset,
442 pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
443 ((s64)result_offset - CCADC_INTRINSIC_OFFSET)));
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800444
445 the_chip->ccadc_offset = result_offset;
446 data_msb = the_chip->ccadc_offset >> 8;
447 data_lsb = the_chip->ccadc_offset;
448
449 rc = calib_ccadc_program_trim(the_chip, CCADC_OFFSET_TRIM1,
450 data_msb, data_lsb, 1);
451 if (rc) {
452 pr_debug("error = %d programming offset trim 0x%02x 0x%02x\n",
453 rc, data_msb, data_lsb);
454 /* enable the interrupt and write it when it fires */
455 enable_irq(the_chip->eoc_irq);
456 }
457
458 rc = calib_ccadc_enable_arbiter(the_chip);
459 if (rc < 0) {
460 pr_err("error = %d enabling arbiter for gain\n", rc);
461 goto bail;
462 }
463
464 /*
465 * Set decimation ratio to 4k, lower ratio may be used in order to speed
466 * up, pending verification through bench
467 */
468 rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
469 CCADC_CALIB_DIG_PARAM);
470 if (rc < 0) {
471 pr_err("error = %d enabling decimation ration for gain\n", rc);
472 goto bail;
473 }
474
475 result_gain = 0;
Abhijeet Dharmapurikar5389ebc2013-03-10 06:55:43 -0700476 for (i = 0; i < sample_count; i++) {
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800477 rc = pm8xxx_writeb(the_chip->dev->parent,
478 ADC_ARB_SECP_RSV, CCADC_CALIB_RSV_25MV);
479 if (rc < 0) {
480 pr_err("error = %d selecting 25mV for gain\n", rc);
481 goto bail;
482 }
483
484 /* Enable CCADC */
485 rc = pm8xxx_writeb(the_chip->dev->parent,
486 ADC_ARB_SECP_ANA_PARAM, CCADC_CALIB_ANA_PARAM);
487 if (rc < 0) {
488 pr_err("error = %d enabling ccadc\n", rc);
489 goto bail;
490 }
491
492 rc = calib_start_conv(the_chip, &result);
493 if (rc < 0) {
494 pr_err("error = %d for adc reading 25mV\n", rc);
495 goto bail;
496 }
497
498 result_gain += result;
499 }
Abhijeet Dharmapurikar5389ebc2013-03-10 06:55:43 -0700500 result_gain = result_gain / sample_count;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800501
502 /*
503 * result_offset includes INTRINSIC OFFSET
504 * the_chip->ccadc_gain_uv will be the actual voltage
505 * measured for 25000UV
506 */
507 the_chip->ccadc_gain_uv = pm8xxx_ccadc_reading_to_microvolt(
508 the_chip->revision,
509 ((s64)result_gain - result_offset));
510
511 pr_debug("gain result_gain = 0x%x, voltage = %d microVolts\n",
512 result_gain, the_chip->ccadc_gain_uv);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800513
514 data_msb = result_gain >> 8;
515 data_lsb = result_gain;
516 rc = calib_ccadc_program_trim(the_chip, CCADC_FULLSCALE_TRIM1,
517 data_msb, data_lsb, 0);
518 if (rc)
519 pr_debug("error = %d programming gain trim\n", rc);
520bail:
521 pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_CNTRL, sec_cntrl);
Abhijeet Dharmapurikarf154bfc2013-01-23 00:24:58 -0800522calibration_unlock:
523 mutex_unlock(&the_chip->calib_mutex);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800524}
Abhijeet Dharmapurikar5389ebc2013-03-10 06:55:43 -0700525
526static void pm8xxx_calib_ccadc_quick(void)
527{
528 __pm8xxx_calib_ccadc(2);
529}
530
531void pm8xxx_calib_ccadc(void)
532{
533 __pm8xxx_calib_ccadc(SAMPLE_COUNT);
534}
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800535EXPORT_SYMBOL(pm8xxx_calib_ccadc);
536
David Keitel3c378822012-06-07 13:43:22 -0700537static void calibrate_ccadc_work(struct work_struct *work)
538{
539 struct pm8xxx_ccadc_chip *chip = container_of(work,
540 struct pm8xxx_ccadc_chip, calib_ccadc_work.work);
541
542 pm8xxx_calib_ccadc();
543 schedule_delayed_work(&chip->calib_ccadc_work,
544 round_jiffies_relative(msecs_to_jiffies
545 (chip->calib_delay_ms)));
546}
547
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800548static irqreturn_t pm8921_bms_ccadc_eoc_handler(int irq, void *data)
549{
550 u8 data_msb, data_lsb;
551 struct pm8xxx_ccadc_chip *chip = data;
552 int rc;
553
Abhijeet Dharmapurikar1b817342012-10-11 11:24:50 -0700554 if (!the_chip)
555 goto out;
556
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800557 pr_debug("irq = %d triggered\n", irq);
558 data_msb = chip->ccadc_offset >> 8;
559 data_lsb = chip->ccadc_offset;
560
561 rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
562 data_msb, data_lsb, 0);
563 disable_irq_nosync(chip->eoc_irq);
564
Abhijeet Dharmapurikar1b817342012-10-11 11:24:50 -0700565out:
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800566 return IRQ_HANDLED;
567}
568
569#define CCADC_IBAT_DIG_PARAM 0xA3
570#define CCADC_IBAT_RSV 0x10
571#define CCADC_IBAT_ANA_PARAM 0x1A
572static int ccadc_get_rsense_voltage(int *voltage_uv)
573{
574 u16 raw;
575 int result;
576 int rc = 0;
577
578 rc = calib_ccadc_enable_arbiter(the_chip);
579 if (rc < 0) {
580 pr_err("error = %d enabling arbiter for offset\n", rc);
581 return rc;
582 }
583
584 rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
585 CCADC_IBAT_DIG_PARAM);
586 if (rc < 0) {
587 pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
588 return rc;
589 }
590
591 rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_RSV,
592 CCADC_IBAT_RSV);
593 if (rc < 0) {
594 pr_err("error = %d selecting rsense\n", rc);
595 return rc;
596 }
597
598 rc = pm8xxx_writeb(the_chip->dev->parent,
599 ADC_ARB_SECP_ANA_PARAM, CCADC_IBAT_ANA_PARAM);
600 if (rc < 0) {
601 pr_err("error = %d enabling ccadc\n", rc);
602 return rc;
603 }
604
605 rc = calib_start_conv(the_chip, &raw);
606 if (rc < 0) {
607 pr_err("error = %d for zero volt measurement\n", rc);
608 return rc;
609 }
610
611 pr_debug("Vsense raw = 0x%x\n", raw);
612 result = cc_adjust_for_offset(raw);
613 pr_debug("Vsense after offset raw = 0x%x offset=0x%x\n",
614 result,
615 the_chip->ccadc_offset);
616 *voltage_uv = pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
617 ((s64)result));
Abhijeet Dharmapurikar034a0342011-12-08 11:12:54 -0800618 pr_debug("Vsense before gain of %d = %d uV\n", the_chip->ccadc_gain_uv,
619 *voltage_uv);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800620 *voltage_uv = pm8xxx_cc_adjust_for_gain(*voltage_uv);
621
622 pr_debug("Vsense = %d uV\n", *voltage_uv);
623 return 0;
624}
625
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800626int pm8xxx_ccadc_get_battery_current(int *bat_current_ua)
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800627{
Trilok Sonic6e541e2012-03-19 17:14:28 +0530628 int voltage_uv = 0, rc;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800629
630 rc = ccadc_get_rsense_voltage(&voltage_uv);
631 if (rc) {
632 pr_err("cant get voltage across rsense rc = %d\n", rc);
633 return rc;
634 }
635
Xiaozhe Shid69c91e2012-11-06 10:00:38 -0800636 *bat_current_ua = div_s64((s64)voltage_uv * 1000000LL,
637 the_chip->r_sense_uohm);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800638 /*
639 * ccadc reads +ve current when the battery is charging
640 * We need to return -ve if the battery is charging
641 */
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800642 *bat_current_ua = -1 * (*bat_current_ua);
643 pr_debug("bat current = %d ma\n", *bat_current_ua);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800644 return 0;
645}
646EXPORT_SYMBOL(pm8xxx_ccadc_get_battery_current);
647
648static int get_reg(void *data, u64 * val)
649{
650 int addr = (int)data;
651 int ret;
652 u8 temp;
653
654 ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
655 if (ret) {
656 pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
657 addr, temp, ret);
658 return -EAGAIN;
659 }
660 *val = temp;
661 return 0;
662}
663
664static int set_reg(void *data, u64 val)
665{
666 int addr = (int)data;
667 int ret;
668 u8 temp;
669
670 temp = (u8) val;
671 ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
672 if (ret) {
673 pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
674 addr, temp, ret);
675 return -EAGAIN;
676 }
677 return 0;
678}
679DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
680
681static int get_calc(void *data, u64 * val)
682{
683 int ibat, rc;
684
685 rc = pm8xxx_ccadc_get_battery_current(&ibat);
686 *val = ibat;
687 return rc;
688}
689DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%lld\n");
690
691static void create_debugfs_entries(struct pm8xxx_ccadc_chip *chip)
692{
693 chip->dent = debugfs_create_dir("pm8xxx-ccadc", NULL);
694
695 if (IS_ERR(chip->dent)) {
696 pr_err("ccadc couldnt create debugfs dir\n");
697 return;
698 }
699
700 debugfs_create_file("CCADC_ANA_PARAM", 0644, chip->dent,
701 (void *)CCADC_ANA_PARAM, &reg_fops);
702 debugfs_create_file("CCADC_DIG_PARAM", 0644, chip->dent,
703 (void *)CCADC_DIG_PARAM, &reg_fops);
704 debugfs_create_file("CCADC_RSV", 0644, chip->dent,
705 (void *)CCADC_RSV, &reg_fops);
706 debugfs_create_file("CCADC_DATA0", 0644, chip->dent,
707 (void *)CCADC_DATA0, &reg_fops);
708 debugfs_create_file("CCADC_DATA1", 0644, chip->dent,
709 (void *)CCADC_DATA1, &reg_fops);
710 debugfs_create_file("CCADC_OFFSET_TRIM1", 0644, chip->dent,
711 (void *)CCADC_OFFSET_TRIM1, &reg_fops);
712 debugfs_create_file("CCADC_OFFSET_TRIM0", 0644, chip->dent,
713 (void *)CCADC_OFFSET_TRIM0, &reg_fops);
714 debugfs_create_file("CCADC_FULLSCALE_TRIM1", 0644, chip->dent,
715 (void *)CCADC_FULLSCALE_TRIM1, &reg_fops);
716 debugfs_create_file("CCADC_FULLSCALE_TRIM0", 0644, chip->dent,
717 (void *)CCADC_FULLSCALE_TRIM0, &reg_fops);
718
719 debugfs_create_file("show_ibatt", 0644, chip->dent,
720 (void *)0, &calc_fops);
721}
722
723static int __devinit pm8xxx_ccadc_probe(struct platform_device *pdev)
724{
725 int rc = 0;
726 struct pm8xxx_ccadc_chip *chip;
727 struct resource *res;
728 const struct pm8xxx_ccadc_platform_data *pdata
729 = pdev->dev.platform_data;
730
731 if (!pdata) {
732 pr_err("missing platform data\n");
733 return -EINVAL;
734 }
735 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
736 "PM8921_BMS_CCADC_EOC");
737 if (!res) {
738 pr_err("failed to get irq\n");
739 return -EINVAL;
740 }
741
742 chip = kzalloc(sizeof(struct pm8xxx_ccadc_chip), GFP_KERNEL);
743 if (!chip) {
744 pr_err("Cannot allocate pm_bms_chip\n");
745 return -ENOMEM;
746 }
747 chip->dev = &pdev->dev;
748 chip->revision = pm8xxx_get_revision(chip->dev->parent);
749 chip->eoc_irq = res->start;
Xiaozhe Shid69c91e2012-11-06 10:00:38 -0800750 chip->r_sense_uohm = pdata->r_sense_uohm;
David Keitel3c378822012-06-07 13:43:22 -0700751 chip->calib_delay_ms = pdata->calib_delay_ms;
Xiaozhe Shid035f6a2012-12-18 16:16:33 -0800752 chip->batt_temp_channel = pdata->ccadc_cdata.batt_temp_channel;
Abhijeet Dharmapurikar5389ebc2013-03-10 06:55:43 -0700753 chip->periodic_wakeup = pdata->periodic_wakeup;
Abhijeet Dharmapurikarf154bfc2013-01-23 00:24:58 -0800754 mutex_init(&chip->calib_mutex);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800755
Abhijeet Dharmapurikar97d40902011-11-30 12:12:51 -0800756 calib_ccadc_read_offset_and_gain(chip,
757 &chip->ccadc_gain_uv,
758 &chip->ccadc_offset);
Anirudh Ghayal1d117472012-12-04 12:41:53 +0530759 irq_set_status_flags(chip->eoc_irq, IRQ_NOAUTOEN);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800760 rc = request_irq(chip->eoc_irq,
761 pm8921_bms_ccadc_eoc_handler, IRQF_TRIGGER_RISING,
762 "bms_eoc_ccadc", chip);
763 if (rc) {
764 pr_err("failed to request %d irq rc= %d\n", chip->eoc_irq, rc);
765 goto free_chip;
766 }
David Keitel3c378822012-06-07 13:43:22 -0700767
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800768 platform_set_drvdata(pdev, chip);
769 the_chip = chip;
Abhijeet Dharmapurikarfbefde22012-07-12 14:39:13 -0700770 INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work);
771 schedule_delayed_work(&chip->calib_ccadc_work, 0);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800772
773 create_debugfs_entries(chip);
774
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800775 return 0;
776
777free_chip:
Abhijeet Dharmapurikarf154bfc2013-01-23 00:24:58 -0800778 mutex_destroy(&chip->calib_mutex);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800779 kfree(chip);
780 return rc;
781}
782
783static int __devexit pm8xxx_ccadc_remove(struct platform_device *pdev)
784{
785 struct pm8xxx_ccadc_chip *chip = platform_get_drvdata(pdev);
786
787 debugfs_remove_recursive(chip->dent);
788 the_chip = NULL;
789 kfree(chip);
790 return 0;
791}
792
Anirudh Ghayal44fde2a2013-05-10 11:02:53 +0530793static int pm8xxx_ccadc_suspend(struct device *dev)
794{
795 struct pm8xxx_ccadc_chip *chip = dev_get_drvdata(dev);
796
797 cancel_delayed_work_sync(&chip->calib_ccadc_work);
798
799 return 0;
800}
801
Xiaozhe Shid035f6a2012-12-18 16:16:33 -0800802#define CCADC_CALIB_TEMP_THRESH 20
803static int pm8xxx_ccadc_resume(struct device *dev)
804{
805 int rc, batt_temp, delta_temp;
806 unsigned long current_time_sec;
807 unsigned long time_since_last_calib;
808
809 rc = get_batt_temp(the_chip, &batt_temp);
810 if (rc) {
811 pr_err("unable to get batt_temp: %d\n", rc);
812 return 0;
813 }
814 rc = get_current_time(&current_time_sec);
815 if (rc) {
816 pr_err("unable to get current time: %d\n", rc);
817 return 0;
818 }
Abhijeet Dharmapurikar5389ebc2013-03-10 06:55:43 -0700819
820 if (the_chip->periodic_wakeup) {
821 pm8xxx_calib_ccadc_quick();
822 return 0;
823 }
824
Xiaozhe Shid035f6a2012-12-18 16:16:33 -0800825 if (current_time_sec > the_chip->last_calib_time) {
826 time_since_last_calib = current_time_sec -
827 the_chip->last_calib_time;
828 delta_temp = abs(batt_temp - the_chip->last_calib_temp);
829 pr_debug("time since last calib: %lu, delta_temp = %d\n",
830 time_since_last_calib, delta_temp);
831 if (time_since_last_calib >= the_chip->calib_delay_ms/1000
832 || delta_temp > CCADC_CALIB_TEMP_THRESH) {
833 the_chip->last_calib_time = current_time_sec;
834 the_chip->last_calib_temp = batt_temp;
Abhijeet Dharmapurikar5389ebc2013-03-10 06:55:43 -0700835 schedule_delayed_work(&the_chip->calib_ccadc_work, 0);
Anirudh Ghayal44fde2a2013-05-10 11:02:53 +0530836 } else {
837 schedule_delayed_work(&the_chip->calib_ccadc_work,
838 msecs_to_jiffies(the_chip->calib_delay_ms -
839 (time_since_last_calib * 1000)));
Xiaozhe Shid035f6a2012-12-18 16:16:33 -0800840 }
841 }
Abhijeet Dharmapurikar5389ebc2013-03-10 06:55:43 -0700842
Xiaozhe Shid035f6a2012-12-18 16:16:33 -0800843 return 0;
844}
845
846static const struct dev_pm_ops pm8xxx_ccadc_pm_ops = {
Anirudh Ghayal44fde2a2013-05-10 11:02:53 +0530847 .suspend = pm8xxx_ccadc_suspend,
Xiaozhe Shid035f6a2012-12-18 16:16:33 -0800848 .resume = pm8xxx_ccadc_resume,
849};
850
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800851static struct platform_driver pm8xxx_ccadc_driver = {
852 .probe = pm8xxx_ccadc_probe,
853 .remove = __devexit_p(pm8xxx_ccadc_remove),
854 .driver = {
855 .name = PM8XXX_CCADC_DEV_NAME,
856 .owner = THIS_MODULE,
Xiaozhe Shid035f6a2012-12-18 16:16:33 -0800857 .pm = &pm8xxx_ccadc_pm_ops,
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800858 },
859};
860
861static int __init pm8xxx_ccadc_init(void)
862{
863 return platform_driver_register(&pm8xxx_ccadc_driver);
864}
865
866static void __exit pm8xxx_ccadc_exit(void)
867{
868 platform_driver_unregister(&pm8xxx_ccadc_driver);
869}
870
871module_init(pm8xxx_ccadc_init);
872module_exit(pm8xxx_ccadc_exit);
873
874MODULE_LICENSE("GPL v2");
875MODULE_DESCRIPTION("PMIC8XXX ccadc driver");
876MODULE_VERSION("1.0");
877MODULE_ALIAS("platform:" PM8XXX_CCADC_DEV_NAME);