Skip to content

Commit

Permalink
ab8500-charger: AB workaround for invalid charger
Browse files Browse the repository at this point in the history
AB8500 refuses to start charging when some types of non standard
chargers are connected. This change force the AB to start charging.

Signed-off-by: Henrik Sölver <henrik.solver@stericsson.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Reviewed-by: Yvan FILLION <yvan.fillion@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Tested-by: Yvan FILLION <yvan.fillion@stericsson.com>
  • Loading branch information
Henrik Sölver authored and Lee Jones committed Jan 23, 2013
1 parent 01ec8c5 commit ff38090
Showing 1 changed file with 55 additions and 4 deletions.
59 changes: 55 additions & 4 deletions drivers/power/ab8500_charger.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ struct ab8500_charger_usb_state {
* @usb_device_is_unrecognised USB device is unrecognised by the hardware
* @autopower Indicate if we should have automatic pwron after pwrloss
* @autopower_cfg platform specific power config support for "pwron after pwrloss"
* @invalid_charger_detect_state State when forcing AB to use invalid charger
* @parent: Pointer to the struct ab8500
* @gpadc: Pointer to the struct gpadc
* @bm: Platform specific battery management information
Expand Down Expand Up @@ -251,6 +252,7 @@ struct ab8500_charger {
bool usb_device_is_unrecognised;
bool autopower;
bool autopower_cfg;
int invalid_charger_detect_state;
struct ab8500 *parent;
struct ab8500_gpadc *gpadc;
struct abx500_bm_data *bm;
Expand Down Expand Up @@ -659,7 +661,6 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
break;
}
case USB_STAT_HM_IDGND:
case USB_STAT_NOT_VALID_LINK:
dev_err(di->dev, "USB Type - Charging not allowed\n");
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
ret = -ENXIO;
Expand Down Expand Up @@ -688,6 +689,9 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
di->max_usb_in_curr);
case USB_STAT_NOT_VALID_LINK:
dev_err(di->dev, "USB Type invalid - try charging anyway\n");
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
break;

default:
Expand Down Expand Up @@ -1957,7 +1961,9 @@ static void ab8500_charger_usb_link_attach_work(struct work_struct *work)
*/
static void ab8500_charger_usb_link_status_work(struct work_struct *work)
{
int detected_chargers;
int ret;
u8 val;

struct ab8500_charger *di = container_of(work,
struct ab8500_charger, usb_link_status_work);
Expand All @@ -1967,11 +1973,55 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
* synchronously, we have the check if is
* connected by reading the status register
*/
ret = ab8500_charger_detect_chargers(di);
if (ret < 0)
detected_chargers = ab8500_charger_detect_chargers(di);
if (detected_chargers < 0)
return;

if (!(ret & USB_PW_CONN)) {
/*
* Some chargers that breaks the USB spec is
* identified as invalid by AB8500 and it refuse
* to start the charging process. but by jumping
* thru a few hoops it can be forced to start.
*/
ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
AB8500_USB_LINE_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 (detected_chargers & USB_PW_CONN) {
if (((val & AB8500_USB_LINK_STATUS) >> 3) == 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, 0x01, 0x01);
/*Enable charger detection*/
abx500_mask_and_set_register_interruptible(di->dev, AB8500_USB,
AB8500_MCH_IPT_CURLVL_REG, 0x01, 0x01);
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_MCH_IPT_CURLVL_REG, 0x01, 0x00);
/*Check link status*/
ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
AB8500_USB_LINE_STAT_REG, &val);
dev_dbg(di->dev, "USB link status= 0x%02x\n",
(val & AB8500_USB_LINK_STATUS) >> 3);
di->invalid_charger_detect_state = 2;
}
} else {
di->invalid_charger_detect_state = 0;
}

if (!(detected_chargers & USB_PW_CONN)) {
di->vbus_detected = 0;
ab8500_charger_set_usb_connected(di, false);
ab8500_power_supply_changed(di, &di->usb_chg.psy);
Expand Down Expand Up @@ -2884,6 +2934,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
spin_lock_init(&di->usb_state.usb_lock);

di->autopower = false;
di->invalid_charger_detect_state = 0;

/* AC supply */
/* power_supply base class */
Expand Down

0 comments on commit ff38090

Please sign in to comment.