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
commit08a442e6134ed287308a5ec9c864b27fa01f3e24 (patch)
tree996c1b103d2e4090277d9bef4adb30f2fe428802
parentf1b855d5148ee7cb7ee78e688ffe8838c7cbf81e (diff)
downloadmrst-s0i3-test-08a442e6134ed287308a5ec9c864b27fa01f3e24.tar.gz
mrst-s0i3-test-08a442e6134ed287308a5ec9c864b27fa01f3e24.tar.xz
mrst-s0i3-test-08a442e6134ed287308a5ec9c864b27fa01f3e24.zip
Added Support for status monitoring
Added a worker function to monitor status if any fault conditions like Over Voltage, Over Temperature, etc.. happens. Removed most of the code from threaded IRQ function and improved exception handling code. 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.c248
1 files changed, 163 insertions, 85 deletions
diff --git a/drivers/power/intel_mdf_battery.c b/drivers/power/intel_mdf_battery.c
index 426cb841e4e..6385be842a1 100644
--- a/drivers/power/intel_mdf_battery.c
+++ b/drivers/power/intel_mdf_battery.c
@@ -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
@@ -215,6 +236,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
@@ -232,6 +254,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 */
@@ -402,6 +427,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
@@ -812,6 +840,7 @@ static unsigned int msic_read_coloumb_ctr(void)
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)
@@ -1062,6 +1091,18 @@ static void msic_log_exception_event(enum msic_event event)
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;
@@ -1079,52 +1120,40 @@ static void msic_handle_exception(struct msic_power_module_info *mbi,
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);
}
@@ -1132,16 +1161,32 @@ static void msic_handle_exception(struct msic_power_module_info *mbi,
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);
+ }
}
/**
@@ -1601,6 +1646,86 @@ static int msic_charger_callback(void *arg, int event, struct otg_bc_cap *cap)
}
/**
+ * 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
*
@@ -1640,8 +1765,7 @@ static irqreturn_t msic_battery_interrupt_handler(int id, void *dev)
*/
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;
@@ -1670,67 +1794,14 @@ static irqreturn_t msic_battery_thread_handler(int id, void *dev)
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;
@@ -1880,6 +1951,12 @@ static void init_batt_props(struct msic_power_module_info *mbi)
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__);
+
}
/**
@@ -1976,6 +2053,7 @@ static int msic_battery_probe(struct platform_device *pdev)
*/
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 */