aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRamakrishna Pallala <ramakrishna.pallala@intel.com>2010-12-09 10:37:48 +0000
committerAlan Cox <alan@linux.intel.com>2010-12-09 10:37:48 +0000
commitea2d256bbbc11a09f87c2e6033a843e6d29464b7 (patch)
treefe9525a32d8a85af75a14bcc0a32b62df626f1dd
parent08a442e6134ed287308a5ec9c864b27fa01f3e24 (diff)
downloadmrst-s0i3-test-ea2d256bbbc11a09f87c2e6033a843e6d29464b7.tar.gz
mrst-s0i3-test-ea2d256bbbc11a09f87c2e6033a843e6d29464b7.tar.xz
mrst-s0i3-test-ea2d256bbbc11a09f87c2e6033a843e6d29464b7.zip
Added Support for Fuel Gauging
To support fuel gauging Energy now, Energy full, charge counter and capacity level Sysfs Interfaces are added to driver. A table is added to look up voltage against charge. Signed-off-by: Ramakrishna Pallala <ramakrishna.pallala@intel.com> Signed-off-by: Alan Cox <alan@linux.intel.com>
-rw-r--r--drivers/power/intel_mdf_battery.c196
1 files changed, 190 insertions, 6 deletions
diff --git a/drivers/power/intel_mdf_battery.c b/drivers/power/intel_mdf_battery.c
index 6385be842a1..5ae23fe5dee 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;
@@ -931,6 +971,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
@@ -987,6 +1149,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;
@@ -995,7 +1161,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:
@@ -1931,9 +2109,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));