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
commit1fc57bbd3cd5bdf3e86341432b4b46ad71de72f3 (patch)
tree2f1f69b8564a9d399c23cc2b6d227d50ee4dec3f
parentea2d256bbbc11a09f87c2e6033a843e6d29464b7 (diff)
downloadmrst-s0i3-test-1fc57bbd3cd5bdf3e86341432b4b46ad71de72f3.tar.gz
mrst-s0i3-test-1fc57bbd3cd5bdf3e86341432b4b46ad71de72f3.tar.xz
mrst-s0i3-test-1fc57bbd3cd5bdf3e86341432b4b46ad71de72f3.zip
Added Support for SW Charge Termination
Check_charge_full function is added to determine the charge full condition. Added a function calculate the charge cycles. Temperature based charging worker is modified according to support SW charge termination and maintenance charging. 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.c225
1 files changed, 189 insertions, 36 deletions
diff --git a/drivers/power/intel_mdf_battery.c b/drivers/power/intel_mdf_battery.c
index 5ae23fe5dee..7a24eebcfaa 100644
--- a/drivers/power/intel_mdf_battery.c
+++ b/drivers/power/intel_mdf_battery.c
@@ -301,6 +301,11 @@ enum {
static void *otg_handle;
static struct device *msic_dev;
+/* Variables for counting charge cycles */
+static unsigned int charge_cycle_ctr;
+static unsigned int chr_clmb_ctr;
+
+
/*
* This array represents the Battery Pack thermistor
* temarature and corresponding ADC value limits
@@ -1495,7 +1500,12 @@ static int msic_batt_do_charging(struct msic_power_module_info *mbi,
dev_dbg(msic_dev, "Charger Enabled\n");
mutex_lock(&mbi->batt_lock);
- mbi->batt_props.status = POWER_SUPPLY_STATUS_CHARGING;
+ spin_lock(&mbi->event_lock);
+ if (mbi->charging_mode == BATT_CHARGING_MODE_MAINTAINENCE)
+ mbi->batt_props.status = POWER_SUPPLY_STATUS_FULL;
+ else
+ mbi->batt_props.status = POWER_SUPPLY_STATUS_CHARGING;
+ spin_unlock(&mbi->event_lock);
mbi->batt_props.health = POWER_SUPPLY_HEALTH_GOOD;
mutex_unlock(&mbi->batt_lock);
@@ -1572,6 +1582,111 @@ static void msic_update_disconn_status(struct msic_power_module_info *mbi)
power_supply_changed(&mbi->batt);
}
+static int check_charge_full(struct msic_power_module_info *mbi, int vref)
+{
+ static int vbat_prev[HYSTR_SAMPLE_MAX];
+ int vflag, vbat, vbat_diff, vocv, i;
+ int ibatt_avg, is_full = false;
+
+ /* Read battey vbatt */
+ vbat = mdf_read_adc_regs(MSIC_ADC_VOL_IDX, mbi);
+ /* Read battery current */
+ ibatt_avg = read_avg_ibatt(mbi);
+
+ /* Check the Vbat is consitant with in the range */
+ for (i = 0; i < HYSTR_SAMPLE_MAX; i++) {
+ vbat_diff = vbat - vbat_prev[i];
+ if (vbat_diff >= -20 && vbat_diff <= 20)
+ continue;
+ else
+ break;
+ }
+
+ if (i >= HYSTR_SAMPLE_MAX)
+ vflag = true;
+ else
+ vflag = false;
+
+ /*
+ * Rbatt value should be read from SMIP header but as a
+ * temporary fix we are hardcoding the Rbatt as 180 mOhms
+ */
+ vocv = vbat - (ibatt_avg * MSIC_CHRG_RBATT_VAL)/1000;
+ dev_dbg(msic_dev, "vocv:%d vbat:%d\n", vocv, vbat);
+
+ /*
+ * Check if Vbat is equal to max charge voltage and
+ * charge rate is less than or equal to 0.05 coloumbs
+ */
+ if (vflag && (vocv >= (vref - BATT_ADC_VOLT_ERROR))) {
+ dev_dbg(msic_dev, "Charge Full Detected\n");
+ /* Disable Charging */
+ msic_batt_stop_charging(mbi);
+ is_full = true;
+ }
+
+ /* Push the array with latest value */
+ for (i = HYSTR_SAMPLE_MAX - 1; i > 0; i--)
+ vbat_prev[i] = vbat_prev[i-1];
+ vbat_prev[0] = vbat;
+
+ return is_full;
+}
+
+static bool is_maint_cutoff_vol(int adc_volt)
+{
+ static int vmaint_prev[HYSTR_SAMPLE_MAX];
+ int i;
+ bool flag;
+
+ /* Check whether the voltage is droping
+ * consistantly to enable maintenence charging
+ */
+ for (i = 0; i < HYSTR_SAMPLE_MAX; i++) {
+ if (adc_volt < vmaint_prev[i])
+ continue;
+ else
+ break;
+ }
+
+ if (i >= HYSTR_SAMPLE_MAX)
+ flag = true;
+ else
+ flag = false;
+
+ /* Push the array with latest value */
+ for (i = HYSTR_SAMPLE_MAX - 1; i > 0; i--)
+ vmaint_prev[i] = vmaint_prev[i-1];
+ vmaint_prev[0] = adc_volt;
+
+ return flag;
+}
+
+static void calculate_charge_cycles(struct msic_power_module_info *mbi,
+ bool start_bit)
+{
+ static unsigned int chr_prev;
+ unsigned int chr_now, chr_now_ctr, chr_prev_ctr = 0;
+
+ if (start_bit)
+ chr_prev = 0;
+
+ chr_now = msic_read_coloumb_ctr();
+ chr_now_ctr = cc_to_coloumbs(chr_now);
+
+ chr_prev_ctr = cc_to_coloumbs(chr_prev);
+
+ /* count inflow charge given by charger */
+ if (!chr_prev)
+ chr_clmb_ctr += chr_now_ctr - chr_prev_ctr;
+
+ if (chr_clmb_ctr >= CHARGE_FULL_IN_MAH) {
+ charge_cycle_ctr++;
+ chr_clmb_ctr = 0;
+ }
+ chr_prev = chr_now;
+}
+
/**
* msic_batt_temp_charging - manages the charging based on temperature
* @charge_param: charging parameter
@@ -1582,9 +1697,9 @@ static void msic_update_disconn_status(struct msic_power_module_info *mbi)
*/
static void msic_batt_temp_charging(struct work_struct *work)
{
- int ret, i;
- static int iprev = -1;
- short int cv = 0, cc = 0, vinlimit = 0;
+ int ret, i, charge_now, is_maint_chrg = false;
+ static int iprev = -1, is_chrg_enbl;
+ short int cv = 0, cc = 0, vinlimit = 0, cvref;
int adc_temp, adc_vol;
struct charge_params charge_param;
struct msic_power_module_info *mbi = container_of(work,
@@ -1600,13 +1715,13 @@ static void msic_batt_temp_charging(struct work_struct *work)
POWER_SUPPLY_CHARGE_TYPE_UNKNOWN ||
mbi->usb_chrg_props.charger_type ==
POWER_SUPPLY_CHARGE_TYPE_NONE) {
-
/*
* If the the charger type is unknown or None
* better start the charging again and compute
* the properties again.
*/
iprev = -1;
+ is_chrg_enbl = false;
}
mutex_unlock(&mbi->usb_chrg_lock);
@@ -1620,6 +1735,7 @@ static void msic_batt_temp_charging(struct work_struct *work)
cv = sfi_table->temp_mon_range[i].full_chrg_vol;
cc = sfi_table->temp_mon_range[i].full_chrg_cur;
+ cvref = cv;
/* D7,D6 bits of CHRCNTL will set the VINILMT */
if (charge_param.vinilmt > 950)
@@ -1637,48 +1753,71 @@ static void msic_batt_temp_charging(struct work_struct *work)
if (i >= sfi_table->temp_mon_ranges) {
dev_warn(msic_dev, "TEMP RANGE NOT EXIST\n");
+ /*
+ * If we are in middle of charge cycle is safer to Reset WDT
+ * Timer Register.Because battery temperature and Changre
+ * status register are not related.
+ */
+ reset_wdt_timer(mbi);
goto lbl_sched_work;
}
/*
+ * Check for Charge full condition and set the battery
+ * properties accordingly. Also check for charging mode
+ * whether it is normal or maintenence mode.
+ */
+ spin_lock(&mbi->event_lock);
+ if (mbi->charging_mode == BATT_CHARGING_MODE_MAINTAINENCE) {
+ cvref = sfi_table->temp_mon_range[i].maint_chrg_vol_ul;
+ is_maint_chrg = true;
+ }
+ ret = check_charge_full(mbi, cvref);
+ if (ret) {
+ is_chrg_enbl = false;
+ if (mbi->charging_mode != BATT_CHARGING_MODE_MAINTAINENCE) {
+ dev_dbg(msic_dev, "Going to Maintenence CHRG Mode\n");
+ mbi->charging_mode = BATT_CHARGING_MODE_MAINTAINENCE;
+ is_maint_chrg = true;
+
+ mutex_lock(&mbi->batt_lock);
+ charge_now = msic_read_coloumb_ctr();
+ mbi->batt_props.charge_full =
+ cc_to_coloumbs(charge_now);
+ mbi->batt_props.status = POWER_SUPPLY_STATUS_FULL;
+ mutex_unlock(&mbi->batt_lock);
+ }
+ }
+ spin_unlock(&mbi->event_lock);
+
+ /*
* If we are in same Temparature range check for check for the
* maintainence charging mode and enable the charging depending
- * on the adc voltage and lower threshold.
- * If Temparature range is changed then anyways we need to set
- * charging parameters and enable charging, in that case no need
- * to check for Maintainence mode.
+ * on the voltage.If Temparature range is changed then anyways
+ * we need to set charging parameters and enable charging.
*/
if (i == iprev) {
-
- spin_lock(&mbi->event_lock);
- if (mbi->charging_mode == BATT_CHARGING_MODE_MAINTAINENCE) {
-
- spin_unlock(&mbi->event_lock);
+ /*
+ * Check if the voltage falls below lower threshold
+ * if we are in maintenence mode charging.
+ */
+ if (is_maint_chrg && !is_chrg_enbl) {
temp_mon = &sfi_table->temp_mon_range[i];
- /* Read ADC value for battery Voltage */
+ /* Read battery Voltage */
adc_vol = mdf_read_adc_regs(MSIC_ADC_VOL_IDX, mbi);
- /*
- * Check if the voltage falls below lower threshold
- * set charge voltage to maintainence voltage upper
- * limit and enable charging otherwise just
- * schedule the work
- */
- if (adc_vol <= temp_mon->maint_chrg_vol_ll)
+ /* Cosidering ADC error of BATT_ADC_VOLT_ERROR */
+ if ((adc_vol + 20) <= temp_mon->maint_chrg_vol_ll &&
+ is_maint_cutoff_vol(adc_vol)) {
+ dev_dbg(msic_dev, "restart charging\n");
cv = temp_mon->maint_chrg_vol_ul;
- else
+ } else {
+ dev_dbg(msic_dev, "vbat is more than ll\n");
goto lbl_sched_work;
-
+ }
} else {
-
- spin_unlock(&mbi->event_lock);
-
- /*
- * We are not in normal Mode and no change in temp
- * range so just write the WDT timer and
- * schedule the wirk again.
- */
- dev_dbg(msic_dev, "NoChange in Temp Range\n");
+ /* count charge cycles */
+ calculate_charge_cycles(mbi, false);
/* Reset WDT Timer Register for 60 Sec */
reset_wdt_timer(mbi);
goto lbl_sched_work;
@@ -1696,9 +1835,6 @@ static void msic_batt_temp_charging(struct work_struct *work)
cc = cc << 3;
charge_param.ccur = cc;
-
- /* Enable Charge Termination */
- vinlimit |= 0x08;
charge_param.vinilmt = vinlimit;
dev_dbg(msic_dev, "params vol: %x cur:%x vinilmt:%x\n",
@@ -1726,6 +1862,9 @@ static void msic_batt_temp_charging(struct work_struct *work)
dev_warn(msic_dev, "msic_batt_do_charging failed\n");
goto lbl_sched_work;
}
+ /* Start cycle counting */
+ calculate_charge_cycles(mbi, true);
+ is_chrg_enbl = true;
power_supply_changed(&mbi->usb);
lbl_sched_work:
@@ -2353,6 +2492,19 @@ ioremap_failed:
return retval;
}
+static void do_exit_ops(struct msic_power_module_info *mbi)
+{
+ /* disable MSIC Charger */
+ mutex_lock(&mbi->batt_lock);
+ if (mbi->batt_props.status != POWER_SUPPLY_STATUS_DISCHARGING)
+ msic_batt_stop_charging(mbi);
+ mutex_unlock(&mbi->batt_lock);
+
+ /* Check charge Cycles */
+ if (chr_clmb_ctr >= (CHARGE_FULL_IN_MAH / 2))
+ charge_cycle_ctr++;
+}
+
/**
* msic_battery_remove - msic battery finalize
* @pdev: msic battery platform device structure
@@ -2371,6 +2523,7 @@ static int msic_battery_remove(struct platform_device *pdev)
flush_scheduled_work();
free_adc_channels(mbi->adc_index, mbi);
free_irq(mbi->irq, mbi);
+ do_exit_ops(mbi);
if (mbi->msic_regs_iomap != NULL)
iounmap(mbi->msic_regs_iomap);
device_remove_file(&pdev->dev, &dev_attr_emrg_charge_enable);