From 1f53feaa52fb4711f6b8e1c04227df3e115ad28a Mon Sep 17 00:00:00 2001
From: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
Date: Tue, 30 Nov 2010 22:35:19 +0530
Subject: [PATCH] Added Support for Fuel Gauging

Change-Id: I2e0ec4d9479617fc74387673f1d8b17ddf3d6405
Signed-off-by: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
---
 drivers/power/intel_mdf_battery.c |  196 +++++++++++++++++++++++++++++++++++-
 1 files changed, 190 insertions(+), 6 deletions(-)

diff --git a/drivers/power/intel_mdf_battery.c b/drivers/power/intel_mdf_battery.c
index c0b3121..8de4cd6 100644
--- a/drivers/power/intel_mdf_battery.c
+++ b/drivers/power/intel_mdf_battery.c
@@ -244,6 +244,37 @@
 #define BATT_STRING_MAX		8
 #define HYSTR_SAMPLE_MAX	4
 
+#define DISCHRG_CURVE_MAX_SAMPLES 17
+#define DISCHRG_CURVE_MAX_COLOMNS 2
+
+
+/*
+ * This array represents the Discharge curve of the battery
+ * Colomn 0 represnts Voltage in mV and colomn 1 represent
+ * charge in mColoumbs.
+ */
+static uint32_t const dischargeCurve[DISCHRG_CURVE_MAX_SAMPLES]
+				[DISCHRG_CURVE_MAX_COLOMNS] = {
+	/* in mV , in mC */
+	{4200, 56603000},
+	{4150, 55754000},
+	{4100, 54905000},
+	{4050, 52385000},
+	{4000, 49811000},
+	{3950, 46697000},
+	{3900, 43584000},
+	{3850, 39339000},
+	{3800, 35094000},
+	{3750, 27452000},
+	{3700, 19811000},
+	{3650, 12735000},
+	{3600, 5660000},
+	{3550, 3963000},
+	{3500, 2264000},
+	{3450, 1132000},
+	{3400, 0}
+};
+
 
 /* Valid msic exceptional events */
 enum msic_event {
@@ -356,9 +387,13 @@ struct msic_batt_props {
 	unsigned int vol_max_des;
 	unsigned int vol_now;
 	unsigned int cur_now;
-	unsigned int charge_full;	/* in mAS */
-	unsigned int charge_now;	/* in mAS */
-	unsigned int charge_avg;	/* in units per second */
+	unsigned int charge_full;	/* in mAh */
+	unsigned int charge_now;	/* in mAh */
+	unsigned int charge_ctr;	/* Coloumb counter raw value */
+	unsigned int charge_avg;	/* moving avg of charge now */
+	unsigned int energy_full;	/* in mWh */
+	unsigned int energy_now;	/* in mWh */
+	unsigned int capacity_level;	/* enumerated values */
 	unsigned int capacity;		/* in units persentage */
 	unsigned int temperature;	/* in milli Centigrade*/
 	char model[BATT_STRING_MAX];
@@ -471,14 +506,19 @@ static enum power_supply_property msic_battery_props[] = {
 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
 	POWER_SUPPLY_PROP_CURRENT_NOW,
 	POWER_SUPPLY_PROP_CHARGE_NOW,
+	POWER_SUPPLY_PROP_CHARGE_COUNTER,
 	POWER_SUPPLY_PROP_CHARGE_FULL,
 	POWER_SUPPLY_PROP_CHARGE_AVG,
-	POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
+	POWER_SUPPLY_PROP_ENERGY_FULL,
+	POWER_SUPPLY_PROP_ENERGY_NOW,
+	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+	POWER_SUPPLY_PROP_CAPACITY,
 	POWER_SUPPLY_PROP_TEMP,
 	POWER_SUPPLY_PROP_MODEL_NAME,
 	POWER_SUPPLY_PROP_MANUFACTURER,
 };
 
+
 static int enable_adc(struct msic_power_module_info *mbi)
 {
 	int ret;
@@ -930,6 +970,128 @@ static unsigned int mdf_cal_avg(unsigned int avg)
 	return avg / 2;
 }
 
+static unsigned int chrg_to_vol_lookup(unsigned int chrg)
+{
+	int ch_diff, ch_total_diff, volt_total_diff, i;
+	unsigned int volt;
+
+	/* Find the index of most nearest charge value */
+	for (i = 0; i < DISCHRG_CURVE_MAX_SAMPLES; i++) {
+		if (chrg < dischargeCurve[i][1])
+			continue;
+		else
+			break;
+	}
+
+	if (i >= DISCHRG_CURVE_MAX_SAMPLES) {
+		dev_dbg(msic_dev, "charge out of range\n");
+		return 0;
+	}
+
+	if (chrg == dischargeCurve[i][1])
+		return dischargeCurve[i][0];
+
+	/* Linear approximation of the charge to voltage */
+	ch_diff = chrg - dischargeCurve[i][1];
+	ch_total_diff = dischargeCurve[i-1][1] - dischargeCurve[i][1];
+	volt_total_diff = dischargeCurve[i-1][0] - dischargeCurve[i][0];
+
+	volt  =  dischargeCurve[i][0] +
+				(ch_diff * volt_total_diff) / ch_total_diff;
+
+	return volt;
+}
+
+static unsigned int msic_read_energy_now(struct msic_power_module_info *mbi)
+{
+	unsigned int vltg, chrg;
+
+	/* Read CC register value */
+	chrg = msic_read_coloumb_ctr();
+	/* Covert to milli coloumbs */
+	chrg = cc_to_coloumbs(chrg);
+
+	/* get voltage form lookup table */
+	vltg = chrg_to_vol_lookup(chrg);
+
+	/* Convert from mColoumbs to mAh */
+	chrg = chrg / COLMB_TO_MAHRS_CONV_FCTR;
+
+	return  (vltg * chrg) / 1000;
+}
+
+static int read_avg_ibatt(struct msic_power_module_info *mbi)
+{
+	int i, ibatt[HYSTR_SAMPLE_MAX], ibatt_avg = 0;
+
+	/* Measure  battey average current */
+	for (i = 0; i < HYSTR_SAMPLE_MAX; i++) {
+		ibatt[i] = mdf_read_adc_regs(MSIC_ADC_CUR_IDX, mbi);
+		ibatt_avg += ibatt[i];
+	}
+	ibatt_avg = ibatt_avg / HYSTR_SAMPLE_MAX;
+
+	return ibatt_avg;
+}
+
+static unsigned int calculate_batt_capacity(struct msic_power_module_info *mbi)
+{
+	int vocv, vbatt, ibatt;
+	unsigned int cap_perc;
+
+	vbatt = mdf_read_adc_regs(MSIC_ADC_VOL_IDX, mbi);
+	ibatt = read_avg_ibatt(mbi);
+
+	vocv = vbatt - (ibatt * MSIC_CHRG_RBATT_VAL) / 1000;
+
+	/* Temporary fix for calculating capacity.
+	 * will be modified once Coloumb Counter
+	 * works correctly.
+	 */
+	vocv = vocv - BATT_DEAD_CUTOFF_VOLT;
+	if (vocv < 0)
+		vocv = 0;
+
+	cap_perc = (vocv * 100) / (mbi->batt_props.vol_max_des -
+						BATT_DEAD_CUTOFF_VOLT);
+
+	/* vocv can be slighlty more than voltage
+	 * max value if the ibatt is -ve and vbatt
+	 * has reached a maximum value.
+	 */
+	if (cap_perc > 100)
+		cap_perc = 100;
+
+	return cap_perc;
+}
+
+static int msic_read_capacity_level(struct msic_power_module_info *mbi)
+{
+	int cap_perc, cap_level;
+
+	/* Calculate % of charge left */
+	cap_perc = calculate_batt_capacity(mbi);
+
+	/*
+	 * 0, 15, 40, 85 threshold cut-offs are temporary
+	 * actuall  thresholds will be read from SMIP header
+	 */
+	if (cap_perc >= 0 && cap_perc < 15)
+		cap_level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+	else if (cap_perc >= 15 && cap_perc < 40)
+		cap_level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+	else if (cap_perc >= 40 && cap_perc < 85)
+		cap_level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+	else if (cap_perc >= 85 && cap_perc < 100)
+		cap_level = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
+	else if (cap_perc == 100)
+		cap_level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+	else
+		cap_level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+
+	return cap_level;
+}
+
 /**
  * msic_battery_get_property - battery power source get property
  * @psy: battery power supply context
@@ -986,6 +1148,10 @@ static int msic_battery_get_property(struct power_supply *psy,
 		mbi->batt_props.charge_now = msic_get_charge_now();
 		val->intval = mbi->batt_props.charge_now * 1000;
 		break;
+	case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+		mbi->batt_props.charge_ctr = msic_read_coloumb_ctr();
+		val->intval =  mbi->batt_props.charge_ctr;
+		break;
 	case POWER_SUPPLY_PROP_CHARGE_FULL:
 		val->intval = mbi->batt_props.charge_full * 1000;
 		break;
@@ -994,7 +1160,19 @@ static int msic_battery_get_property(struct power_supply *psy,
 				mdf_cal_avg(mbi->batt_props.charge_avg);
 		val->intval = mbi->batt_props.charge_avg * 1000;
 		break;
+	case POWER_SUPPLY_PROP_ENERGY_FULL:
+		val->intval = mbi->batt_props.energy_full * 1000;
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_NOW:
+		mbi->batt_props.energy_now = msic_read_energy_now(mbi);
+		val->intval = mbi->batt_props.energy_now * 1000;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+		mbi->batt_props.capacity_level = msic_read_capacity_level(mbi);
+		val->intval = mbi->batt_props.capacity_level;
+		break;
 	case POWER_SUPPLY_PROP_CAPACITY:
+		mbi->batt_props.capacity = calculate_batt_capacity(mbi);
 		val->intval = mbi->batt_props.capacity;
 		break;
 	case POWER_SUPPLY_PROP_TEMP:
@@ -1930,9 +2108,15 @@ static void init_batt_props(struct msic_power_module_info *mbi)
 	mbi->batt_props.cur_now = 0x0;
 	mbi->batt_props.charge_full = CHARGE_FULL_IN_MAH;
 	mbi->batt_props.charge_now = 0x0;
+	mbi->batt_props.charge_ctr = 0x0;
 	mbi->batt_props.charge_avg = 0x0;
-	mbi->batt_props.capacity = 0;
-	mbi->batt_props.temperature = 0;
+	mbi->batt_props.capacity = 0x0;
+	mbi->batt_props.capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+	mbi->batt_props.temperature = 0x0;
+
+	mbi->batt_props.energy_now = 0x0;
+	mbi->batt_props.energy_full = (mbi->batt_props.vol_max_des *
+					mbi->batt_props.charge_full) / 1000;
 
 	memcpy(mbi->batt_props.vender, sfi_table->batt_id.manufac,
 				sizeof(sfi_table->batt_id.manufac));
-- 
1.7.2.3

