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

Commit 2882ba56 authored by Elson Roy Serrao's avatar Elson Roy Serrao
Browse files

usb: dwc3: Drive a pulse on DP on CDP detection



Some USB host PCs switch the downstream port mode from
CDP to SDP if the device does not pull DP up within certain
duration (~2s).
This limits the current drawn by the device to 500mA(or 900mA)
as opposed to the possible 1.5A resulting in slow charging of
the battery.
Fix this by driving a DP pulse when there is a connect
notification from PMIC and detected the charger type as CDP.

Change-Id: I7655a0812ba85bdc29fc4f66d4675717f5b4cd8f
Signed-off-by: default avatarElson Roy Serrao <eserrao@codeaurora.org>
parent 629f8312
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -3931,7 +3931,7 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
	if (get_chg_type(mdwc) == POWER_SUPPLY_TYPE_USB_CDP &&
			mdwc->vbus_active && !mdwc->check_eud_state) {
		dev_dbg(mdwc->dev, "Connected to CDP, pull DP up\n");
		usb_phy_drive_dp_pulse(mdwc->hs_phy, DP_PULSE_WIDTH_MSEC);
		mdwc->hs_phy->charger_detect(mdwc->hs_phy);
	}

	mdwc->ext_idx = enb->idx;
+8 −5
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.
 * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
 */

#include <linux/module.h>
@@ -834,7 +834,8 @@ static int qusb_phy_notify_disconnect(struct usb_phy *phy,
	return 0;
}

void usb_phy_drive_dp_pulse(void *phy, unsigned int interval_ms)
#define DP_PULSE_WIDTH_MSEC 200
static enum usb_charger_type usb_phy_drive_dp_pulse(struct usb_phy *phy)
{
	struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
	int ret;
@@ -843,7 +844,7 @@ void usb_phy_drive_dp_pulse(void *phy, unsigned int interval_ms)
	if (ret < 0) {
		dev_dbg(qphy->phy.dev,
			"dpdm regulator enable failed:%d\n", ret);
		return;
		return 0;
	}
	qusb_phy_enable_clocks(qphy, true);
	msm_usb_write_readback(qphy->base, qphy->phy_reg[PWR_CTRL1],
@@ -862,7 +863,7 @@ void usb_phy_drive_dp_pulse(void *phy, unsigned int interval_ms)
	msm_usb_write_readback(qphy->base, qphy->phy_reg[INTR_CTRL],
				DPSE_INTR_EN, DPSE_INTR_EN);

	msleep(interval_ms);
	msleep(DP_PULSE_WIDTH_MSEC);

	msm_usb_write_readback(qphy->base, qphy->phy_reg[INTR_CTRL],
				DPSE_INTR_HIGH_SEL |
@@ -884,8 +885,9 @@ void usb_phy_drive_dp_pulse(void *phy, unsigned int interval_ms)
		dev_dbg(qphy->phy.dev,
			"dpdm regulator disable failed:%d\n", ret);
	}

	return 0;
}
EXPORT_SYMBOL(usb_phy_drive_dp_pulse);

static int qusb_phy_dpdm_regulator_enable(struct regulator_dev *rdev)
{
@@ -1316,6 +1318,7 @@ static int qusb_phy_probe(struct platform_device *pdev)
	qphy->phy.type			= USB_PHY_TYPE_USB2;
	qphy->phy.notify_connect        = qusb_phy_notify_connect;
	qphy->phy.notify_disconnect     = qusb_phy_notify_disconnect;
	qphy->phy.charger_detect	= usb_phy_drive_dp_pulse;

	ret = usb_add_phy_dev(&qphy->phy);
	if (ret)
+63 −0
Original line number Diff line number Diff line
@@ -28,6 +28,11 @@
#define OPMODE_MASK				(0x3 << 3)
#define OPMODE_NONDRIVING			(0x1 << 3)
#define SLEEPM					BIT(0)
#define OPMODE_NORMAL				(0x00)
#define TERMSEL					BIT(5)

#define USB2_PHY_USB_PHY_UTMI_CTRL1		(0x40)
#define XCVRSEL					BIT(0)

#define USB2_PHY_USB_PHY_UTMI_CTRL5		(0x50)
#define POR					BIT(1)
@@ -557,6 +562,63 @@ static int msm_hsphy_notify_disconnect(struct usb_phy *uphy,
	return 0;
}

#define DP_PULSE_WIDTH_MSEC 200
static enum usb_charger_type usb_phy_drive_dp_pulse(struct usb_phy *uphy)
{
	struct msm_hsphy *phy = container_of(uphy, struct msm_hsphy, phy);
	int ret;

	ret = msm_hsphy_enable_power(phy, true);
	if (ret < 0) {
		dev_dbg(phy->phy.dev,
			"dpdm regulator enable failed:%d\n", ret);
		return 0;
	}
	msm_hsphy_enable_clocks(phy, true);
	/* set utmi_phy_cmn_cntrl_override_en &
	 * utmi_phy_datapath_ctrl_override_en
	 */
	msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_CFG0,
				UTMI_PHY_CMN_CTRL_OVERRIDE_EN,
				UTMI_PHY_CMN_CTRL_OVERRIDE_EN);
	msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_CFG0,
				UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN,
				UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN);
	/* set opmode to normal i.e. 0x0 & termsel to fs */
	msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_UTMI_CTRL0,
				OPMODE_MASK, OPMODE_NORMAL);
	msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_UTMI_CTRL0,
				TERMSEL, TERMSEL);
	/* set xcvrsel to fs */
	msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_UTMI_CTRL1,
					XCVRSEL, XCVRSEL);
	msleep(DP_PULSE_WIDTH_MSEC);
	/* clear termsel to fs */
	msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_UTMI_CTRL0,
				TERMSEL, 0x00);
	/* clear xcvrsel */
	msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_UTMI_CTRL1,
					XCVRSEL, 0x00);
	/* clear utmi_phy_cmn_cntrl_override_en &
	 * utmi_phy_datapath_ctrl_override_en
	 */
	msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_CFG0,
				UTMI_PHY_CMN_CTRL_OVERRIDE_EN, 0x00);
	msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_CFG0,
				UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN, 0x00);

	msleep(20);

	msm_hsphy_enable_clocks(phy, false);
	ret = msm_hsphy_enable_power(phy, false);
	if (ret < 0) {
		dev_dbg(phy->phy.dev,
			"dpdm regulator disable failed:%d\n", ret);
	}

	return 0;
}

static int msm_hsphy_dpdm_regulator_enable(struct regulator_dev *rdev)
{
	int ret = 0;
@@ -827,6 +889,7 @@ static int msm_hsphy_probe(struct platform_device *pdev)
	phy->phy.notify_connect		= msm_hsphy_notify_connect;
	phy->phy.notify_disconnect	= msm_hsphy_notify_disconnect;
	phy->phy.type			= USB_PHY_TYPE_USB2;
	phy->phy.charger_detect		= usb_phy_drive_dp_pulse;

	ret = usb_add_phy_dev(&phy->phy);
	if (ret)
+1 −9
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
 * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
 */

#ifndef __LINUX_USB_DWC3_MSM_H
@@ -115,14 +115,6 @@ struct gsi_channel_info {
	struct usb_gsi_request *ch_req;
};

#if IS_ENABLED(CONFIG_MSM_QUSB_PHY)
extern void usb_phy_drive_dp_pulse(void *phy,
					unsigned int interval_ms);
#else
static inline void usb_phy_drive_dp_pulse(void *phy, unsigned int interval_ms)
{ }
#endif

#if IS_ENABLED(CONFIG_USB_DWC3_MSM)
struct usb_ep *usb_ep_autoconfig_by_name(struct usb_gadget *gadget,
		struct usb_endpoint_descriptor *desc, const char *ep_name);