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:
parent
7c8e890941
commit
2c4593ecc9
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue