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

Commit 119baad7 authored by Sriharsha Allenki's avatar Sriharsha Allenki
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.

The ser current call also leads to the same failure, hence
limit the set current call only to the case where the device
is connected to a Standard Downstream Port.

Change-Id: I2c737c68de57bc4126fb3e6e1c291b83d681fb13
Signed-off-by: default avatarSriharsha Allenki <sallenki@codeaurora.org>
parent ec57ea06
Loading
Loading
Loading
Loading
+46 −3
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/dmapool.h>
#include <linux/pm_runtime.h>
#include <linux/ratelimit.h>
#include <linux/iio/consumer.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
#include <linux/ioport.h>
@@ -33,6 +34,7 @@
#include <linux/pm_wakeup.h>
#include <linux/pm_qos.h>
#include <linux/power_supply.h>
#include <linux/qti_power_supply.h>
#include <linux/cdev.h>
#include <linux/completion.h>
#include <linux/interconnect.h>
@@ -441,6 +443,7 @@ struct dwc3_msm {
	enum bus_vote		override_bus_vote;
	struct icc_path		*icc_paths[3];
	struct power_supply	*usb_psy;
	struct iio_channel	*chg_type;
	struct work_struct	vbus_draw_work;
	bool			in_host_mode;
	bool			in_device_mode;
@@ -3416,6 +3419,7 @@ static irqreturn_t msm_dwc3_pwr_irq(int irq, void *data)
}

static void dwc3_otg_sm_work(struct work_struct *w);
static int get_chg_type(struct dwc3_msm *mdwc);

static int dwc3_msm_get_clk_gdsc(struct dwc3_msm *mdwc)
{
@@ -3552,6 +3556,7 @@ static int dwc3_msm_id_notifier(struct notifier_block *nb,
	return NOTIFY_DONE;
}

#define DP_PULSE_WIDTH_MSEC 200

static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
	unsigned long event, void *ptr)
@@ -3586,6 +3591,16 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
		mdwc->vbus_active = event;
	}

	/*
	 * Drive a pulse on DP to ensure proper CDP detection
	 * and only when the vbus connect event is a valid one.
	 */
	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->ext_idx = enb->idx;
	if (dwc->dr_mode == USB_DR_MODE_OTG && !mdwc->in_restart)
		queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
@@ -4859,10 +4874,32 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on)
	return 0;
}

static int get_chg_type(struct dwc3_msm *mdwc)
{
	int ret, value;

	if (!mdwc->chg_type) {
		mdwc->chg_type = devm_iio_channel_get(mdwc->dev, "chg_type");
		if (IS_ERR_OR_NULL(mdwc->chg_type)) {
			dev_dbg(mdwc->dev, "unable to get iio channel\n");
			mdwc->chg_type = NULL;
			return -ENODEV;
		}
	}

	ret = iio_read_channel_processed(mdwc->chg_type, &value);
	if (ret < 0) {
		dev_err(mdwc->dev, "failed to get charger type\n");
		return ret;
	}

	return value;
}

static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned int mA)
{
	union power_supply_propval pval = {0};
	int ret;
	int ret, chg_type;

	if (!mdwc->usb_psy && of_property_read_bool(mdwc->dev->of_node,
					"qcom,usb-charger")) {
@@ -4876,13 +4913,20 @@ static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned int mA)
	if (!mdwc->usb_psy)
		return 0;

	if (mdwc->max_power == mA)
	/*
	 * Set the valid current only when the device
	 * is connected to a Standard Downstream Port.
	 */
	chg_type = get_chg_type(mdwc);
	if (mdwc->max_power == mA || (chg_type != -ENODEV
				&& chg_type != POWER_SUPPLY_TYPE_USB))
		return 0;

	dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA);

	/* Set max current limit in uA */
	pval.intval = 1000 * mA;

	ret = power_supply_set_property(mdwc->usb_psy,
				POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &pval);
	if (ret) {
@@ -4894,7 +4938,6 @@ static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned int mA)
	return 0;
}


/**
 * dwc3_otg_sm_work - workqueue function.
 *
+72 −0
Original line number Diff line number Diff line
@@ -461,6 +461,25 @@ static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt,
	}
}

static void msm_usb_write_readback(void __iomem *base, u32 offset,
					const u32 mask, u32 val)
{
	u32 write_val, tmp = readl_relaxed(base + offset);

	tmp &= ~mask;		/* retain other bits */
	write_val = tmp | val;

	writel_relaxed(write_val, base + offset);

	/* Read back to see if val was written */
	tmp = readl_relaxed(base + offset);
	tmp &= mask;		/* clear other bits */

	if (tmp != val)
		pr_err("%s: write: %x to QSCRATCH: %x FAILED\n",
			__func__, val, offset);
}

static void qusb_phy_reset(struct qusb_phy *qphy)
{
	int ret;
@@ -815,6 +834,59 @@ static int qusb_phy_notify_disconnect(struct usb_phy *phy,
	return 0;
}

void usb_phy_drive_dp_pulse(void *phy, unsigned int interval_ms)
{
	struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
	int ret;

	ret = qusb_phy_enable_power(qphy);
	if (ret < 0) {
		dev_dbg(qphy->phy.dev,
			"dpdm regulator enable failed:%d\n", ret);
		return;
	}
	qusb_phy_enable_clocks(qphy, true);
	msm_usb_write_readback(qphy->base, qphy->phy_reg[PWR_CTRL1],
				PWR_CTRL1_POWR_DOWN, 0x00);
	msm_usb_write_readback(qphy->base, qphy->phy_reg[DEBUG_CTRL4],
				FORCED_UTMI_DPPULLDOWN, 0x00);
	msm_usb_write_readback(qphy->base, qphy->phy_reg[DEBUG_CTRL4],
				FORCED_UTMI_DMPULLDOWN,
				FORCED_UTMI_DMPULLDOWN);
	msm_usb_write_readback(qphy->base, qphy->phy_reg[DEBUG_CTRL3],
				0xd1, 0xd1);
	msm_usb_write_readback(qphy->base, qphy->phy_reg[PWR_CTRL1],
				CLAMP_N_EN, CLAMP_N_EN);
	msm_usb_write_readback(qphy->base, qphy->phy_reg[INTR_CTRL],
				DPSE_INTR_HIGH_SEL, 0x00);
	msm_usb_write_readback(qphy->base, qphy->phy_reg[INTR_CTRL],
				DPSE_INTR_EN, DPSE_INTR_EN);

	msleep(interval_ms);

	msm_usb_write_readback(qphy->base, qphy->phy_reg[INTR_CTRL],
				DPSE_INTR_HIGH_SEL |
				DPSE_INTR_EN, 0x00);
	msm_usb_write_readback(qphy->base, qphy->phy_reg[DEBUG_CTRL3],
				0xd1, 0x00);
	msm_usb_write_readback(qphy->base, qphy->phy_reg[DEBUG_CTRL4],
				FORCED_UTMI_DPPULLDOWN |
				FORCED_UTMI_DMPULLDOWN, 0x00);
	msm_usb_write_readback(qphy->base, qphy->phy_reg[PWR_CTRL1],
				PWR_CTRL1_POWR_DOWN |
				CLAMP_N_EN, 0x00);

	msleep(20);

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

static int qusb_phy_dpdm_regulator_enable(struct regulator_dev *rdev)
{
	int ret = 0;
+8 −0
Original line number Diff line number Diff line
@@ -111,6 +111,14 @@ 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);