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: Frieder Schrempf <frieder.schrempf@kontron.de>
Tested-by: Frieder Schrempf <frieder.schrempf@kontron.de>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
This commit is contained in:
Peter Chen 2018-10-16 10:11:42 +08:00
parent 7c8e890941
commit 2c4593ecc9
1 changed files with 75 additions and 0 deletions

View File

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