aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 */