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

Commit c51c319b authored by Jack Pham's avatar Jack Pham
Browse files

usb: dwc3: gadget: Allow booting in host mode



Typically gadget drivers will call usb_composite_probe() during
bootup which initializes the UDC. However, for DRD/OTG devices,
if the controller is already activated in host mode,
dwc3_gadget_start() will fail trying to enable ep0, causing the
gadget driver to fail to bind during its probe.

To fix this, skip the device-specific initialization in
dwc_gadget_start() if the driver supports OTG. Instead,
dwc3_gadget_restart() will handle it when device mode is activated
via dwc3_gadget_run_stop(). Finally, factor out the common init
sequence into a new __dwc3_gadget_start() helper.

Change-Id: Ie83da8e4736c367fe72d41928a460b00820701aa
Signed-off-by: default avatarJack Pham <jackp@codeaurora.org>
parent af83c6c7
Loading
Loading
Loading
Loading
+31 −99
Original line number Diff line number Diff line
@@ -1780,42 +1780,12 @@ static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active)
	return 0;
}

/* Required gadget re-initialization before switching to gadget in OTG mode */
void dwc3_gadget_restart(struct dwc3 *dwc)
static int __dwc3_gadget_start(struct dwc3 *dwc)
{
	struct dwc3_ep		*dep;
	int			ret = 0;
	u32			reg;

	/* Enable all but Start and End of Frame IRQs */
	reg = (DWC3_DEVTEN_EVNTOVERFLOWEN |
			DWC3_DEVTEN_CMDCMPLTEN |
			DWC3_DEVTEN_ERRTICERREN |
			DWC3_DEVTEN_WKUPEVTEN |
			DWC3_DEVTEN_ULSTCNGEN |
			DWC3_DEVTEN_CONNECTDONEEN |
			DWC3_DEVTEN_USBRSTEN |
			DWC3_DEVTEN_DISCONNEVTEN);
	dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);

	/* Enable USB2 LPM and automatic phy suspend only on recent versions */
	if (dwc->revision >= DWC3_REVISION_194A) {
		reg = dwc3_readl(dwc->regs, DWC3_DCFG);
		reg |= DWC3_DCFG_LPM_CAP;
		dwc3_writel(dwc->regs, DWC3_DCFG, reg);

		reg = dwc3_readl(dwc->regs, DWC3_DCTL);
		reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);

		/* TODO: This should be configurable */
		reg |= DWC3_DCTL_HIRD_THRES(28);

		dwc3_writel(dwc->regs, DWC3_DCTL, reg);

		dwc3_gadget_usb2_phy_suspend(dwc, true);
		dwc3_gadget_usb3_phy_suspend(dwc, true);
	}

	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
	reg &= ~(DWC3_DCFG_SPEED_MASK);

@@ -1862,34 +1832,51 @@ void dwc3_gadget_restart(struct dwc3 *dwc)
	/* reinitialize physical ep0-1 */
	dep = dwc->eps[0];
	dep->flags = 0;
	dep->endpoint.maxburst = 1;
	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
	if (ret) {
		dev_err(dwc->dev, "failed to enable %s\n", dep->name);
		return;
		return ret;
	}

	dep = dwc->eps[1];
	dep->flags = 0;
	dep->endpoint.maxburst = 1;
	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
	if (ret) {
		dev_err(dwc->dev, "failed to enable %s\n", dep->name);
		return;
		__dwc3_gadget_ep_disable(dwc->eps[0]);
		return ret;
	}

	/* begin to receive SETUP packets */
	dwc->ep0state = EP0_SETUP_PHASE;
	dwc3_ep0_out_start(dwc);

	dwc3_gadget_enable_irq(dwc);

	return ret;
}

/* Required gadget re-initialization before switching to gadget in OTG mode */
void dwc3_gadget_restart(struct dwc3 *dwc)
{
	/* automatic phy suspend only on recent versions */
	if (dwc->revision >= DWC3_REVISION_194A) {
		dwc3_gadget_usb2_phy_suspend(dwc, true);
		dwc3_gadget_usb3_phy_suspend(dwc, true);
	}

	__dwc3_gadget_start(dwc);
}

static int dwc3_gadget_start(struct usb_gadget *g,
		struct usb_gadget_driver *driver)
{
	struct dwc3		*dwc = gadget_to_dwc(g);
	struct dwc3_ep		*dep;
	unsigned long		flags;
	int			ret = 0;
	int			irq;
	u32			reg;

	pm_runtime_get_sync(dwc->dev);
	irq = platform_get_irq(to_platform_device(dwc->dev), 0);
@@ -1913,78 +1900,23 @@ static int dwc3_gadget_start(struct usb_gadget *g,

	dwc->gadget_driver	= driver;

	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
	reg &= ~(DWC3_DCFG_SPEED_MASK);

	/**
	 * WORKAROUND: DWC3 revision < 2.20a have an issue
	 * which would cause metastability state on Run/Stop
	 * bit if we try to force the IP to USB2-only mode.
	 *
	 * Because of that, we cannot configure the IP to any
	 * speed other than the SuperSpeed
	 *
	 * Refers to:
	 *
	 * STAR#9000525659: Clock Domain Crossing on DCTL in
	 * USB 2.0 Mode
	/*
	 * For DRD, this might get called by gadget driver during bootup
	 * even though host mode might be active. Don't actually perform
	 * device-specific initialization until device mode is activated.
	 * In that case dwc3_gadget_restart() will handle it.
	 */
	if (dwc->revision < DWC3_REVISION_220A) {
		reg |= DWC3_DCFG_SUPERSPEED;
	} else {
		switch (dwc->maximum_speed) {
		case USB_SPEED_LOW:
			reg |= DWC3_DSTS_LOWSPEED;
			break;
		case USB_SPEED_FULL:
			reg |= DWC3_DSTS_FULLSPEED2;
			break;
		case USB_SPEED_HIGH:
			reg |= DWC3_DSTS_HIGHSPEED;
			break;
		case USB_SPEED_SUPER:	/* FALLTHROUGH */
		case USB_SPEED_UNKNOWN:	/* FALTHROUGH */
		default:
			reg |= DWC3_DSTS_SUPERSPEED;
		}
	}
	dwc3_writel(dwc->regs, DWC3_DCFG, reg);

	dwc->start_config_issued = false;

	/* Start with SuperSpeed Default */
	dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);

	dep = dwc->eps[0];
	dep->endpoint.maxburst = 1;
	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
	if (ret) {
		dev_err(dwc->dev, "failed to enable %s\n", dep->name);
	if (!dwc->dotg) {
		ret = __dwc3_gadget_start(dwc);
		if (ret)
			goto err1;
	}

	dep = dwc->eps[1];
	dep->endpoint.maxburst = 1;
	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
	if (ret) {
		dev_err(dwc->dev, "failed to enable %s\n", dep->name);
		goto err2;
	}

	/* begin to receive SETUP packets */
	dwc->ep0state = EP0_SETUP_PHASE;
	dwc3_ep0_out_start(dwc);

	dwc3_gadget_enable_irq(dwc);

	spin_unlock_irqrestore(&dwc->lock, flags);
	pm_runtime_put(dwc->dev);

	return 0;

err2:
	__dwc3_gadget_ep_disable(dwc->eps[0]);

err1:
	spin_unlock_irqrestore(&dwc->lock, flags);
	pm_runtime_put(dwc->dev);