static int ci_ehci_bus_suspend(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); int port; u32 tmp; struct device *dev = hcd->self.controller; struct ci_hdrc *ci = dev_get_drvdata(dev); int ret = orig_bus_suspend(hcd); if (ret) return ret; port = HCS_N_PORTS(ehci->hcs_params); while (port--) { u32 __iomem *reg = &ehci->regs->port_status[port]; u32 portsc = ehci_readl(ehci, reg); if (portsc & PORT_CONNECT) { /* * For chipidea, the resume signal will be ended * automatically, so for remote wakeup case, the * usbcmd.rs may not be set before the resume has * ended if other resume paths consumes too much * time (~24ms), in that case, the SOF will not * send out within 3ms after resume ends, then the * high speed device will enter full speed mode. */ tmp = ehci_readl(ehci, &ehci->regs->command); tmp |= CMD_RUN; ehci_writel(ehci, tmp, &ehci->regs->command); /* * It needs a short delay between set RS bit and PHCD. */ usleep_range(150, 200); /* * 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, reg, PORT_SUSPEND, PORT_SUSPEND, 5000)) ehci_err(ehci, "timeout waiting for SUSPEND\n"); if (ci->platdata->flags & CI_HDRC_IMX_IS_HSIC) ci_ehci_override_wakeup_flag(ehci, reg, PORT_WKDISC_E | PORT_WKCONN_E, false); if (hcd->usb_phy && test_bit(port, &ehci->bus_suspended) && (ehci_port_speed(ehci, portsc) == USB_PORT_STAT_HIGH_SPEED)) /* * notify the USB PHY, it is for global * suspend case. */ usb_phy_notify_suspend(hcd->usb_phy, USB_SPEED_HIGH); break; } } return 0; }
static int tegra_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); struct tegra_ehci_hcd *tegra = (struct tegra_ehci_hcd *)ehci->priv; u32 __iomem *status_reg; u32 temp; unsigned long flags; int retval = 0; status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; spin_lock_irqsave(&ehci->lock, flags); if (typeReq == GetPortStatus) { temp = ehci_readl(ehci, status_reg); if (tegra->port_resuming && !(temp & PORT_SUSPEND)) { /* Resume completed, re-enable disconnect detection */ tegra->port_resuming = 0; tegra_usb_phy_postresume(hcd->phy); } } else 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)) pr_err("%s: timeout waiting for SUSPEND\n", __func__); set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); goto done; } /* For USB1 port we need to issue Port Reset twice internally */ if (tegra->needs_double_reset && (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET)) { spin_unlock_irqrestore(&ehci->lock, flags); return tegra_ehci_internal_port_reset(ehci, status_reg); } /* * Tegra host controller will time the resume operation to clear the bit * when the port control state switches to HS or FS Idle. This behavior * is different from EHCI where the host controller driver is required * to set this bit to a zero after the resume duration is timed in the * driver. */ else if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { temp = ehci_readl(ehci, status_reg); if ((temp & PORT_RESET) || !(temp & PORT_PE)) { retval = -EPIPE; goto done; } if (!(temp & PORT_SUSPEND)) goto done; /* Disable disconnect detection during port resume */ tegra_usb_phy_preresume(hcd->phy); ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); /* start resume signalling */ ehci_writel(ehci, temp | PORT_RESUME, status_reg); set_bit(wIndex-1, &ehci->resuming_ports); spin_unlock_irqrestore(&ehci->lock, flags); msleep(20); spin_lock_irqsave(&ehci->lock, flags); /* Poll until the controller clears RESUME and SUSPEND */ if (ehci_handshake(ehci, status_reg, PORT_RESUME, 0, 2000)) pr_err("%s: timeout waiting for RESUME\n", __func__); if (ehci_handshake(ehci, status_reg, PORT_SUSPEND, 0, 2000)) pr_err("%s: timeout waiting for SUSPEND\n", __func__); ehci->reset_done[wIndex-1] = 0; clear_bit(wIndex-1, &ehci->resuming_ports); tegra->port_resuming = 1; goto done; } spin_unlock_irqrestore(&ehci->lock, flags); /* Handle the hub control events here */ return orig_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); done: spin_unlock_irqrestore(&ehci->lock, flags); return retval; }
/* The below code is based on tegra ehci driver */ static int ci_imx_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); ci_ehci_override_wakeup_flag(ehci, status_reg, PORT_WKDISC_E | PORT_WKCONN_E, false); } spin_unlock_irqrestore(&ehci->lock, flags); if (ehci_port_speed(ehci, temp) == USB_PORT_STAT_HIGH_SPEED && hcd->usb_phy) { /* notify the USB PHY */ usb_phy_notify_suspend(hcd->usb_phy, USB_SPEED_HIGH); } spin_lock_irqsave(&ehci->lock, flags); 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"); temp = ehci_readl(ehci, status_reg); if (ehci_port_speed(ehci, temp) == USB_PORT_STAT_HIGH_SPEED && hcd->usb_phy) { /* notify the USB PHY */ usb_phy_notify_resume(hcd->usb_phy, USB_SPEED_HIGH); } } spin_unlock_irqrestore(&ehci->lock, flags); /* Handle the hub control events here */ return orig_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); done: spin_unlock_irqrestore(&ehci->lock, flags); return retval; }