power: pm8xxx-ccadc: implement quick calibration

There are certain systems that wakeup every so often to do some
housekeeping work. We don't want to run a full calibration
when the system wakeups.

For such systems runs a quick calibration with just two samples.
Use the periodic_wakeup flag to indicate such a system.

CRs-Fixed: 454221
Change-Id: Ifc86414f7fab4d0921e3289d6e52b9e077a1f625
Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
diff --git a/drivers/power/pm8xxx-ccadc.c b/drivers/power/pm8xxx-ccadc.c
index 7e37daa..5313593 100644
--- a/drivers/power/pm8xxx-ccadc.c
+++ b/drivers/power/pm8xxx-ccadc.c
@@ -81,6 +81,7 @@
 	int			r_sense_uohm;
 	struct delayed_work	calib_ccadc_work;
 	struct mutex		calib_mutex;
+	bool			periodic_wakeup;
 };
 
 static struct pm8xxx_ccadc_chip *the_chip;
@@ -367,7 +368,7 @@
 	return 0;
 }
 
-void pm8xxx_calib_ccadc(void)
+static void __pm8xxx_calib_ccadc(int sample_count)
 {
 	u8 data_msb, data_lsb, sec_cntrl;
 	int result_offset, result_gain;
@@ -379,6 +380,8 @@
 		return;
 	}
 
+	pr_debug("sample_count = %d\n", sample_count);
+
 	mutex_lock(&the_chip->calib_mutex);
 	rc = pm8xxx_readb(the_chip->dev->parent,
 					ADC_ARB_SECP_CNTRL, &sec_cntrl);
@@ -405,7 +408,7 @@
 	}
 
 	result_offset = 0;
-	for (i = 0; i < SAMPLE_COUNT; i++) {
+	for (i = 0; i < sample_count; i++) {
 		/* Short analog inputs to CCADC internally to ground */
 		rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_RSV,
 							CCADC_CALIB_RSV_GND);
@@ -431,7 +434,7 @@
 		result_offset += result;
 	}
 
-	result_offset = result_offset / SAMPLE_COUNT;
+	result_offset = result_offset / sample_count;
 
 
 	pr_debug("offset result_offset = 0x%x, voltage = %llduV\n",
@@ -470,7 +473,7 @@
 	}
 
 	result_gain = 0;
-	for (i = 0; i < SAMPLE_COUNT; i++) {
+	for (i = 0; i < sample_count; i++) {
 		rc = pm8xxx_writeb(the_chip->dev->parent,
 					ADC_ARB_SECP_RSV, CCADC_CALIB_RSV_25MV);
 		if (rc < 0) {
@@ -494,7 +497,7 @@
 
 		result_gain += result;
 	}
-	result_gain = result_gain / SAMPLE_COUNT;
+	result_gain = result_gain / sample_count;
 
 	/*
 	 * result_offset includes INTRINSIC OFFSET
@@ -519,6 +522,16 @@
 calibration_unlock:
 	mutex_unlock(&the_chip->calib_mutex);
 }
+
+static void pm8xxx_calib_ccadc_quick(void)
+{
+	__pm8xxx_calib_ccadc(2);
+}
+
+void pm8xxx_calib_ccadc(void)
+{
+	__pm8xxx_calib_ccadc(SAMPLE_COUNT);
+}
 EXPORT_SYMBOL(pm8xxx_calib_ccadc);
 
 static void calibrate_ccadc_work(struct work_struct *work)
@@ -737,6 +750,7 @@
 	chip->r_sense_uohm = pdata->r_sense_uohm;
 	chip->calib_delay_ms = pdata->calib_delay_ms;
 	chip->batt_temp_channel = pdata->ccadc_cdata.batt_temp_channel;
+	chip->periodic_wakeup = pdata->periodic_wakeup;
 	mutex_init(&chip->calib_mutex);
 
 	calib_ccadc_read_offset_and_gain(chip,
@@ -793,6 +807,12 @@
 		pr_err("unable to get current time: %d\n", rc);
 		return 0;
 	}
+
+	if (the_chip->periodic_wakeup) {
+		pm8xxx_calib_ccadc_quick();
+		return 0;
+	}
+
 	if (current_time_sec > the_chip->last_calib_time) {
 		time_since_last_calib = current_time_sec -
 					the_chip->last_calib_time;
@@ -803,9 +823,11 @@
 				|| delta_temp > CCADC_CALIB_TEMP_THRESH) {
 			the_chip->last_calib_time = current_time_sec;
 			the_chip->last_calib_temp = batt_temp;
-			pm8xxx_calib_ccadc();
+			cancel_delayed_work(&the_chip->calib_ccadc_work);
+			schedule_delayed_work(&the_chip->calib_ccadc_work, 0);
 		}
 	}
+
 	return 0;
 }
 
diff --git a/include/linux/mfd/pm8xxx/ccadc.h b/include/linux/mfd/pm8xxx/ccadc.h
index 955e286..a29486f 100644
--- a/include/linux/mfd/pm8xxx/ccadc.h
+++ b/include/linux/mfd/pm8xxx/ccadc.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -26,11 +26,15 @@
  * @ccadc_cdata:	core data for the ccadc driver containing channel info
  * @r_sense_uohm:		sense resistor value in (micro Ohms)
  * @calib_delay_ms:	how often should the adc calculate gain and offset
+ * @periodic_wakeup:	a flag to indicate that this system wakeups periodically
+ *			for calibration/other housekeeping activities. The ccadc
+ *			does a quick calibration while resuming
  */
 struct pm8xxx_ccadc_platform_data {
 	struct pm8xxx_ccadc_core_data	ccadc_cdata;
 	int				r_sense_uohm;
 	unsigned int			calib_delay_ms;
+	bool				periodic_wakeup;
 };
 
 #define CCADC_READING_RESOLUTION_N	542535