power: pm8921-bms: Add calibration for hkadc

The output of hkadc calibration steps are voltages measured at
0.625V and 1.25V.  We use these to adjust the vbatt readings.

Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index cdf33e7..7c73c23 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -36,12 +36,12 @@
 #define CCADC_OFFSET_TRIM1	0x34A
 #define CCADC_OFFSET_TRIM0	0x34B
 
-#define ADC_ARB_SECP_CNTRL 0x190
-#define ADC_ARB_SECP_AMUX_CNTRL 0x191
-#define ADC_ARB_SECP_ANA_PARAM 0x192
-#define ADC_ARB_SECP_RSV 0x194
-#define ADC_ARB_SECP_DATA1 0x195
-#define ADC_ARB_SECP_DATA0 0x196
+#define ADC_ARB_SECP_CNTRL	0x190
+#define ADC_ARB_SECP_AMUX_CNTRL	0x191
+#define ADC_ARB_SECP_ANA_PARAM	0x192
+#define ADC_ARB_SECP_RSV	0x194
+#define ADC_ARB_SECP_DATA1	0x195
+#define ADC_ARB_SECP_DATA0	0x196
 
 enum pmic_bms_interrupts {
 	PM8921_BMS_SBI_WRITE_OK,
@@ -75,11 +75,15 @@
 	struct single_row_lut	*fcc_sf_lut;
 	struct pc_temp_ocv_lut	*pc_temp_ocv_lut;
 	struct pc_sf_lut	*pc_sf_lut;
-	struct delayed_work	calib_work;
+	struct work_struct	calib_hkadc_work;
 	unsigned int		calib_delay_ms;
 	unsigned int		revision;
+	unsigned int		xoadc_v0625;
+	unsigned int		xoadc_v125;
 	unsigned int		batt_temp_channel;
 	unsigned int		vbat_channel;
+	unsigned int		ref625mv_channel;
+	unsigned int		ref1p25v_channel;
 	unsigned int		pmic_bms_irq[PM_BMS_MAX_INTS];
 	DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
 };
@@ -111,6 +115,15 @@
 					chip->pmic_bms_irq[irq_id]);
 }
 
+static void pm8921_bms_enable_irq(struct pm8921_bms_chip *chip, int interrupt)
+{
+	if (!__test_and_set_bit(interrupt, chip->enabled_irqs)) {
+		dev_dbg(chip->dev, "%s %d\n", __func__,
+						chip->pmic_bms_irq[interrupt]);
+		enable_irq(chip->pmic_bms_irq[interrupt]);
+	}
+}
+
 static void pm8921_bms_disable_irq(struct pm8921_bms_chip *chip, int interrupt)
 {
 	if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
@@ -202,6 +215,17 @@
 	return (a - INTRINSIC_OFFSET) * V_PER_BIT_MUL_FACTOR;
 }
 
+#define XOADC_CALIB_UV		625000
+static int adjust_xo_reading(struct pm8921_bms_chip *chip, unsigned int uv)
+{
+	u64 numerator = ((u64)uv - chip->xoadc_v0625) * XOADC_CALIB_UV;
+	u64 denominator =  chip->xoadc_v125 - chip->xoadc_v0625;
+
+	if (denominator == 0)
+		return uv;
+	return XOADC_CALIB_UV + div_u64(numerator, denominator);
+}
+
 #define CC_RESOLUTION_N_V1	1085069
 #define CC_RESOLUTION_D_V1	100000
 #define CC_RESOLUTION_N_V2	868056
@@ -574,11 +598,15 @@
 		pr_err("fail to read ocv_for_rbatt rc = %d\n", rc);
 		ocv = 0;
 	}
+	ocv = adjust_xo_reading(chip, ocv);
+
 	rc = read_vbatt_for_rbatt(chip, &vbatt);
 	if (rc) {
 		pr_err("fail to read vbatt_for_rbatt rc = %d\n", rc);
 		ocv = 0;
 	}
+	vbatt = adjust_xo_reading(chip, vbatt);
+
 	rc = read_vsense_for_rbatt(chip, &vsense);
 	if (rc) {
 		pr_err("fail to read vsense_for_rbatt rc = %d\n", rc);
@@ -762,6 +790,65 @@
 	return soc;
 }
 
+#define XOADC_MAX_1P25V		1312500
+#define XOADC_MIN_1P25V		1187500
+#define XOADC_MAX_0P625V		656250
+#define XOADC_MIN_0P625V		593750
+
+#define HKADC_V_PER_BIT_MUL_FACTOR	977
+#define HKADC_V_PER_BIT_DIV_FACTOR	10
+static int calib_hkadc_convert_microvolt(unsigned int phy)
+{
+	return phy * HKADC_V_PER_BIT_MUL_FACTOR / HKADC_V_PER_BIT_DIV_FACTOR;
+}
+
+static void calib_hkadc(struct pm8921_bms_chip *chip)
+{
+	int voltage, rc;
+	struct pm8921_adc_chan_result result;
+
+	rc = pm8921_adc_read(the_chip->ref1p25v_channel, &result);
+	if (rc) {
+		pr_err("ADC failed for 1.25volts rc = %d\n", rc);
+		return;
+	}
+	voltage = calib_hkadc_convert_microvolt(result.physical);
+
+	pr_debug("result 1.25v = 0x%llx, voltage = %dmV adc_meas = %lld\n",
+				result.physical, voltage, result.measurement);
+
+	/* check for valid range */
+	if (voltage > XOADC_MAX_1P25V)
+		voltage = XOADC_MAX_1P25V;
+	else if (voltage < XOADC_MIN_1P25V)
+		voltage = XOADC_MIN_1P25V;
+	chip->xoadc_v125 = voltage;
+
+	rc = pm8921_adc_read(the_chip->ref625mv_channel, &result);
+	if (rc) {
+		pr_err("ADC failed for 1.25volts rc = %d\n", rc);
+		return;
+	}
+	voltage = calib_hkadc_convert_microvolt(result.physical);
+	pr_debug("result 0.625V = 0x%llx, voltage = %dmV adc_mead = %lld\n",
+				result.physical, voltage, result.measurement);
+	/* check for valid range */
+	if (voltage > XOADC_MAX_0P625V)
+		voltage = XOADC_MAX_0P625V;
+	else if (voltage < XOADC_MIN_0P625V)
+		voltage = XOADC_MIN_0P625V;
+
+	chip->xoadc_v0625 = voltage;
+}
+
+static void calibrate_hkadc_work(struct work_struct *work)
+{
+	struct pm8921_bms_chip *chip = container_of(work,
+				struct pm8921_bms_chip, calib_hkadc_work);
+
+	calib_hkadc(chip);
+}
+
 int pm8921_bms_get_vsense_avg(int *result)
 {
 	if (the_chip)
@@ -867,15 +954,19 @@
 
 static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
 {
+	struct pm8921_bms_chip *chip = data;
 
 	pr_debug("irq = %d triggered", irq);
+	schedule_work(&chip->calib_hkadc_work);
 	return IRQ_HANDLED;
 }
 
 static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
 {
+	struct pm8921_bms_chip *chip = data;
 
 	pr_debug("irq = %d triggered", irq);
+	schedule_work(&chip->calib_hkadc_work);
 	return IRQ_HANDLED;
 }
 
@@ -1015,6 +1106,7 @@
 	CALC_FCC,
 	CALC_PC,
 	CALC_SOC,
+	CALIB_HKADC,
 };
 
 static int test_batt_temp = 5;
@@ -1085,6 +1177,11 @@
 		*val = calculate_state_of_charge(the_chip,
 					test_batt_temp, test_chargecycle);
 		break;
+	case CALIB_HKADC:
+		/* reading this will trigger calibration */
+		*val = 0;
+		calib_hkadc(the_chip);
+		break;
 	default:
 		ret = -EINVAL;
 	}
@@ -1234,6 +1331,8 @@
 				(void *)CALC_PC, &calc_fops);
 	debugfs_create_file("show_soc", 0644, chip->dent,
 				(void *)CALC_SOC, &calc_fops);
+	debugfs_create_file("calib_hkadc", 0644, chip->dent,
+				(void *)CALIB_HKADC, &calc_fops);
 
 	for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
 		if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
@@ -1244,11 +1343,6 @@
 	}
 }
 
-static void calibrate_work(struct work_struct *work)
-{
-	/* TODO */
-}
-
 static int __devinit pm8921_bms_probe(struct platform_device *pdev)
 {
 	int rc = 0;
@@ -1280,7 +1374,10 @@
 
 	chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
 	chip->vbat_channel = pdata->bms_cdata.vbat_channel;
+	chip->ref625mv_channel = pdata->bms_cdata.ref625mv_channel;
+	chip->ref1p25v_channel = pdata->bms_cdata.ref1p25v_channel;
 	chip->revision = pm8xxx_get_revision(chip->dev->parent);
+	INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
 
 	rc = pm8921_bms_hw_init(chip);
 	if (rc) {
@@ -1300,10 +1397,9 @@
 
 	check_initial_ocv(chip);
 
-	INIT_DELAYED_WORK(&chip->calib_work, calibrate_work);
-	schedule_delayed_work(&chip->calib_work,
-			round_jiffies_relative(msecs_to_jiffies
-			(chip->calib_delay_ms)));
+	/* enable the vbatt reading interrupts for scheduling hkadc calib */
+	pm8921_bms_enable_irq(chip, PM8921_BMS_GOOD_OCV);
+	pm8921_bms_enable_irq(chip, PM8921_BMS_OCV_FOR_R);
 	return 0;
 
 free_chip: