blob: 7e37daa4ed12b20dd9b66e5c57223067d06c48ce [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 Dharmapurikar82d93982011-11-09 15:52:25 -080084};
85
86static struct pm8xxx_ccadc_chip *the_chip;
87
Abhijeet Dharmapurikared8e5fb2011-12-07 14:31:26 -080088#ifdef DEBUG
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080089static s64 microvolt_to_ccadc_reading(struct pm8xxx_ccadc_chip *chip, s64 cc)
90{
Abhijeet Dharmapurikar305c5292012-06-21 16:15:43 -070091 return div_s64(uv * CCADC_READING_RESOLUTION_D,
92 CCADC_READING_RESOLUTION_N);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080093}
Abhijeet Dharmapurikared8e5fb2011-12-07 14:31:26 -080094#endif
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080095
96static int cc_adjust_for_offset(u16 raw)
97{
98 /* this has the intrinsic offset */
99 return (int)raw - the_chip->ccadc_offset;
100}
101
102#define GAIN_REFERENCE_UV 25000
103/*
104 * gain compensation for ccadc readings - common for vsense based and
105 * couloumb counter based readings
106 */
107s64 pm8xxx_cc_adjust_for_gain(s64 uv)
108{
109 if (the_chip == NULL || the_chip->ccadc_gain_uv == 0)
110 return uv;
111
112 return div_s64(uv * GAIN_REFERENCE_UV, the_chip->ccadc_gain_uv);
113}
114EXPORT_SYMBOL(pm8xxx_cc_adjust_for_gain);
115
116static int pm_ccadc_masked_write(struct pm8xxx_ccadc_chip *chip, u16 addr,
117 u8 mask, u8 val)
118{
119 int rc;
120 u8 reg;
121
122 rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
123 if (rc) {
124 pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
125 return rc;
126 }
127 reg &= ~mask;
128 reg |= val & mask;
129 rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
130 if (rc) {
131 pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
132 return rc;
133 }
134 return 0;
135}
136
137#define REG_SBI_CONFIG 0x04F
138#define PAGE3_ENABLE_MASK 0x6
139static int calib_ccadc_enable_trim_access(struct pm8xxx_ccadc_chip *chip,
140 u8 *sbi_config)
141{
142 u8 reg;
143 int rc;
144
145 rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
146 if (rc) {
147 pr_err("error = %d reading sbi config reg\n", rc);
148 return rc;
149 }
150
151 reg = *sbi_config | PAGE3_ENABLE_MASK;
152 return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
153}
154
155static int calib_ccadc_restore_trim_access(struct pm8xxx_ccadc_chip *chip,
156 u8 sbi_config)
157{
158 return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
159}
160
161static int calib_ccadc_enable_arbiter(struct pm8xxx_ccadc_chip *chip)
162{
163 int rc;
164
165 /* enable Arbiter, must be sent twice */
166 rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
167 SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
168 if (rc < 0) {
169 pr_err("error = %d enabling arbiter for offset\n", rc);
170 return rc;
171 }
172 rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
173 SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
174 if (rc < 0) {
175 pr_err("error = %d writing ADC_ARB_SECP_CNTRL\n", rc);
176 return rc;
177 }
178 return 0;
179}
180
181static int calib_start_conv(struct pm8xxx_ccadc_chip *chip,
182 u16 *result)
183{
184 int rc, i;
185 u8 data_msb, data_lsb, reg;
186
187 /* Start conversion */
188 rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
189 START_CONV_BIT, START_CONV_BIT);
190 if (rc < 0) {
191 pr_err("error = %d starting offset meas\n", rc);
192 return rc;
193 }
194
195 /* Wait for End of conversion */
196 for (i = 0; i < ADC_WAIT_COUNT; i++) {
197 rc = pm8xxx_readb(chip->dev->parent,
198 ADC_ARB_SECP_CNTRL, &reg);
199 if (rc < 0) {
200 pr_err("error = %d read eoc for offset\n", rc);
201 return rc;
202 }
203 if ((reg & (START_CONV_BIT | EOC_CONV_BIT)) != EOC_CONV_BIT)
Abhijeet Dharmapurikarccfc4f32012-01-16 17:35:18 -0800204 msleep(20);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800205 else
206 break;
207 }
208 if (i == ADC_WAIT_COUNT) {
David Keiteleb380812012-04-09 18:34:12 -0700209 pr_err("waited too long for offset eoc returning -EBUSY\n");
210 return -EBUSY;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800211 }
212
213 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA0, &data_lsb);
214 if (rc < 0) {
215 pr_err("error = %d reading offset lsb\n", rc);
216 return rc;
217 }
218
219 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA1, &data_msb);
220 if (rc < 0) {
221 pr_err("error = %d reading offset msb\n", rc);
222 return rc;
223 }
224
225 *result = (data_msb << 8) | data_lsb;
226 return 0;
227}
228
229static int calib_ccadc_read_trim(struct pm8xxx_ccadc_chip *chip,
230 int addr, u8 *data_msb, u8 *data_lsb)
231{
232 int rc;
233 u8 sbi_config;
234
235 calib_ccadc_enable_trim_access(chip, &sbi_config);
236 rc = pm8xxx_readb(chip->dev->parent, addr, data_msb);
237 if (rc < 0) {
238 pr_err("error = %d read msb\n", rc);
239 return rc;
240 }
241 rc = pm8xxx_readb(chip->dev->parent, addr + 1, data_lsb);
242 if (rc < 0) {
243 pr_err("error = %d read lsb\n", rc);
244 return rc;
245 }
246 calib_ccadc_restore_trim_access(chip, sbi_config);
247 return 0;
248}
249
250static void calib_ccadc_read_offset_and_gain(struct pm8xxx_ccadc_chip *chip,
251 int *gain, u16 *offset)
252{
Abhijeet Dharmapurikar034a0342011-12-08 11:12:54 -0800253 u8 data_msb;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800254 u8 data_lsb;
255 int rc;
256
257 rc = calib_ccadc_read_trim(chip, CCADC_FULLSCALE_TRIM1,
258 &data_msb, &data_lsb);
259 *gain = (data_msb << 8) | data_lsb;
260
261 rc = calib_ccadc_read_trim(chip, CCADC_OFFSET_TRIM1,
262 &data_msb, &data_lsb);
263 *offset = (data_msb << 8) | data_lsb;
264
265 pr_debug("raw gain trim = 0x%x offset trim =0x%x\n", *gain, *offset);
266 *gain = pm8xxx_ccadc_reading_to_microvolt(chip->revision,
267 (s64)*gain - *offset);
268 pr_debug("gain uv = %duV offset=0x%x\n", *gain, *offset);
269}
270
271#define CCADC_PROGRAM_TRIM_COUNT 2
272#define ADC_ARB_BMS_CNTRL_CCADC_SHIFT 4
273#define ADC_ARB_BMS_CNTRL_CONV_MASK 0x03
274#define BMS_CONV_IN_PROGRESS 0x2
275
276static int calib_ccadc_program_trim(struct pm8xxx_ccadc_chip *chip,
277 int addr, u8 data_msb, u8 data_lsb,
278 int wait)
279{
280 int i, rc, loop;
281 u8 cntrl, sbi_config;
282 bool in_progress = 0;
283
284 loop = wait ? CCADC_PROGRAM_TRIM_COUNT : 0;
285
286 calib_ccadc_enable_trim_access(chip, &sbi_config);
287
288 for (i = 0; i < loop; i++) {
289 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_BMS_CNTRL, &cntrl);
290 if (rc < 0) {
291 pr_err("error = %d reading ADC_ARB_BMS_CNTRL\n", rc);
292 return rc;
293 }
294
295 /* break if a ccadc conversion is not happening */
296 in_progress = (((cntrl >> ADC_ARB_BMS_CNTRL_CCADC_SHIFT)
297 & ADC_ARB_BMS_CNTRL_CONV_MASK) == BMS_CONV_IN_PROGRESS);
298
299 if (!in_progress)
300 break;
301 }
302
303 if (in_progress) {
304 pr_debug("conv in progress cannot write trim,returing EBUSY\n");
305 return -EBUSY;
306 }
307
308 rc = pm8xxx_writeb(chip->dev->parent, addr, data_msb);
309 if (rc < 0) {
310 pr_err("error = %d write msb = 0x%x\n", rc, data_msb);
311 return rc;
312 }
313 rc = pm8xxx_writeb(chip->dev->parent, addr + 1, data_lsb);
314 if (rc < 0) {
315 pr_err("error = %d write lsb = 0x%x\n", rc, data_lsb);
316 return rc;
317 }
318 calib_ccadc_restore_trim_access(chip, sbi_config);
319 return 0;
320}
321
Xiaozhe Shid035f6a2012-12-18 16:16:33 -0800322static int get_batt_temp(struct pm8xxx_ccadc_chip *chip, int *batt_temp)
323{
324 int rc;
325 struct pm8xxx_adc_chan_result result;
326
327 rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
328 if (rc) {
329 pr_err("error reading batt_temp_channel = %d, rc = %d\n",
330 chip->batt_temp_channel, rc);
331 return rc;
332 }
333 *batt_temp = result.physical;
334 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
335 result.measurement);
336 return 0;
337}
338
339static int get_current_time(unsigned long *now_tm_sec)
340{
341 struct rtc_time tm;
342 struct rtc_device *rtc;
343 int rc;
344
345 rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
346 if (rtc == NULL) {
347 pr_err("%s: unable to open rtc device (%s)\n",
348 __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
349 return -EINVAL;
350 }
351
352 rc = rtc_read_time(rtc, &tm);
353 if (rc) {
354 pr_err("Error reading rtc device (%s) : %d\n",
355 CONFIG_RTC_HCTOSYS_DEVICE, rc);
356 return rc;
357 }
358
359 rc = rtc_valid_tm(&tm);
360 if (rc) {
361 pr_err("Invalid RTC time (%s): %d\n",
362 CONFIG_RTC_HCTOSYS_DEVICE, rc);
363 return rc;
364 }
365 rtc_tm_to_time(&tm, now_tm_sec);
366
367 return 0;
368}
369
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800370void pm8xxx_calib_ccadc(void)
371{
372 u8 data_msb, data_lsb, sec_cntrl;
Abhijeet Dharmapurikared8e5fb2011-12-07 14:31:26 -0800373 int result_offset, result_gain;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800374 u16 result;
375 int i, rc;
376
David Keitel3c378822012-06-07 13:43:22 -0700377 if (!the_chip) {
378 pr_err("chip not initialized\n");
379 return;
380 }
381
Abhijeet Dharmapurikarf154bfc2013-01-23 00:24:58 -0800382 mutex_lock(&the_chip->calib_mutex);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800383 rc = pm8xxx_readb(the_chip->dev->parent,
384 ADC_ARB_SECP_CNTRL, &sec_cntrl);
385 if (rc < 0) {
386 pr_err("error = %d reading ADC_ARB_SECP_CNTRL\n", rc);
Abhijeet Dharmapurikarf154bfc2013-01-23 00:24:58 -0800387 goto calibration_unlock;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800388 }
389
390 rc = calib_ccadc_enable_arbiter(the_chip);
391 if (rc < 0) {
392 pr_err("error = %d enabling arbiter for offset\n", rc);
393 goto bail;
394 }
395
396 /*
397 * Set decimation ratio to 4k, lower ratio may be used in order to speed
398 * up, pending verification through bench
399 */
400 rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
401 CCADC_CALIB_DIG_PARAM);
402 if (rc < 0) {
403 pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
404 goto bail;
405 }
406
407 result_offset = 0;
408 for (i = 0; i < SAMPLE_COUNT; i++) {
409 /* Short analog inputs to CCADC internally to ground */
410 rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_RSV,
411 CCADC_CALIB_RSV_GND);
412 if (rc < 0) {
413 pr_err("error = %d selecting gnd voltage\n", rc);
414 goto bail;
415 }
416
417 /* Enable CCADC */
418 rc = pm8xxx_writeb(the_chip->dev->parent,
419 ADC_ARB_SECP_ANA_PARAM, CCADC_CALIB_ANA_PARAM);
420 if (rc < 0) {
421 pr_err("error = %d enabling ccadc\n", rc);
422 goto bail;
423 }
424
425 rc = calib_start_conv(the_chip, &result);
426 if (rc < 0) {
427 pr_err("error = %d for zero volt measurement\n", rc);
428 goto bail;
429 }
430
431 result_offset += result;
432 }
433
434 result_offset = result_offset / SAMPLE_COUNT;
435
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800436
Abhijeet Dharmapurikared8e5fb2011-12-07 14:31:26 -0800437 pr_debug("offset result_offset = 0x%x, voltage = %llduV\n",
438 result_offset,
439 pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
440 ((s64)result_offset - CCADC_INTRINSIC_OFFSET)));
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800441
442 the_chip->ccadc_offset = result_offset;
443 data_msb = the_chip->ccadc_offset >> 8;
444 data_lsb = the_chip->ccadc_offset;
445
446 rc = calib_ccadc_program_trim(the_chip, CCADC_OFFSET_TRIM1,
447 data_msb, data_lsb, 1);
448 if (rc) {
449 pr_debug("error = %d programming offset trim 0x%02x 0x%02x\n",
450 rc, data_msb, data_lsb);
451 /* enable the interrupt and write it when it fires */
452 enable_irq(the_chip->eoc_irq);
453 }
454
455 rc = calib_ccadc_enable_arbiter(the_chip);
456 if (rc < 0) {
457 pr_err("error = %d enabling arbiter for gain\n", rc);
458 goto bail;
459 }
460
461 /*
462 * Set decimation ratio to 4k, lower ratio may be used in order to speed
463 * up, pending verification through bench
464 */
465 rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
466 CCADC_CALIB_DIG_PARAM);
467 if (rc < 0) {
468 pr_err("error = %d enabling decimation ration for gain\n", rc);
469 goto bail;
470 }
471
472 result_gain = 0;
473 for (i = 0; i < SAMPLE_COUNT; i++) {
474 rc = pm8xxx_writeb(the_chip->dev->parent,
475 ADC_ARB_SECP_RSV, CCADC_CALIB_RSV_25MV);
476 if (rc < 0) {
477 pr_err("error = %d selecting 25mV for gain\n", rc);
478 goto bail;
479 }
480
481 /* Enable CCADC */
482 rc = pm8xxx_writeb(the_chip->dev->parent,
483 ADC_ARB_SECP_ANA_PARAM, CCADC_CALIB_ANA_PARAM);
484 if (rc < 0) {
485 pr_err("error = %d enabling ccadc\n", rc);
486 goto bail;
487 }
488
489 rc = calib_start_conv(the_chip, &result);
490 if (rc < 0) {
491 pr_err("error = %d for adc reading 25mV\n", rc);
492 goto bail;
493 }
494
495 result_gain += result;
496 }
497 result_gain = result_gain / SAMPLE_COUNT;
498
499 /*
500 * result_offset includes INTRINSIC OFFSET
501 * the_chip->ccadc_gain_uv will be the actual voltage
502 * measured for 25000UV
503 */
504 the_chip->ccadc_gain_uv = pm8xxx_ccadc_reading_to_microvolt(
505 the_chip->revision,
506 ((s64)result_gain - result_offset));
507
508 pr_debug("gain result_gain = 0x%x, voltage = %d microVolts\n",
509 result_gain, the_chip->ccadc_gain_uv);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800510
511 data_msb = result_gain >> 8;
512 data_lsb = result_gain;
513 rc = calib_ccadc_program_trim(the_chip, CCADC_FULLSCALE_TRIM1,
514 data_msb, data_lsb, 0);
515 if (rc)
516 pr_debug("error = %d programming gain trim\n", rc);
517bail:
518 pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_CNTRL, sec_cntrl);
Abhijeet Dharmapurikarf154bfc2013-01-23 00:24:58 -0800519calibration_unlock:
520 mutex_unlock(&the_chip->calib_mutex);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800521}
522EXPORT_SYMBOL(pm8xxx_calib_ccadc);
523
David Keitel3c378822012-06-07 13:43:22 -0700524static void calibrate_ccadc_work(struct work_struct *work)
525{
526 struct pm8xxx_ccadc_chip *chip = container_of(work,
527 struct pm8xxx_ccadc_chip, calib_ccadc_work.work);
528
529 pm8xxx_calib_ccadc();
530 schedule_delayed_work(&chip->calib_ccadc_work,
531 round_jiffies_relative(msecs_to_jiffies
532 (chip->calib_delay_ms)));
533}
534
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800535static irqreturn_t pm8921_bms_ccadc_eoc_handler(int irq, void *data)
536{
537 u8 data_msb, data_lsb;
538 struct pm8xxx_ccadc_chip *chip = data;
539 int rc;
540
Abhijeet Dharmapurikar1b817342012-10-11 11:24:50 -0700541 if (!the_chip)
542 goto out;
543
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800544 pr_debug("irq = %d triggered\n", irq);
545 data_msb = chip->ccadc_offset >> 8;
546 data_lsb = chip->ccadc_offset;
547
548 rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
549 data_msb, data_lsb, 0);
550 disable_irq_nosync(chip->eoc_irq);
551
Abhijeet Dharmapurikar1b817342012-10-11 11:24:50 -0700552out:
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800553 return IRQ_HANDLED;
554}
555
556#define CCADC_IBAT_DIG_PARAM 0xA3
557#define CCADC_IBAT_RSV 0x10
558#define CCADC_IBAT_ANA_PARAM 0x1A
559static int ccadc_get_rsense_voltage(int *voltage_uv)
560{
561 u16 raw;
562 int result;
563 int rc = 0;
564
565 rc = calib_ccadc_enable_arbiter(the_chip);
566 if (rc < 0) {
567 pr_err("error = %d enabling arbiter for offset\n", rc);
568 return rc;
569 }
570
571 rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
572 CCADC_IBAT_DIG_PARAM);
573 if (rc < 0) {
574 pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
575 return rc;
576 }
577
578 rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_RSV,
579 CCADC_IBAT_RSV);
580 if (rc < 0) {
581 pr_err("error = %d selecting rsense\n", rc);
582 return rc;
583 }
584
585 rc = pm8xxx_writeb(the_chip->dev->parent,
586 ADC_ARB_SECP_ANA_PARAM, CCADC_IBAT_ANA_PARAM);
587 if (rc < 0) {
588 pr_err("error = %d enabling ccadc\n", rc);
589 return rc;
590 }
591
592 rc = calib_start_conv(the_chip, &raw);
593 if (rc < 0) {
594 pr_err("error = %d for zero volt measurement\n", rc);
595 return rc;
596 }
597
598 pr_debug("Vsense raw = 0x%x\n", raw);
599 result = cc_adjust_for_offset(raw);
600 pr_debug("Vsense after offset raw = 0x%x offset=0x%x\n",
601 result,
602 the_chip->ccadc_offset);
603 *voltage_uv = pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
604 ((s64)result));
Abhijeet Dharmapurikar034a0342011-12-08 11:12:54 -0800605 pr_debug("Vsense before gain of %d = %d uV\n", the_chip->ccadc_gain_uv,
606 *voltage_uv);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800607 *voltage_uv = pm8xxx_cc_adjust_for_gain(*voltage_uv);
608
609 pr_debug("Vsense = %d uV\n", *voltage_uv);
610 return 0;
611}
612
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800613int pm8xxx_ccadc_get_battery_current(int *bat_current_ua)
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800614{
Trilok Sonic6e541e2012-03-19 17:14:28 +0530615 int voltage_uv = 0, rc;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800616
617 rc = ccadc_get_rsense_voltage(&voltage_uv);
618 if (rc) {
619 pr_err("cant get voltage across rsense rc = %d\n", rc);
620 return rc;
621 }
622
Xiaozhe Shid69c91e2012-11-06 10:00:38 -0800623 *bat_current_ua = div_s64((s64)voltage_uv * 1000000LL,
624 the_chip->r_sense_uohm);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800625 /*
626 * ccadc reads +ve current when the battery is charging
627 * We need to return -ve if the battery is charging
628 */
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800629 *bat_current_ua = -1 * (*bat_current_ua);
630 pr_debug("bat current = %d ma\n", *bat_current_ua);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800631 return 0;
632}
633EXPORT_SYMBOL(pm8xxx_ccadc_get_battery_current);
634
635static int get_reg(void *data, u64 * val)
636{
637 int addr = (int)data;
638 int ret;
639 u8 temp;
640
641 ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
642 if (ret) {
643 pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
644 addr, temp, ret);
645 return -EAGAIN;
646 }
647 *val = temp;
648 return 0;
649}
650
651static int set_reg(void *data, u64 val)
652{
653 int addr = (int)data;
654 int ret;
655 u8 temp;
656
657 temp = (u8) val;
658 ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
659 if (ret) {
660 pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
661 addr, temp, ret);
662 return -EAGAIN;
663 }
664 return 0;
665}
666DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
667
668static int get_calc(void *data, u64 * val)
669{
670 int ibat, rc;
671
672 rc = pm8xxx_ccadc_get_battery_current(&ibat);
673 *val = ibat;
674 return rc;
675}
676DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%lld\n");
677
678static void create_debugfs_entries(struct pm8xxx_ccadc_chip *chip)
679{
680 chip->dent = debugfs_create_dir("pm8xxx-ccadc", NULL);
681
682 if (IS_ERR(chip->dent)) {
683 pr_err("ccadc couldnt create debugfs dir\n");
684 return;
685 }
686
687 debugfs_create_file("CCADC_ANA_PARAM", 0644, chip->dent,
688 (void *)CCADC_ANA_PARAM, &reg_fops);
689 debugfs_create_file("CCADC_DIG_PARAM", 0644, chip->dent,
690 (void *)CCADC_DIG_PARAM, &reg_fops);
691 debugfs_create_file("CCADC_RSV", 0644, chip->dent,
692 (void *)CCADC_RSV, &reg_fops);
693 debugfs_create_file("CCADC_DATA0", 0644, chip->dent,
694 (void *)CCADC_DATA0, &reg_fops);
695 debugfs_create_file("CCADC_DATA1", 0644, chip->dent,
696 (void *)CCADC_DATA1, &reg_fops);
697 debugfs_create_file("CCADC_OFFSET_TRIM1", 0644, chip->dent,
698 (void *)CCADC_OFFSET_TRIM1, &reg_fops);
699 debugfs_create_file("CCADC_OFFSET_TRIM0", 0644, chip->dent,
700 (void *)CCADC_OFFSET_TRIM0, &reg_fops);
701 debugfs_create_file("CCADC_FULLSCALE_TRIM1", 0644, chip->dent,
702 (void *)CCADC_FULLSCALE_TRIM1, &reg_fops);
703 debugfs_create_file("CCADC_FULLSCALE_TRIM0", 0644, chip->dent,
704 (void *)CCADC_FULLSCALE_TRIM0, &reg_fops);
705
706 debugfs_create_file("show_ibatt", 0644, chip->dent,
707 (void *)0, &calc_fops);
708}
709
710static int __devinit pm8xxx_ccadc_probe(struct platform_device *pdev)
711{
712 int rc = 0;
713 struct pm8xxx_ccadc_chip *chip;
714 struct resource *res;
715 const struct pm8xxx_ccadc_platform_data *pdata
716 = pdev->dev.platform_data;
717
718 if (!pdata) {
719 pr_err("missing platform data\n");
720 return -EINVAL;
721 }
722 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
723 "PM8921_BMS_CCADC_EOC");
724 if (!res) {
725 pr_err("failed to get irq\n");
726 return -EINVAL;
727 }
728
729 chip = kzalloc(sizeof(struct pm8xxx_ccadc_chip), GFP_KERNEL);
730 if (!chip) {
731 pr_err("Cannot allocate pm_bms_chip\n");
732 return -ENOMEM;
733 }
734 chip->dev = &pdev->dev;
735 chip->revision = pm8xxx_get_revision(chip->dev->parent);
736 chip->eoc_irq = res->start;
Xiaozhe Shid69c91e2012-11-06 10:00:38 -0800737 chip->r_sense_uohm = pdata->r_sense_uohm;
David Keitel3c378822012-06-07 13:43:22 -0700738 chip->calib_delay_ms = pdata->calib_delay_ms;
Xiaozhe Shid035f6a2012-12-18 16:16:33 -0800739 chip->batt_temp_channel = pdata->ccadc_cdata.batt_temp_channel;
Abhijeet Dharmapurikarf154bfc2013-01-23 00:24:58 -0800740 mutex_init(&chip->calib_mutex);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800741
Abhijeet Dharmapurikar97d40902011-11-30 12:12:51 -0800742 calib_ccadc_read_offset_and_gain(chip,
743 &chip->ccadc_gain_uv,
744 &chip->ccadc_offset);
Anirudh Ghayal1d117472012-12-04 12:41:53 +0530745 irq_set_status_flags(chip->eoc_irq, IRQ_NOAUTOEN);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800746 rc = request_irq(chip->eoc_irq,
747 pm8921_bms_ccadc_eoc_handler, IRQF_TRIGGER_RISING,
748 "bms_eoc_ccadc", chip);
749 if (rc) {
750 pr_err("failed to request %d irq rc= %d\n", chip->eoc_irq, rc);
751 goto free_chip;
752 }
David Keitel3c378822012-06-07 13:43:22 -0700753
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800754 platform_set_drvdata(pdev, chip);
755 the_chip = chip;
Abhijeet Dharmapurikarfbefde22012-07-12 14:39:13 -0700756 INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work);
757 schedule_delayed_work(&chip->calib_ccadc_work, 0);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800758
759 create_debugfs_entries(chip);
760
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800761 return 0;
762
763free_chip:
Abhijeet Dharmapurikarf154bfc2013-01-23 00:24:58 -0800764 mutex_destroy(&chip->calib_mutex);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800765 kfree(chip);
766 return rc;
767}
768
769static int __devexit pm8xxx_ccadc_remove(struct platform_device *pdev)
770{
771 struct pm8xxx_ccadc_chip *chip = platform_get_drvdata(pdev);
772
773 debugfs_remove_recursive(chip->dent);
774 the_chip = NULL;
775 kfree(chip);
776 return 0;
777}
778
Xiaozhe Shid035f6a2012-12-18 16:16:33 -0800779#define CCADC_CALIB_TEMP_THRESH 20
780static int pm8xxx_ccadc_resume(struct device *dev)
781{
782 int rc, batt_temp, delta_temp;
783 unsigned long current_time_sec;
784 unsigned long time_since_last_calib;
785
786 rc = get_batt_temp(the_chip, &batt_temp);
787 if (rc) {
788 pr_err("unable to get batt_temp: %d\n", rc);
789 return 0;
790 }
791 rc = get_current_time(&current_time_sec);
792 if (rc) {
793 pr_err("unable to get current time: %d\n", rc);
794 return 0;
795 }
796 if (current_time_sec > the_chip->last_calib_time) {
797 time_since_last_calib = current_time_sec -
798 the_chip->last_calib_time;
799 delta_temp = abs(batt_temp - the_chip->last_calib_temp);
800 pr_debug("time since last calib: %lu, delta_temp = %d\n",
801 time_since_last_calib, delta_temp);
802 if (time_since_last_calib >= the_chip->calib_delay_ms/1000
803 || delta_temp > CCADC_CALIB_TEMP_THRESH) {
804 the_chip->last_calib_time = current_time_sec;
805 the_chip->last_calib_temp = batt_temp;
806 pm8xxx_calib_ccadc();
807 }
808 }
809 return 0;
810}
811
812static const struct dev_pm_ops pm8xxx_ccadc_pm_ops = {
813 .resume = pm8xxx_ccadc_resume,
814};
815
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800816static struct platform_driver pm8xxx_ccadc_driver = {
817 .probe = pm8xxx_ccadc_probe,
818 .remove = __devexit_p(pm8xxx_ccadc_remove),
819 .driver = {
820 .name = PM8XXX_CCADC_DEV_NAME,
821 .owner = THIS_MODULE,
Xiaozhe Shid035f6a2012-12-18 16:16:33 -0800822 .pm = &pm8xxx_ccadc_pm_ops,
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800823 },
824};
825
826static int __init pm8xxx_ccadc_init(void)
827{
828 return platform_driver_register(&pm8xxx_ccadc_driver);
829}
830
831static void __exit pm8xxx_ccadc_exit(void)
832{
833 platform_driver_unregister(&pm8xxx_ccadc_driver);
834}
835
836module_init(pm8xxx_ccadc_init);
837module_exit(pm8xxx_ccadc_exit);
838
839MODULE_LICENSE("GPL v2");
840MODULE_DESCRIPTION("PMIC8XXX ccadc driver");
841MODULE_VERSION("1.0");
842MODULE_ALIAS("platform:" PM8XXX_CCADC_DEV_NAME);