From 504970e04ba15aa8a0c3a6f69991789315692589 Mon Sep 17 00:00:00 2001
From: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
Date: Tue, 30 Nov 2010 22:38:32 +0530
Subject: [PATCH] Added Support for SW Charge Termination

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

diff --git a/drivers/power/intel_mdf_battery.c b/drivers/power/intel_mdf_battery.c
index 8de4cd6..093d889 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
@@ -1494,7 +1499,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);
 
@@ -1571,6 +1581,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
@@ -1581,9 +1696,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,
@@ -1599,13 +1714,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);
 
@@ -1619,6 +1734,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)
@@ -1636,48 +1752,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;
@@ -1695,9 +1834,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",
@@ -1725,6 +1861,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:
@@ -2350,6 +2489,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
@@ -2368,6 +2520,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);
-- 
1.7.2.3

