power: pm8921-bms: adjust for FCC calculation

When charging finishes with a full battery, the bms driver calculates
last_real_fcc. This parameter will be preserved between reboots.

Also the battery temperature when last_real_fcc is saved and
preserved between reboots along with last_real_fcc.

Since we are creating additional parameters whose set functions are
different from the others, move all of them to use module param with
accessor functions. Create macros to easily define accessor functions.
While at it change last_rbatt, last_ocv_uv parameters to be updated
only when a valid reading is available from the hardware or when the
userspace programs a new value.

When userspace updates the last_real_fcc and last_real_fcc_batt_temp
we create another adjusted table similar to fcc vs temperature filling up
its entries based on last_real_fcc and last_real_fcc_batt_temp. Note
that the ratios between FCCs at various temperatures are kept the same.

This new adjusted fcc vs temperature table reflects the fcc of the battery
more accurately and the algorithm is updated to use this table when
available. The scalingfactor is not used along with this adjusted table.

Change-Id: Ic1befd07b17fa16889e593fdec51021be4eeb1d5
Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index e666bfc..61b7608 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -88,30 +88,122 @@
 	unsigned int		pmic_bms_irq[PM_BMS_MAX_INTS];
 	DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
 	spinlock_t		bms_output_lock;
+	struct single_row_lut	*adjusted_fcc_temp_lut;
 };
 
 static struct pm8921_bms_chip *the_chip;
 
 #define DEFAULT_RBATT_MOHMS		128
-#define DEFAULT_UNUSABLE_CHARGE_MAH	10
 #define DEFAULT_OCV_MICROVOLTS		3900000
-#define DEFAULT_REMAINING_CHARGE_MAH	990
-#define DEFAULT_COULUMB_COUNTER		0
 #define DEFAULT_CHARGE_CYCLES		0
 
+static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
+static int last_charge_increase;
+module_param(last_chargecycles, int, 0644);
+module_param(last_charge_increase, int, 0644);
+
 static int last_rbatt = -EINVAL;
 static int last_ocv_uv = -EINVAL;
 static int last_soc = -EINVAL;
 static int last_real_fcc = -EINVAL;
+static int last_real_fcc_batt_temp = -EINVAL;
 
-static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
-static int last_charge_increase;
+static int bms_ops_set(const char *val, const struct kernel_param *kp)
+{
+	if (*(int *)kp->arg == -EINVAL)
+		return param_set_int(val, kp);
+	else
+		return 0;
+}
 
-module_param(last_rbatt, int, 0644);
-module_param(last_ocv_uv, int, 0644);
-module_param(last_chargecycles, int, 0644);
-module_param(last_charge_increase, int, 0644);
-module_param(last_real_fcc, int, 0644);
+static struct kernel_param_ops bms_param_ops = {
+	.set = bms_ops_set,
+	.get = param_get_int,
+};
+
+module_param_cb(last_rbatt, &bms_param_ops, &last_rbatt, 0644);
+module_param_cb(last_ocv_uv, &bms_param_ops, &last_ocv_uv, 0644);
+module_param_cb(last_soc, &bms_param_ops, &last_soc, 0644);
+
+static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp);
+static void readjust_fcc_table(void)
+{
+	struct single_row_lut *temp, *old;
+	int i, fcc, ratio;
+
+	if (!the_chip->fcc_temp_lut) {
+		pr_err("The static fcc lut table is NULL\n");
+		return;
+	}
+
+	temp = kzalloc(sizeof(struct single_row_lut), GFP_KERNEL);
+	if (!temp) {
+		pr_err("Cannot allocate memory for adjusted fcc table\n");
+		return;
+	}
+
+	fcc = interpolate_fcc(the_chip, last_real_fcc_batt_temp);
+
+	temp->cols = the_chip->fcc_temp_lut->cols;
+	for (i = 0; i < the_chip->fcc_temp_lut->cols; i++) {
+		temp->x[i] = the_chip->fcc_temp_lut->x[i];
+		ratio = div_u64(the_chip->fcc_temp_lut->y[i] * 1000, fcc);
+		temp->y[i] =  (ratio * last_real_fcc);
+		temp->y[i] /= 1000;
+		pr_debug("temp=%d, staticfcc=%d, adjfcc=%d, ratio=%d\n",
+				temp->x[i], the_chip->fcc_temp_lut->y[i],
+				temp->y[i], ratio);
+	}
+
+	old = the_chip->adjusted_fcc_temp_lut;
+	the_chip->adjusted_fcc_temp_lut = temp;
+	kfree(old);
+}
+
+static int bms_last_real_fcc_set(const char *val,
+				const struct kernel_param *kp)
+{
+	int rc = 0;
+
+	if (last_real_fcc == -EINVAL)
+		rc = param_set_int(val, kp);
+	if (rc) {
+		pr_err("Failed to set last_real_fcc rc=%d\n", rc);
+		return rc;
+	}
+	if (last_real_fcc_batt_temp != -EINVAL)
+		readjust_fcc_table();
+	return rc;
+}
+static struct kernel_param_ops bms_last_real_fcc_param_ops = {
+	.set = bms_last_real_fcc_set,
+	.get = param_get_int,
+};
+module_param_cb(last_real_fcc, &bms_last_real_fcc_param_ops,
+					&last_real_fcc, 0644);
+
+static int bms_last_real_fcc_batt_temp_set(const char *val,
+				const struct kernel_param *kp)
+{
+	int rc = 0;
+
+	if (last_real_fcc_batt_temp == -EINVAL)
+		rc = param_set_int(val, kp);
+	if (rc) {
+		pr_err("Failed to set last_real_fcc_batt_temp rc=%d\n", rc);
+		return rc;
+	}
+	if (last_real_fcc != -EINVAL)
+		readjust_fcc_table();
+	return rc;
+}
+
+static struct kernel_param_ops bms_last_real_fcc_batt_temp_param_ops = {
+	.set = bms_last_real_fcc_batt_temp_set,
+	.get = param_get_int,
+};
+module_param_cb(last_real_fcc_batt_temp, &bms_last_real_fcc_batt_temp_param_ops,
+					&last_real_fcc_batt_temp, 0644);
 
 static int pm_bms_get_rt_status(struct pm8921_bms_chip *chip, int irq_id)
 {
@@ -335,6 +427,8 @@
 	}
 	*result = vbatt_to_microvolt(reading);
 	pr_debug("raw = %04x ocv_microV = %u\n", reading, *result);
+	if (*result != 0)
+		last_ocv_uv = *result;
 	return 0;
 }
 
@@ -444,6 +538,11 @@
 	return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
 }
 
+static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp)
+{
+	return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp);
+}
+
 static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
 								int cycles)
 {
@@ -665,6 +764,7 @@
 		return -EINVAL;
 	}
 	r_batt = ((ocv - vbatt) * chip->r_sense) / vsense;
+	last_rbatt = r_batt;
 	pr_debug("r_batt = %umilliOhms", r_batt);
 	return r_batt;
 }
@@ -674,16 +774,18 @@
 {
 	int initfcc, result, scalefactor = 0;
 
-	initfcc = interpolate_fcc(chip, batt_temp);
-	pr_debug("intfcc = %umAh batt_temp = %d\n", initfcc, batt_temp);
+	if (chip->adjusted_fcc_temp_lut == NULL) {
+		initfcc = interpolate_fcc(chip, batt_temp);
 
-	scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
-	pr_debug("scalefactor = %d batt_temp = %d\n", scalefactor, batt_temp);
+		scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
 
-	/* Multiply the initial FCC value by the scale factor. */
-	result = (initfcc * scalefactor) / 100;
-	pr_debug("fcc mAh = %d\n", result);
-	return result;
+		/* Multiply the initial FCC value by the scale factor. */
+		result = (initfcc * scalefactor) / 100;
+		pr_debug("fcc mAh = %d\n", result);
+		return result;
+	} else {
+		return interpolate_fcc_adjusted(chip, batt_temp);
+	}
 }
 
 static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
@@ -711,20 +813,18 @@
 	rc = get_battery_uvolts(chip, &vbatt);
 	if (rc) {
 		pr_err("failed to read vbatt from adc rc = %d\n", rc);
-		last_ocv_uv = DEFAULT_OCV_MICROVOLTS;
 		return rc;
 	}
 
 	rc =  pm8921_bms_get_battery_current(&ibatt);
 	if (rc) {
 		pr_err("failed to read batt current rc = %d\n", rc);
-		last_ocv_uv = DEFAULT_OCV_MICROVOLTS;
 		return rc;
 	}
 
 	rbatt = calculate_rbatt(the_chip);
 	if (rbatt < 0)
-		rbatt = DEFAULT_RBATT_MOHMS;
+		rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
 	*ocv = vbatt + ibatt * rbatt;
 	return 0;
 }
@@ -771,8 +871,6 @@
 	if (rbatt < 0) {
 		rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
 		pr_debug("rbatt unavailable assuming %d\n", rbatt);
-	} else {
-		last_rbatt = rbatt;
 	}
 
 	/* calculate unusable charge */
@@ -800,9 +898,6 @@
 	if (ocv == 0) {
 		ocv = last_ocv_uv;
 		pr_debug("ocv not available using last_ocv_uv=%d\n", ocv);
-	} else {
-		/* update the usespace param since a good ocv is available */
-		last_ocv_uv = ocv;
 	}
 
 	pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
@@ -1107,6 +1202,8 @@
 		batt_temp = (int)result.physical;
 		last_real_fcc = calculate_real_fcc(the_chip,
 						batt_temp, last_chargecycles);
+		last_real_fcc_batt_temp = batt_temp;
+		readjust_fcc_table();
 	}
 
 charge_cycle_calculation:
@@ -1272,13 +1369,18 @@
 	int ocv, rc;
 
 	/*
-	 * Check if a last_good_ocv is available,
-	 * if not compute it here at boot time.
+	 * Check if a ocv is available in bms hw,
+	 * if not compute it here at boot time and save it
+	 * in the last_ocv_uv.
 	 */
 	rc = read_last_good_ocv(chip, &ocv);
 	if (rc || ocv == 0) {
-		rc = adc_based_ocv(chip, &last_ocv_uv);
-		pr_err("failed to read ocv from adc and bms rc = %d\n", rc);
+		rc = adc_based_ocv(chip, &ocv);
+		if (rc) {
+			pr_err("failed to read adc based ocv rc = %d\n", rc);
+			ocv = DEFAULT_OCV_MICROVOLTS;
+		}
+		last_ocv_uv = ocv;
 	}
 	pr_debug("ocv = %d last_ocv_uv = %d\n", ocv, last_ocv_uv);
 }
@@ -1644,6 +1746,7 @@
 	struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
 
 	free_irqs(chip);
+	kfree(chip->adjusted_fcc_temp_lut);
 	platform_set_drvdata(pdev, NULL);
 	the_chip = NULL;
 	kfree(chip);