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

Commit fd985903 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: phy-msm-qusb: Add support for floating data lines detection"

parents f3253fda 7e6b3b7f
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -42,6 +42,9 @@ Optional properties :
- qcom,disable-host-mode-pm: If present, it disables XHCI PM runtime functionality when USB
  host mode is used.
- qcom,core-clk-rate: If present, indicates clock frequency to be set for USB master clock.
- qcom,detect-dpdm-floating: Indicates if SDP must be rechecked for floating data lines.
  If datalines are floating then don't treat that as SDP.

Sub nodes:
- Sub node for "DWC3- USB3 controller".
  This sub node is required property for device node. The properties of this subnode
+43 −8
Original line number Diff line number Diff line
@@ -157,8 +157,6 @@ enum dwc3_id_state {
 * DWC3_PROPRIETARY_CHARGER	A non-standard charger that pulls DP/DM to
 *				specific voltages between 2.0-3.3v for
 *				identification.
 * DWC3_FLOATED_CHARGER		Non standard charger whose data lines are
 *				floating.
 */
enum dwc3_chg_type {
	DWC3_INVALID_CHARGER = 0,
@@ -166,7 +164,6 @@ enum dwc3_chg_type {
	DWC3_DCP_CHARGER,
	DWC3_CDP_CHARGER,
	DWC3_PROPRIETARY_CHARGER,
	DWC3_FLOATED_CHARGER,
};

struct dwc3_msm {
@@ -208,10 +205,10 @@ struct dwc3_msm {
	enum dwc3_chg_type	chg_type;
	unsigned		max_power;
	bool			charging_disabled;
	bool			detect_dpdm_floating;
	enum usb_otg_state	otg_state;
	enum usb_chg_state	chg_state;
	int			pmic_id_irq;
	u8			dcd_retries;
	struct work_struct	bus_vote_w;
	unsigned int		bus_vote;
	u32			bus_perf_client;
@@ -1817,7 +1814,6 @@ static const char *chg_to_string(enum dwc3_chg_type chg_type)
	case DWC3_DCP_CHARGER:		return "USB_DCP_CHARGER";
	case DWC3_CDP_CHARGER:		return "USB_CDP_CHARGER";
	case DWC3_PROPRIETARY_CHARGER:	return "USB_PROPRIETARY_CHARGER";
	case DWC3_FLOATED_CHARGER:	return "USB_FLOATED_CHARGER";
	default:			return "UNKNOWN_CHARGER";
	}
}
@@ -1853,7 +1849,8 @@ static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc)
	unsigned long timeout;
	u32 reg = 0;

	if ((mdwc->in_host_mode || mdwc->vbus_active)
	if ((mdwc->in_host_mode || (mdwc->vbus_active
			&& mdwc->otg_state == OTG_STATE_B_SUSPEND))
			&& dwc3_msm_is_superspeed(mdwc)) {
		if (!atomic_read(&mdwc->in_p3)) {
			dev_err(mdwc->dev, "Not in P3,aborting LPM sequence\n");
@@ -2003,7 +2000,9 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
	clk_disable_unprepare(mdwc->xo_clk);

	/* Perform controller power collapse */
	if (!mdwc->in_host_mode && (!mdwc->vbus_active || mdwc->in_restart)) {
	if (!mdwc->in_host_mode && (!mdwc->vbus_active ||
				    mdwc->otg_state == OTG_STATE_B_IDLE ||
				    mdwc->in_restart)) {
		mdwc->lpm_flags |= MDWC3_POWER_COLLAPSE;
		dev_dbg(mdwc->dev, "%s: power collapse\n", __func__);
		dwc3_msm_config_gdsc(mdwc, 0);
@@ -2036,7 +2035,8 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
	 * using HS_PHY_IRQ or SS_PHY_IRQ. Hence enable wakeup only in
	 * case of host bus suspend and device bus suspend.
	 */
	if (mdwc->vbus_active || mdwc->in_host_mode) {
	if ((mdwc->vbus_active && mdwc->otg_state == OTG_STATE_B_SUSPEND)
				|| mdwc->in_host_mode) {
		enable_irq_wake(mdwc->hs_phy_irq);
		enable_irq(mdwc->hs_phy_irq);
		if (mdwc->ss_phy_irq) {
@@ -2918,6 +2918,9 @@ static int dwc3_msm_probe(struct platform_device *pdev)
	mdwc->disable_host_mode_pm = of_property_read_bool(node,
				"qcom,disable-host-mode-pm");

	mdwc->detect_dpdm_floating = of_property_read_bool(node,
				"qcom,detect-dpdm-floating");

	dwc3_set_notifier(&dwc3_msm_notify_event);

	/* Assumes dwc3 is the only DT child of dwc3-msm */
@@ -3373,6 +3376,26 @@ psy_error:
	return -ENXIO;
}

static void dwc3_check_float_lines(struct dwc3_msm *mdwc)
{
	int dpdm;

	dev_dbg(mdwc->dev, "%s: Check linestate\n", __func__);
	dwc3_msm_gadget_vbus_draw(mdwc, 0);

	/* Get linestate with Idp_src enabled */
	dpdm = usb_phy_dpdm_with_idp_src(mdwc->hs_phy);
	if (dpdm == 0x2) {
		/* DP is HIGH = lines are floating */
		mdwc->chg_type = DWC3_PROPRIETARY_CHARGER;
		mdwc->otg_state = OTG_STATE_B_IDLE;
		pm_runtime_put_sync(mdwc->dev);
		dbg_event(0xFF, "FLT psync",
				atomic_read(&mdwc->dev->power.usage_count));
	} else if (dpdm) {
		dev_dbg(mdwc->dev, "%s:invalid linestate:%x\n", __func__, dpdm);
	}
}

static void dwc3_initialize(struct dwc3_msm *mdwc)
{
@@ -3492,6 +3515,12 @@ static void dwc3_otg_sm_work(struct work_struct *w)
				pm_runtime_enable(mdwc->dev);
				pm_runtime_get_noresume(mdwc->dev);
				dwc3_initialize(mdwc);
				/* check dp/dm for SDP & runtime_put if !SDP */
				if (mdwc->detect_dpdm_floating) {
					dwc3_check_float_lines(mdwc);
					if (mdwc->chg_type != DWC3_SDP_CHARGER)
						break;
				}
				dwc3_otg_start_peripheral(mdwc, 1);
				mdwc->otg_state = OTG_STATE_B_PERIPHERAL;
				dbg_event(0xFF, "Undef SDP",
@@ -3548,6 +3577,12 @@ static void dwc3_otg_sm_work(struct work_struct *w)
				dbg_event(0xFF, "CHG gsync",
					atomic_read(
						&mdwc->dev->power.usage_count));
				/* check dp/dm for SDP & runtime_put if !SDP */
				if (mdwc->detect_dpdm_floating) {
					dwc3_check_float_lines(mdwc);
					if (mdwc->chg_type != DWC3_SDP_CHARGER)
						break;
				}
				dwc3_otg_start_peripheral(mdwc, 1);
				mdwc->otg_state = OTG_STATE_B_PERIPHERAL;
				work = 1;
+96 −0
Original line number Diff line number Diff line
@@ -28,6 +28,23 @@
/* TCSR_PHY_CLK_SCHEME_SEL bit mask */
#define PHY_CLK_SCHEME_SEL BIT(0)

#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_PORT_QUICKCHARGE1	0x70
#define IDP_SRC_EN			BIT(3)

#define QUSB2PHY_PORT_QUICKCHARGE2	0x74
#define QUSB2PHY_PORT_INT_STATUS	0xF0

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

@@ -47,6 +64,7 @@
#define POWER_DOWN			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)
@@ -730,6 +748,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_QUICKCHARGE1);
	writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QUICKCHARGE2);

	/* 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_QUICKCHARGE1);

	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.
 *
@@ -1121,6 +1216,7 @@ static int qusb_phy_probe(struct platform_device *pdev)
	qphy->phy.shutdown		= qusb_phy_shutdown;
	qphy->phy.change_dpdm		= qusb_phy_update_dpdm;
	qphy->phy.type			= USB_PHY_TYPE_USB2;
	qphy->phy.dpdm_with_idp_src	= qusb_phy_linestate_with_idp_src;

	if (qphy->qscratch_base) {
		qphy->phy.notify_connect        = qusb_phy_notify_connect;
+10 −0
Original line number Diff line number Diff line
@@ -134,6 +134,8 @@ struct usb_phy {
			char *event, int msg1, int msg2);
	/* update DP/DM state */
	int	(*change_dpdm)(struct usb_phy *x, int dpdm);
	/* return linestate with Idp_src (used for DCD with USB2 PHY) */
	int	(*dpdm_with_idp_src)(struct usb_phy *x);
};

/**
@@ -334,6 +336,14 @@ usb_phy_dbg_events(struct usb_phy *x,
		x->dbg_event(x, event, msg1, msg2);
}

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;
}

/* notifiers */
static inline int
usb_register_notifier(struct usb_phy *x, struct notifier_block *nb)