--- drivers/power/intel_mdf_battery-Emrg-Chrg.c	2010-11-27 04:49:49.885435111 +0530
+++ drivers/power/intel_mdf_battery.c	2010-11-27 04:55:35.951194000 +0530
@@ -190,6 +190,27 @@
 #define MSIC_BATT_ADC_CHRGNG_MASK	(1 << 31)
 #define MSIC_BATT_ADC_ACCCHRGVAL_MASK   0x7FFFFFFF
 
+#define CHR_STATUS_FLULT_REG	0x37D
+#define CHR_STATUS_TMR_RST	(1 << 7)
+#define CHR_STATUS_VOTG_ENBL	(1 << 7)
+#define CHR_STATUS_STAT_ENBL	(1 << 6)
+
+#define CHR_STATUS_BIT_MASK	0x30
+#define CHR_STATUS_BIT_READY	0x0
+#define CHR_STATUS_BIT_PROGRESS	0x1
+#define CHR_STATUS_BIT_CYCLE	0x2
+#define CHR_STATUS_BIT_FAULT	0x3
+
+#define CHR_FAULT_BIT_MASK	0x7
+#define CHR_FAULT_BIT_NORMAL	0x0
+#define CHR_FAULT_BIT_VBUS_OVP	0x1
+#define CHR_FAULT_BIT_SLEEP	0x2
+#define CHR_FAULT_BIT_LOW_VBUS	0x3
+#define CHR_FAULT_BIT_BATT_OVP	0x4
+#define CHR_FAULT_BIT_THRM	0x5
+#define CHR_FAULT_BIT_TIMER	0x6
+#define CHR_FAULT_BIT_NO_BATT	0x7
+
 /*
  * Convert the voltage form decimal to
  * Register writable format
@@ -214,6 +235,7 @@
 #define IPCMSG_BATTERY		0xEF
 
 #define TEMP_CHARGE_DELAY_JIFFIES	(HZ * 30)	/*30 sec */
+#define CHARGE_STATUS_DELAY_JIFFIES	(HZ * 10)	/*10 sec */
 
 #define IRQ_FIFO_MAX		16
 #define THERM_CURVE_MAX_SAMPLES 7
@@ -231,6 +253,9 @@ enum msic_event {
 	MSIC_EVENT_ADPOVP_EXCPT,
 	MSIC_EVENT_CHROTP_EXCPT,
 	MSIC_EVENT_USBOVP_EXCPT,
+	MSIC_EVENT_USB_VINREG_EXCPT,
+	MSIC_EVENT_WEAKVIN_EXCPT,
+	MSIC_EVENT_TIMEEXP_EXCPT,
 };
 
 /* Valid Charging modes */
@@ -401,6 +426,9 @@ struct msic_power_module_info {
 	int charging_mode;
 	int emrg_chrg_enbl;	/* Emergency call charge enable */
 
+	/* Worker to monitor status and faluts */
+	struct delayed_work chr_status_monitor;
+
 	/* lock to avoid concurrent  access to HW Registers.
 	 * As some chargeer control and parameter registers
 	 * can be read or write at same time, ipc_rw_lock lock
@@ -810,6 +838,7 @@ static unsigned int msic_read_coloumb_ct
 	err = intel_scu_ipc_command(IPCMSG_BATTERY, 0x01, NULL, 0, &cvalue, 1);
 	if (err)
 		dev_warn(msic_dev, "IPC Command Failed %s\n", __func__);
+
 	return cvalue;
 }
 static unsigned int cc_to_coloumbs(unsigned int cc_val)
@@ -1060,6 +1089,18 @@ static void msic_log_exception_event(enu
 	case MSIC_EVENT_USBOVP_EXCPT:
 		dev_warn(msic_dev, "USB over voltage condition detected\n");
 		break;
+	case MSIC_EVENT_USB_VINREG_EXCPT:
+		dev_warn(msic_dev, "USB Input voltage regulation "
+						"condition detected\n");
+		break;
+	case MSIC_EVENT_WEAKVIN_EXCPT:
+		dev_warn(msic_dev, "USB Weak VBUS volatge "
+						"condition detected\n");
+		break;
+	case MSIC_EVENT_TIMEEXP_EXCPT:
+		dev_warn(msic_dev, "Charger Total Time Expiration "
+						"condition detected\n");
+		break;
 	default:
 		dev_warn(msic_dev, "unknown error %u detected\n", event);
 		break;
@@ -1077,52 +1118,40 @@ static void msic_handle_exception(struct
 		uint8_t CHRINT_reg_value, uint8_t CHRINT1_reg_value)
 {
 	enum msic_event exception;
-	uint8_t chrint_reg_value, chrint1_reg_value;
 
-	chrint_reg_value = CHRINT_reg_value;
-	chrint1_reg_value = CHRINT1_reg_value;
-
-	/* Battery Exceptions */
+	/* Battery Events */
 	mutex_lock(&mbi->batt_lock);
-	if (chrint_reg_value & MSIC_BATT_CHR_BATTOCP_MASK) {
+	if (CHRINT_reg_value & MSIC_BATT_CHR_BATTOCP_MASK) {
 		exception = MSIC_EVENT_BATTOCP_EXCPT;
-		mbi->batt_props.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
 		msic_log_exception_event(exception);
 	}
 
-	if (chrint1_reg_value & MSIC_BATT_CHR_BATTOTP_MASK) {
-		mbi->batt_props.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+	if (CHRINT_reg_value & MSIC_BATT_CHR_BATTOTP_MASK) {
 		mbi->batt_props.health = POWER_SUPPLY_HEALTH_OVERHEAT;
-
 		exception = MSIC_EVENT_BATTOTP_EXCPT;
 		msic_log_exception_event(exception);
 	}
 
 	if (CHRINT_reg_value & MSIC_BATT_CHR_LOWBATT_MASK) {
+		mbi->batt_props.health = POWER_SUPPLY_HEALTH_DEAD;
 		exception = MSIC_EVENT_LOWBATT_EXCPT;
 		msic_log_exception_event(exception);
 	}
+	if (CHRINT_reg_value & MSIC_BATT_CHR_TIMEEXP_MASK) {
+		exception = MSIC_EVENT_TIMEEXP_EXCPT;
+		msic_log_exception_event(exception);
+	}
 
 	if (CHRINT1_reg_value & MSIC_BATT_CHR_BATTOVP_MASK) {
-		mbi->batt_props.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
 		mbi->batt_props.health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-
 		exception = MSIC_EVENT_BATTOVP_EXCPT;
 		msic_log_exception_event(exception);
 	}
 	mutex_unlock(&mbi->batt_lock);
 
-
+	/* Charger Events */
 	mutex_lock(&mbi->usb_chrg_lock);
-	/* Charger exceptions */
 	if (CHRINT1_reg_value & MSIC_BATT_CHR_CHROTP_MASK) {
-		mbi->usb_chrg_props.charger_health =
-						POWER_SUPPLY_HEALTH_OVERHEAT;
-
-		mutex_lock(&mbi->batt_lock);
-		mbi->batt_props.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-		mutex_unlock(&mbi->batt_lock);
-
 		exception = MSIC_EVENT_CHROTP_EXCPT;
 		msic_log_exception_event(exception);
 	}
@@ -1130,16 +1159,32 @@ static void msic_handle_exception(struct
 	if (CHRINT1_reg_value & MSIC_BATT_CHR_USBOVP_MASK) {
 		mbi->usb_chrg_props.charger_health =
 						POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-
-		mutex_lock(&mbi->batt_lock);
-		mbi->batt_props.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-		mutex_unlock(&mbi->batt_lock);
-
 		exception = MSIC_EVENT_USBOVP_EXCPT;
 		msic_log_exception_event(exception);
 	}
+	if (CHRINT1_reg_value & MSIC_BATT_CHR_WKVINDET_MASK) {
+		mbi->usb_chrg_props.charger_health =
+						POWER_SUPPLY_HEALTH_DEAD;
+		exception = MSIC_EVENT_WEAKVIN_EXCPT;
+		msic_log_exception_event(exception);
+	}
+	if (CHRINT1_reg_value & MSIC_BATT_CHR_VINREGMINT_MASK) {
+		mbi->usb_chrg_props.charger_health =
+						POWER_SUPPLY_HEALTH_DEAD;
+		exception = MSIC_EVENT_USB_VINREG_EXCPT;
+		msic_log_exception_event(exception);
+	}
 	mutex_unlock(&mbi->usb_chrg_lock);
 
+	if (CHRINT1_reg_value || (CHRINT_reg_value &
+				~(MSIC_BATT_CHR_LOWBATT_MASK))) {
+		mutex_lock(&mbi->batt_lock);
+		if ((mbi->batt_props.status == POWER_SUPPLY_STATUS_CHARGING) ||
+			(mbi->batt_props.status == POWER_SUPPLY_STATUS_FULL))
+			mbi->batt_props.status =
+					POWER_SUPPLY_STATUS_NOT_CHARGING;
+		mutex_unlock(&mbi->batt_lock);
+	}
 }
 
 /**
@@ -1599,6 +1644,86 @@ static int msic_charger_callback(void *a
 }
 
 /**
+ * msic_status_monitor - worker function to monitor status
+ * @work: delayed work handler structure
+ * Context: Can sleep
+ *
+ * Will be called from the threaded IRQ function.
+ * Monitors status of the charge register and temperature.
+ */
+static void msic_status_monitor(struct work_struct *work)
+{
+	uint8_t data;
+	int retval, val, volt, temp;
+	struct msic_power_module_info *mbi = container_of(work,
+			struct msic_power_module_info, chr_status_monitor.work);
+
+	retval = intel_scu_ipc_ioread8(CHR_STATUS_FLULT_REG, &data);
+	if (retval) {
+		dev_warn(msic_dev, "%s:ipc read failed\n", __func__);
+		return ;
+	}
+
+	/* Check Fluts bits in Status Register */
+	val = (data & CHR_STATUS_BIT_MASK) >> 4;
+	if (val == CHR_STATUS_BIT_FAULT) {
+		dev_dbg(msic_dev, "chr status regisrer:%x\n", data);
+		schedule_delayed_work(&mbi->chr_status_monitor,
+					CHARGE_STATUS_DELAY_JIFFIES);
+		return ;
+	}
+
+	/* Compute Charger health */
+	mutex_lock(&mbi->usb_chrg_lock);
+	if (mbi->usb_chrg_props.charger_health != POWER_SUPPLY_HEALTH_GOOD)
+		mbi->usb_chrg_props.charger_health = POWER_SUPPLY_HEALTH_GOOD;
+	mutex_unlock(&mbi->usb_chrg_lock);
+
+	/* Compute Battery health */
+	mutex_lock(&mbi->batt_lock);
+	if (mbi->batt_props.health == POWER_SUPPLY_HEALTH_OVERHEAT) {
+		temp = mdf_read_adc_regs(MSIC_ADC_TEMP_IDX, mbi);
+		dev_dbg(msic_dev, "temp in millC :%d\n", temp);
+		/*
+		 * Valid temperature window is 0 to 60 Degrees
+		 * and thermister has 2 drgee hysteris and considering
+		 * 2 degree adc error, fault revert temperature will
+		 * be 4 to 56 degrees. These vaules will be fine tuned later.
+		 */
+		if (temp >= (MSIC_BATT_TEMP_MAX - MSIC_TEMP_HYST_ERR) ||
+			temp <= (MSIC_BATT_TEMP_MIN + MSIC_TEMP_HYST_ERR)) {
+			schedule_delayed_work(&mbi->chr_status_monitor,
+						CHARGE_STATUS_DELAY_JIFFIES);
+			mutex_unlock(&mbi->batt_lock);
+			return ;
+		}
+	}
+
+	spin_lock(&mbi->event_lock);
+	if (mbi->charging_mode == BATT_CHARGING_MODE_MAINTAINENCE)
+		mbi->batt_props.status = POWER_SUPPLY_STATUS_FULL;
+	else if (mbi->charging_mode == BATT_CHARGING_MODE_NORMAL)
+		mbi->batt_props.status = POWER_SUPPLY_STATUS_CHARGING;
+	else if (mbi->batt_event == USBCHRG_EVENT_SUSPEND)
+		mbi->batt_props.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+	else
+		mbi->batt_props.status = POWER_SUPPLY_STATUS_DISCHARGING;
+	spin_unlock(&mbi->event_lock);
+
+	if (mbi->batt_props.health == POWER_SUPPLY_HEALTH_DEAD) {
+		volt = mdf_read_adc_regs(MSIC_ADC_VOL_IDX, mbi);
+		dev_dbg(msic_dev, "low vol in mV :%d\n", volt);
+		if (volt < BATT_LOWBATT_CUTOFF_VOLT) {
+			schedule_delayed_work(&mbi->chr_status_monitor,
+						CHARGE_STATUS_DELAY_JIFFIES);
+			mutex_unlock(&mbi->batt_lock);
+			return ;
+		}
+	}
+	mbi->batt_props.health = POWER_SUPPLY_HEALTH_GOOD;
+	mutex_unlock(&mbi->batt_lock);
+}
+/**
  * msic_battery_interrupt_handler - msic battery interrupt handler
  * Context: interrupt context
  *
@@ -1638,8 +1763,7 @@ static irqreturn_t msic_battery_interrup
  */
 static irqreturn_t msic_battery_thread_handler(int id, void *dev)
 {
-	int err, ret;
-	uint32_t batt_charge_val;
+	int ret;
 	unsigned char data[2];
 	struct msic_power_module_info *mbi = dev;
 	u32 tmp;
@@ -1668,67 +1792,14 @@ static irqreturn_t msic_battery_thread_h
 	dev_dbg(msic_dev, "CHR Int %x %x\n", data[0], data[1]);
 
 	/* Check if charge complete */
-	if (data[1] & MSIC_BATT_CHR_CHRCMPLT_MASK) {
+	if (data[1] & MSIC_BATT_CHR_CHRCMPLT_MASK)
 		dev_dbg(msic_dev, "CHRG COMPLT\n");
-		/* Disable Charging */
-		msic_batt_stop_charging(mbi);
-
-		spin_lock(&mbi->event_lock);
-		mbi->charging_mode = BATT_CHARGING_MODE_MAINTAINENCE;
-		spin_unlock(&mbi->event_lock);
-
-
-		err = intel_scu_ipc_command(IPCMSG_BATTERY, 0x01, NULL,
-						 0, &batt_charge_val, 1);
-		mutex_lock(&mbi->batt_lock);
-		if (!err)
-			mbi->batt_props.charge_full = batt_charge_val;
-		mbi->batt_props.status = POWER_SUPPLY_STATUS_FULL;
-		mutex_unlock(&mbi->batt_lock);
-
-	}
-
-	if ((data[1] & MSIC_BATT_CHR_WKVINDET_MASK) ||
-		(data[1] & MSIC_BATT_CHR_VINREGMINT_MASK)) {
-
-		dev_dbg(msic_dev, "WEAK VIN or VINREGMINT DETCTED\n");
-
-		spin_lock(&mbi->event_lock);
-		/* Sometimes we may get weakVIN because of VBUS voltage
-		 * drops even though there is no charger connected.
-		 * So before we suspend the device check for the
-		 * battery connection.
-		 */
-		if (mbi->batt_event != USBCHRG_EVENT_SUSPEND ||
-			mbi->batt_event != USBCHRG_EVENT_DISCONN) {
-			spin_unlock(&mbi->event_lock);
-			/* In case of weakVIN the device can not recover from
-			 * low voltage level.So its better to disconnect the
-			 * charger and reconnect it again. From driver point
-			 * of view we will just suspend the device.
-			 */
-			msic_charger_callback(mbi, USBCHRG_EVENT_SUSPEND, NULL);
-		} else {
-			spin_unlock(&mbi->event_lock);
-			dev_dbg(msic_dev, "WeakVIN when No charger preset\n");
-		}
-	}
-
-	/* Check if total charge time expired */
-	if (data[0] & MSIC_BATT_CHR_TIMEEXP_MASK) {
-		dev_dbg(msic_dev, "CHR TIMER EXP\n");
-		mutex_lock(&mbi->batt_lock);
-		mbi->batt_props.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-		mutex_unlock(&mbi->batt_lock);
-	}
 
 	/* Check if an exception occured */
-	if ((data[0] & ~MSIC_BATT_CHR_TIMEEXP_MASK) ||
-		(data[1] & ~(MSIC_BATT_CHR_CHRCMPLT_MASK |
-			MSIC_BATT_CHR_WKVINDET_MASK |
-			MSIC_BATT_CHR_VINREGMINT_MASK))) {
-
+	if (data[0] || (data[1] & ~(MSIC_BATT_CHR_CHRCMPLT_MASK))) {
 		msic_handle_exception(mbi, data[0], data[1]);
+		schedule_delayed_work(&mbi->chr_status_monitor,
+					CHARGE_STATUS_DELAY_JIFFIES);
 	}
 
 	return IRQ_HANDLED;
@@ -1878,6 +1949,12 @@ static void init_batt_props(struct msic_
 	else
 		mbi->batt_props.present = MSIC_BATT_NOT_PRESENT;
 
+	/* Enable Status Register */
+	retval = intel_scu_ipc_iowrite8(CHR_STATUS_FLULT_REG,
+				CHR_STATUS_TMR_RST | CHR_STATUS_STAT_ENBL);
+	if (retval)
+		dev_warn(&mbi->pdev->dev, "%s:ipc r/w failed\n", __func__);
+
 }
 
 /**
@@ -1974,6 +2051,7 @@ static int msic_battery_probe(struct pla
 	 */
 	INIT_DELAYED_WORK(&mbi->disconn_handler, msic_batt_disconn);
 	INIT_DELAYED_WORK(&mbi->connect_handler, msic_batt_temp_charging);
+	INIT_DELAYED_WORK(&mbi->chr_status_monitor, msic_status_monitor);
 
 
 	/* Initialize the spin locks */
