Commit 7c9a2598 authored by Wesley Cheng's avatar Wesley Cheng Committed by Greg Kroah-Hartman
Browse files

usb: dwc3: gadget: Preserve UDC max speed setting



The USB gadget/UDC driver can restrict the DWC3 controller speed using
dwc3_gadget_set_speed().  Store this setting into a variable, in order for
this setting to persist across controller resets due to runtime PM.

Signed-off-by: default avatarWesley Cheng <wcheng@codeaurora.org>
Link: https://lore.kernel.org/r/1609283136-22140-3-git-send-email-wcheng@codeaurora.org


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 77adb8bd
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1125,6 +1125,7 @@ struct dwc3 {
	u32			nr_scratch;
	u32			u1u2;
	u32			maximum_speed;
	u32			gadget_max_speed;

	u32			ip;

+57 −51
Original line number Diff line number Diff line
@@ -2036,6 +2036,61 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc)
	}
}

static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
{
	u32			reg;

	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
	 */
	if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
	    !dwc->dis_metastability_quirk) {
		reg |= DWC3_DCFG_SUPERSPEED;
	} else {
		switch (dwc->gadget_max_speed) {
		case USB_SPEED_LOW:
			reg |= DWC3_DCFG_LOWSPEED;
			break;
		case USB_SPEED_FULL:
			reg |= DWC3_DCFG_FULLSPEED;
			break;
		case USB_SPEED_HIGH:
			reg |= DWC3_DCFG_HIGHSPEED;
			break;
		case USB_SPEED_SUPER:
			reg |= DWC3_DCFG_SUPERSPEED;
			break;
		case USB_SPEED_SUPER_PLUS:
			if (DWC3_IP_IS(DWC3))
				reg |= DWC3_DCFG_SUPERSPEED;
			else
				reg |= DWC3_DCFG_SUPERSPEED_PLUS;
			break;
		default:
			dev_err(dwc->dev, "invalid speed (%d)\n", dwc->gadget_max_speed);

			if (DWC3_IP_IS(DWC3))
				reg |= DWC3_DCFG_SUPERSPEED;
			else
				reg |= DWC3_DCFG_SUPERSPEED_PLUS;
		}
	}
	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
}

static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
{
	u32			reg;
@@ -2058,6 +2113,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
		if (dwc->has_hibernation)
			reg |= DWC3_DCTL_KEEP_CONNECT;

		__dwc3_gadget_set_speed(dwc);
		dwc->pullups_connected = true;
	} else {
		reg &= ~DWC3_DCTL_RUN_STOP;
@@ -2420,59 +2476,9 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
{
	struct dwc3		*dwc = gadget_to_dwc(g);
	unsigned long		flags;
	u32			reg;

	spin_lock_irqsave(&dwc->lock, flags);
	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
	 */
	if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
	    !dwc->dis_metastability_quirk) {
		reg |= DWC3_DCFG_SUPERSPEED;
	} else {
		switch (speed) {
		case USB_SPEED_LOW:
			reg |= DWC3_DCFG_LOWSPEED;
			break;
		case USB_SPEED_FULL:
			reg |= DWC3_DCFG_FULLSPEED;
			break;
		case USB_SPEED_HIGH:
			reg |= DWC3_DCFG_HIGHSPEED;
			break;
		case USB_SPEED_SUPER:
			reg |= DWC3_DCFG_SUPERSPEED;
			break;
		case USB_SPEED_SUPER_PLUS:
			if (DWC3_IP_IS(DWC3))
				reg |= DWC3_DCFG_SUPERSPEED;
			else
				reg |= DWC3_DCFG_SUPERSPEED_PLUS;
			break;
		default:
			dev_err(dwc->dev, "invalid speed (%d)\n", speed);

			if (DWC3_IP_IS(DWC3))
				reg |= DWC3_DCFG_SUPERSPEED;
			else
				reg |= DWC3_DCFG_SUPERSPEED_PLUS;
		}
	}
	dwc3_writel(dwc->regs, DWC3_DCFG, reg);

	dwc->gadget_max_speed = speed;
	spin_unlock_irqrestore(&dwc->lock, flags);
}