Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ff38090a authored by Henrik Sölver's avatar Henrik Sölver Committed by Lee Jones
Browse files

ab8500-charger: AB workaround for invalid charger



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: default avatarHenrik Sölver <henrik.solver@stericsson.com>
Signed-off-by: default avatarLee Jones <lee.jones@linaro.org>
Reviewed-by: default avatarYvan FILLION <yvan.fillion@stericsson.com>
Reviewed-by: default avatarJonas ABERG <jonas.aberg@stericsson.com>
Tested-by: default avatarYvan FILLION <yvan.fillion@stericsson.com>
parent 01ec8c54
Loading
Loading
Loading
Loading
+55 −4
Original line number Diff line number Diff line
@@ -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
@@ -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;
@@ -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;
@@ -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:
@@ -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);
@@ -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);
@@ -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 */