in supply/ab8500_charger.c [2280:2408]
static void ab8500_charger_usb_link_status_work(struct work_struct *work)
{
int detected_chargers;
int ret;
u8 val;
u8 link_status;
struct ab8500_charger *di = container_of(work,
struct ab8500_charger, usb_link_status_work);
/*
* Since we can't be sure that the events are received
* synchronously, we have the check if is
* connected by reading the status register
*/
detected_chargers = ab8500_charger_detect_chargers(di, false);
if (detected_chargers < 0)
return;
/*
* Some chargers that breaks the USB spec is
* identified as invalid by AB8500 and it refuse
* to start the charging process. but by jumping
* through a few hoops it can be forced to start.
*/
if (is_ab8500(di->parent))
ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
AB8500_USB_LINE_STAT_REG, &val);
else
ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
AB8500_USB_LINK1_STAT_REG, &val);
if (ret >= 0)
dev_dbg(di->dev, "UsbLineStatus register = 0x%02x\n", val);
else
dev_dbg(di->dev, "Error reading USB link status\n");
if (is_ab8500(di->parent))
link_status = AB8500_USB_LINK_STATUS;
else
link_status = AB8505_USB_LINK_STATUS;
if (detected_chargers & USB_PW_CONN) {
if (((val & link_status) >> USB_LINK_STATUS_SHIFT) ==
USB_STAT_NOT_VALID_LINK &&
di->invalid_charger_detect_state == 0) {
dev_dbg(di->dev,
"Invalid charger detected, state= 0\n");
/*Enable charger*/
abx500_mask_and_set_register_interruptible(di->dev,
AB8500_CHARGER, AB8500_USBCH_CTRL1_REG,
USB_CH_ENA, USB_CH_ENA);
/*Enable charger detection*/
abx500_mask_and_set_register_interruptible(di->dev,
AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
USB_CH_DET, USB_CH_DET);
di->invalid_charger_detect_state = 1;
/*exit and wait for new link status interrupt.*/
return;
}
if (di->invalid_charger_detect_state == 1) {
dev_dbg(di->dev,
"Invalid charger detected, state= 1\n");
/*Stop charger detection*/
abx500_mask_and_set_register_interruptible(di->dev,
AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
USB_CH_DET, 0x00);
/*Check link status*/
if (is_ab8500(di->parent))
ret = abx500_get_register_interruptible(di->dev,
AB8500_USB, AB8500_USB_LINE_STAT_REG,
&val);
else
ret = abx500_get_register_interruptible(di->dev,
AB8500_USB, AB8500_USB_LINK1_STAT_REG,
&val);
dev_dbg(di->dev, "USB link status= 0x%02x\n",
(val & link_status) >> USB_LINK_STATUS_SHIFT);
di->invalid_charger_detect_state = 2;
}
} else {
di->invalid_charger_detect_state = 0;
}
if (!(detected_chargers & USB_PW_CONN)) {
di->vbus_detected = false;
ab8500_charger_set_usb_connected(di, false);
ab8500_power_supply_changed(di, di->usb_chg.psy);
return;
}
dev_dbg(di->dev,"%s di->vbus_detected = true\n",__func__);
di->vbus_detected = true;
ret = ab8500_charger_read_usb_type(di);
if (ret) {
if (ret == -ENXIO) {
/* No valid charger type detected */
ab8500_charger_set_usb_connected(di, false);
ab8500_power_supply_changed(di, di->usb_chg.psy);
}
return;
}
if (di->usb_device_is_unrecognised) {
dev_dbg(di->dev,
"Potential Legacy Charger device. "
"Delay work for %d msec for USB enum "
"to finish",
WAIT_ACA_RID_ENUMERATION);
queue_delayed_work(di->charger_wq,
&di->attach_work,
msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
} else if (di->is_aca_rid == 1) {
/* Only wait once */
di->is_aca_rid++;
dev_dbg(di->dev,
"%s Wait %d msec for USB enum to finish",
__func__, WAIT_ACA_RID_ENUMERATION);
queue_delayed_work(di->charger_wq,
&di->attach_work,
msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
} else {
queue_delayed_work(di->charger_wq,
&di->attach_work,
0);
}
}