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

Commit 669f5036 authored by Sriharsha Allenki's avatar Sriharsha Allenki
Browse files

usb: dwc3: Support float charger detection



Chargers on some platforms do not support FLOAT charger
detection. On these platforms, the float charger can be
detected depending on the state of the DP and DM lines as
detected by the PHY.
So, on these platforms check for the DP DM line state
and if found floating, do not start the peripheral mode
and notify PMIC -ETIMEDOUT implying the connected
charger is of type float.

Change-Id: I6bf54b0d5c143a849ce9ea7bc515d62204ed2f33
Signed-off-by: default avatarSriharsha Allenki <sallenki@codeaurora.org>
parent 705aac61
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -66,6 +66,8 @@ Optional properties :
	which is used as a vote by driver to get max performance in perf mode.
- qcom,no-wakeup-src-in-hostmode: If present then driver doesn't use wakeup_source APIs
  in host mode. This allows PM suspend to happen irrespective of runtimePM state of host.
- qcom,check-for-float: If present, the driver will always check for possible
  float connection irrespective of the charger type.

Sub nodes:
- Sub node for "DWC3- USB3 controller".
+27 −2
Original line number Diff line number Diff line
/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -66,6 +66,9 @@
/* AHB2PHY read/write waite value */
#define ONE_READ_WRITE_WAIT 0x11

/* DP_DM linestate float */
#define DP_DM_STATE_FLOAT 0x02

/* cpu to fix usb interrupt */
static int cpu_to_affin;
module_param(cpu_to_affin, int, S_IRUGO|S_IWUSR);
@@ -220,6 +223,8 @@ struct dwc3_msm {
	bool			hc_died;
	bool			xhci_ss_compliance_enable;
	bool			no_wakeup_src_in_hostmode;
	bool			check_for_float;
	bool			float_detected;

	struct extcon_dev	*extcon_vbus;
	struct extcon_dev	*extcon_id;
@@ -2724,6 +2729,7 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
	if (mdwc->vbus_active == event)
		return NOTIFY_DONE;

	mdwc->float_detected = false;
	cc_state = extcon_get_cable_state_(edev, EXTCON_USB_CC);
	if (cc_state < 0)
		mdwc->typec_orientation = ORIENTATION_NONE;
@@ -3272,6 +3278,8 @@ static int dwc3_msm_probe(struct platform_device *pdev)
	if (of_property_read_bool(node, "qcom,disable-dev-mode-pm"))
		pm_runtime_get_noresume(mdwc->dev);

	mdwc->check_for_float = of_property_read_bool(node,
					"qcom,check-for-float");
	ret = dwc3_msm_extcon_register(mdwc);
	if (ret)
		goto put_dwc3;
@@ -3820,7 +3828,8 @@ static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA)
	int ret, psy_type;

	psy_type = get_psy_type(mdwc);
	if (psy_type == POWER_SUPPLY_TYPE_USB_FLOAT) {
	if (psy_type == POWER_SUPPLY_TYPE_USB_FLOAT
		|| (mdwc->check_for_float && mdwc->float_detected)) {
		if (!mA)
			pval.intval = -ETIMEDOUT;
		else
@@ -3906,6 +3915,7 @@ static void dwc3_otg_sm_work(struct work_struct *w)
			work = 1;
		} else if (test_bit(B_SESS_VLD, &mdwc->inputs)) {
			dev_dbg(mdwc->dev, "b_sess_vld\n");
			mdwc->float_detected = false;
			if (get_psy_type(mdwc) == POWER_SUPPLY_TYPE_USB_FLOAT)
				queue_delayed_work(mdwc->dwc3_wq,
						&mdwc->sdp_check,
@@ -3918,6 +3928,21 @@ static void dwc3_otg_sm_work(struct work_struct *w)
			pm_runtime_get_sync(mdwc->dev);
			dbg_event(0xFF, "BIDLE gsync",
				atomic_read(&mdwc->dev->power.usage_count));
			if (mdwc->check_for_float) {
				/*
				 * If DP_DM are found to be floating, do not
				 * start the peripheral mode.
				 */
				if (usb_phy_dpdm_with_idp_src(mdwc->hs_phy) ==
							DP_DM_STATE_FLOAT) {
					mdwc->float_detected = true;
					dwc3_msm_gadget_vbus_draw(mdwc, 0);
					pm_runtime_put_sync(mdwc->dev);
					dbg_event(0xFF, "FLT sync", atomic_read(
						&mdwc->dev->power.usage_count));
					break;
				}
			}
			dwc3_otg_start_peripheral(mdwc, 1);
			mdwc->otg_state = OTG_STATE_B_PERIPHERAL;
			work = 1;
+93 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
 * Copyright (c) 2014-2017,2019 The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -29,11 +29,23 @@
#include <linux/usb/msm_hsusb.h>
#include <linux/reset.h>

#define QUSB2PHY_PLL_PWR_CTL		0x18
#define REF_BUF_EN			BIT(0)
#define REXT_EN				BIT(1)
#define PLL_BYPASSNL			BIT(2)
#define REXT_TRIM_0			BIT(4)

#define QUSB2PHY_PLL_AUTOPGM_CTL1	0x1C
#define PLL_RESET_N_CNT_5		0x5
#define PLL_RESET_N			BIT(4)
#define PLL_AUTOPGM_EN			BIT(7)

#define QUSB2PHY_PLL_STATUS	0x38
#define QUSB2PHY_PLL_LOCK	BIT(5)

#define QUSB2PHY_PORT_QC1	0x70
#define VDM_SRC_EN		BIT(4)
#define IDP_SRC_EN		BIT(3)
#define VDP_SRC_EN		BIT(2)

#define QUSB2PHY_PORT_QC2	0x74
@@ -57,6 +69,7 @@
#define CORE_READY_STATUS		BIT(0)

#define QUSB2PHY_PORT_UTMI_CTRL1	0xC0
#define SUSPEND_N			BIT(5)
#define TERM_SELECT			BIT(4)
#define XCVR_SELECT_FS			BIT(2)
#define OP_MODE_NON_DRIVE		BIT(0)
@@ -84,6 +97,7 @@
#define DPSE_INTR_HIGH_SEL              BIT(1)
#define DPSE_INTR_EN                    BIT(0)

#define QUSB2PHY_PORT_INT_STATUS	0xF0
#define QUSB2PHY_PORT_UTMI_STATUS	0xF4
#define LINESTATE_DP			BIT(0)
#define LINESTATE_DM			BIT(1)
@@ -692,6 +706,83 @@ static void qusb_phy_shutdown(struct usb_phy *phy)

	qusb_phy_enable_clocks(qphy, false);
}

/**
 * Returns DP/DM linestate with Idp_src enabled to detect if lines are floating
 *
 * @uphy - usb phy pointer.
 *
 */
static int qusb_phy_linestate_with_idp_src(struct usb_phy *phy)
{
	struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
	u8 int_status, ret;

	/* Disable/powerdown the PHY */
	writel_relaxed(CLAMP_N_EN | FREEZIO_N | POWER_DOWN,
			qphy->base + QUSB2PHY_PORT_POWERDOWN);

	/* Put PHY in non-driving mode */
	writel_relaxed(TERM_SELECT | XCVR_SELECT_FS | OP_MODE_NON_DRIVE |
			SUSPEND_N, qphy->base + QUSB2PHY_PORT_UTMI_CTRL1);

	/* Switch PHY to utmi register mode */
	writel_relaxed(UTMI_ULPI_SEL | UTMI_TEST_MUX_SEL,
			qphy->base + QUSB2PHY_PORT_UTMI_CTRL2);

	writel_relaxed(PLL_RESET_N_CNT_5,
			qphy->base + QUSB2PHY_PLL_AUTOPGM_CTL1);

	/* Enable PHY */
	writel_relaxed(CLAMP_N_EN | FREEZIO_N,
			qphy->base + QUSB2PHY_PORT_POWERDOWN);

	writel_relaxed(REF_BUF_EN | REXT_EN | PLL_BYPASSNL | REXT_TRIM_0,
			qphy->base + QUSB2PHY_PLL_PWR_CTL);

	usleep_range(5, 1000);

	writel_relaxed(PLL_RESET_N | PLL_RESET_N_CNT_5,
			qphy->base + QUSB2PHY_PLL_AUTOPGM_CTL1);
	usleep_range(50, 1000);

	writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC1);
	writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC2);

	/* Enable all chg_det events from PHY */
	writel_relaxed(0x1F, qphy->base + QUSB2PHY_PORT_INTR_CTRL);
	/* Enable Idp_src */
	writel_relaxed(IDP_SRC_EN, qphy->base + QUSB2PHY_PORT_QC1);

	usleep_range(1000, 2000);
	int_status = readl_relaxed(qphy->base + QUSB2PHY_PORT_INT_STATUS);

	/* Exit chg_det mode, set PHY regs to default values */
	writel_relaxed(CLAMP_N_EN | FREEZIO_N | POWER_DOWN,
			qphy->base + QUSB2PHY_PORT_POWERDOWN);  /* 23 */

	writel_relaxed(PLL_AUTOPGM_EN | PLL_RESET_N | PLL_RESET_N_CNT_5,
			qphy->base + QUSB2PHY_PLL_AUTOPGM_CTL1);

	writel_relaxed(UTMI_ULPI_SEL, qphy->base + QUSB2PHY_PORT_UTMI_CTRL2);

	writel_relaxed(TERM_SELECT, qphy->base + QUSB2PHY_PORT_UTMI_CTRL1);

	writel_relaxed(CLAMP_N_EN | FREEZIO_N,
			qphy->base + QUSB2PHY_PORT_POWERDOWN);

	int_status = int_status & 0x5;

	/*
	 * int_status's Bit(0) is DP and Bit(2) is DM.
	 * Caller expects bit(1) as DP and bit(0) DM i.e. usual linestate format
	 */
	ret = (int_status >> 2) | ((int_status & 0x1) << 1);
	pr_debug("%s: int_status:%x, dpdm:%x\n", __func__, int_status, ret);

	return ret;
}

/**
 * Performs QUSB2 PHY suspend/resume functionality.
 *
@@ -1150,6 +1241,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.dpdm_with_idp_src	= qusb_phy_linestate_with_idp_src;

	/*
	 * On some platforms multiple QUSB PHYs are available. If QUSB PHY is
+12 −0
Original line number Diff line number Diff line
@@ -126,6 +126,9 @@ struct usb_phy {

	/* reset the PHY clocks */
	int	(*reset)(struct usb_phy *x);

	/* return linestate with Idp_src (used for DCD with USB2 PHY) */
	int (*dpdm_with_idp_src)(struct usb_phy *x);
};

/**
@@ -209,6 +212,15 @@ usb_phy_reset(struct usb_phy *x)
	return 0;
}

static inline int
usb_phy_dpdm_with_idp_src(struct usb_phy *x)
{
	if (x && x->dpdm_with_idp_src)
		return x->dpdm_with_idp_src(x);

	return 0;
}

/* for usb host and peripheral controller drivers */
#if IS_ENABLED(CONFIG_USB_PHY)
extern struct usb_phy *usb_get_phy(enum usb_phy_type type);