Commit 2c4593ec authored by Peter Chen's avatar Peter Chen
Browse files

usb: chipidea: host: override ehci->hub_control



The chipidea controller has some special requirements during
suspend/resume, override common ehci->hub_control to implement
it.

Reviewed-by: default avatarFrieder Schrempf <frieder.schrempf@kontron.de>
Tested-by: default avatarFrieder Schrempf <frieder.schrempf@kontron.de>
Signed-off-by: default avatarPeter Chen <peter.chen@nxp.com>
parent 7c8e8909
Loading
Loading
Loading
Loading
+75 −0
Original line number Diff line number Diff line
@@ -220,6 +220,80 @@ void ci_hdrc_host_destroy(struct ci_hdrc *ci)
		host_stop(ci);
}

/* The below code is based on tegra ehci driver */
static int ci_ehci_hub_control(
	struct usb_hcd	*hcd,
	u16		typeReq,
	u16		wValue,
	u16		wIndex,
	char		*buf,
	u16		wLength
)
{
	struct ehci_hcd	*ehci = hcd_to_ehci(hcd);
	u32 __iomem	*status_reg;
	u32		temp;
	unsigned long	flags;
	int		retval = 0;
	struct device *dev = hcd->self.controller;
	struct ci_hdrc *ci = dev_get_drvdata(dev);

	status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1];

	spin_lock_irqsave(&ehci->lock, flags);

	if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
		temp = ehci_readl(ehci, status_reg);
		if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) {
			retval = -EPIPE;
			goto done;
		}

		temp &= ~(PORT_RWC_BITS | PORT_WKCONN_E);
		temp |= PORT_WKDISC_E | PORT_WKOC_E;
		ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);

		/*
		 * If a transaction is in progress, there may be a delay in
		 * suspending the port. Poll until the port is suspended.
		 */
		if (ehci_handshake(ehci, status_reg, PORT_SUSPEND,
			PORT_SUSPEND, 5000))
			ehci_err(ehci, "timeout waiting for SUSPEND\n");

		if (ci->platdata->flags & CI_HDRC_IMX_IS_HSIC) {
			if (ci->platdata->notify_event)
				ci->platdata->notify_event(ci,
					CI_HDRC_IMX_HSIC_SUSPEND_EVENT);

			temp = ehci_readl(ehci, status_reg);
			temp &= ~(PORT_WKDISC_E | PORT_WKCONN_E);
			ehci_writel(ehci, temp, status_reg);
		}

		set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
		goto done;
	}

	/*
	 * After resume has finished, it needs do some post resume
	 * operation for some SoCs.
	 */
	else if (typeReq == ClearPortFeature &&
		wValue == USB_PORT_FEAT_C_SUSPEND) {
		/* Make sure the resume has finished, it should be finished */
		if (ehci_handshake(ehci, status_reg, PORT_RESUME, 0, 25000))
			ehci_err(ehci, "timeout waiting for resume\n");
	}

	spin_unlock_irqrestore(&ehci->lock, flags);

	/* Handle the hub control events here */
	return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
done:
	spin_unlock_irqrestore(&ehci->lock, flags);
	return retval;
}
static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
{
	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
@@ -298,4 +372,5 @@ void ci_hdrc_host_driver_init(void)
	ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides);
	orig_bus_suspend = ci_ehci_hc_driver.bus_suspend;
	ci_ehci_hc_driver.bus_suspend = ci_ehci_bus_suspend;
	ci_ehci_hc_driver.hub_control = ci_ehci_hub_control;
}