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); int ports = HCS_N_PORTS(ehci->hcs_params); u32 temp, status, cmd_run; u32 __iomem *status_reg; u32 usbsts_reg; unsigned long flags; int retval = 0; unsigned selector; struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); bool hsic = false; mutex_lock(&tegra->tegra_ehci_hcd_mutex); if (!tegra->host_resumed) { if (buf) memset (buf, 0, wLength); mutex_unlock(&tegra->tegra_ehci_hcd_mutex); return retval; } hsic = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC); status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; spin_lock_irqsave(&ehci->lock, flags); /* * In ehci_hub_control() for USB_PORT_FEAT_ENABLE clears the other bits * that are write on clear, by writing back the register read value, so * USB_PORT_FEAT_ENABLE is handled by masking the set on clear bits */ if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_ENABLE) { temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS; ehci_writel(ehci, temp & ~PORT_PE, status_reg); goto done; } else if (typeReq == GetPortStatus) { temp = ehci_readl(ehci, status_reg); /* check port is in resume state */ if (tegra->port_resuming) { int delay = ehci->reset_done[wIndex-1] - jiffies; /* Sometimes it seems we get called too soon... In that case, wait.*/ if (delay > 0) { ehci_dbg(ehci, "GetPortStatus called too soon, waiting %dms...\n", delay); mdelay(jiffies_to_msecs(delay)); } /* Ensure the port PORT_SUSPEND and PORT_RESUME has cleared */ if (handshake(ehci, status_reg, (PORT_SUSPEND | PORT_RESUME), 0, 25000)) { pr_err("%s: timeout waiting for SUSPEND to clear\n", __func__); } tegra->port_resuming = 0; tegra_usb_phy_postresume(tegra->phy, false); if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { ehci->command |= CMD_RUN; cmd_run = ehci_readl(ehci, &ehci->regs->command); cmd_run |= CMD_RUN; /* * ehci run bit is disabled to avoid SOF. * 2LS WAR is executed by now enable the run bit. */ ehci_writel(ehci, cmd_run, &ehci->regs->command); /* Now we can safely re-enable irqs */ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); } } } 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_WKCONN_E; temp |= PORT_WKDISC_E | PORT_WKOC_E; ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); /* Need a 4ms delay before the controller goes to suspend */ mdelay(4); /* * If a transaction is in progress, there may be a delay in * suspending the port. Poll until the port is suspended. */ if (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); /* * If RUN bit is disabled interrupt is not generated after suspend. * This change on T20 will allow ASE interrupt generated after suspend * which will unlink the qheads. */ #ifndef CONFIG_ARCH_TEGRA_2x_SOC if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { /* Disable RUN bit. */ ehci->command &= ~CMD_RUN; cmd_run = ehci_readl(ehci, &ehci->regs->command); cmd_run &= ~CMD_RUN; ehci_writel(ehci, cmd_run, &ehci->regs->command); if (handshake (ehci, &ehci->regs->status, STS_HALT, STS_HALT, 16 * 125)) pr_err("%s() timeout waiting for STS_HALT\n", __func__); } #endif tegra_usb_phy_postsuspend(tegra->phy, false); goto done; } /* * 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; tegra->port_resuming = 1; if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { /* disable interrupts */ ehci_writel(ehci, 0, &ehci->regs->intr_enable); /* Disable RUN bit. */ ehci->command &= ~CMD_RUN; cmd_run = ehci_readl(ehci, &ehci->regs->command); cmd_run &= ~CMD_RUN; ehci_writel(ehci, cmd_run, &ehci->regs->command); if (handshake (ehci, &ehci->regs->status, STS_HALT, STS_HALT, 16 * 125)) pr_err("%s() timeout waiting for STS_HALT\n", __func__); } /* Disable disconnect detection during port resume */ tegra_usb_phy_preresume(tegra->phy, false); #ifndef CONFIG_ARCH_TEGRA_2x_SOC if (tegra->phy->usb_phy_type != TEGRA_USB_PHY_TYPE_UTMIP) { #endif ehci_dbg(ehci, "%s:USBSTS = 0x%x", __func__, ehci_readl(ehci, &ehci->regs->status)); usbsts_reg = ehci_readl(ehci, &ehci->regs->status); ehci_writel(ehci, usbsts_reg, &ehci->regs->status); usbsts_reg = ehci_readl(ehci, &ehci->regs->status); udelay(20); if (handshake(ehci, &ehci->regs->status, STS_SRI, STS_SRI, 2000)) pr_err("%s: timeout set for STS_SRI\n", __func__); usbsts_reg = ehci_readl(ehci, &ehci->regs->status); ehci_writel(ehci, usbsts_reg, &ehci->regs->status); if (handshake(ehci, &ehci->regs->status, STS_SRI, 0, 2000)) pr_err("%s: timeout clear STS_SRI\n", __func__); if (handshake(ehci, &ehci->regs->status, STS_SRI, STS_SRI, 2000)) pr_err("%s: timeout set STS_SRI\n", __func__); udelay(20); #ifndef CONFIG_ARCH_TEGRA_2x_SOC } #endif temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); /* start resume signaling */ ehci_writel(ehci, temp | PORT_RESUME, status_reg); ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); /* whoever resumes must GetPortStatus to complete it!! */ goto done; } /* Handle port reset here */ if ((hsic) && (typeReq == SetPortFeature) && ((wValue == USB_PORT_FEAT_RESET) || (wValue == USB_PORT_FEAT_POWER))) { selector = wIndex >> 8; wIndex &= 0xff; if (!wIndex || wIndex > ports) { retval = -EPIPE; goto done; } wIndex--; status = 0; temp = ehci_readl(ehci, status_reg); if (temp & PORT_OWNER) goto done; temp &= ~PORT_RWC_BITS; switch (wValue) { case USB_PORT_FEAT_RESET: { if (temp & PORT_RESUME) { retval = -EPIPE; goto done; } /* line status bits may report this as low speed, * which can be fine if this root hub has a * transaction translator built in. */ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT && !ehci_is_TDI(ehci) && PORT_USB11 (temp)) { ehci_dbg (ehci, "port %d low speed --> companion\n", wIndex + 1); temp |= PORT_OWNER; ehci_writel(ehci, temp, status_reg); } else { ehci_vdbg(ehci, "port %d reset\n", wIndex + 1); temp &= ~PORT_PE; /* * caller must wait, then call GetPortStatus * usb 2.0 spec says 50 ms resets on root */ ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(50); ehci_writel(ehci, temp, status_reg); if (hsic && (wIndex == 0)) tegra_usb_phy_bus_reset(tegra->phy); } break; } case USB_PORT_FEAT_POWER: { if (HCS_PPC(ehci->hcs_params)) ehci_writel(ehci, temp | PORT_POWER, status_reg); if (hsic && (wIndex == 0)) tegra_usb_phy_bus_connect(tegra->phy); break; } } goto done; }
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); int ports = HCS_N_PORTS(ehci->hcs_params); u32 temp, status; u32 __iomem *status_reg; u32 usbsts_reg; unsigned long flags; int retval = 0; unsigned selector; struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); bool hsic = false; mutex_lock(&tegra->tegra_ehci_hcd_mutex); if (!tegra->host_resumed) { if (buf) memset (buf, 0, wLength); mutex_unlock(&tegra->tegra_ehci_hcd_mutex); return retval; } hsic = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC); /* log for resume fail */ printk(KERN_INFO"%s: typereq: %x wValue: %x USBMODE: %x, USBCMD: %x, PORTSC: %x, USBSTS: %x HOSTPC: %x \n", __func__, typeReq, wValue, readl(&ehci->regs->command + (USBMODE)), readl(&ehci->regs->command), readl(&ehci->regs->port_status[0]), readl(&ehci->regs->status), readl(hcd->regs + HOSTPC_REG_OFFSET)); /* 84717-1 patch */ if(hsic) s_hsic_hcd = hcd; /* 84717-1 patch */ status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; pr_info("%s hsic=%s typeReq=%x wValue=%x wIndex=%x\n", __func__,hsic ? "true" : "false", typeReq, wValue, wIndex); spin_lock_irqsave(&ehci->lock, flags); /* * In ehci_hub_control() for USB_PORT_FEAT_ENABLE clears the other bits * that are write on clear, by writing back the register read value, so * USB_PORT_FEAT_ENABLE is handled by masking the set on clear bits */ if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_ENABLE) { temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS; ehci_writel(ehci, temp & ~PORT_PE, status_reg); goto done; } else if (typeReq == GetPortStatus) { temp = ehci_readl(ehci, status_reg); if (tegra->port_resuming && !(temp & PORT_SUSPEND) && time_after_eq(jiffies, ehci->reset_done[wIndex-1])) { /* Resume completed, re-enable disconnect detection */ tegra->port_resuming = 0; clear_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); ehci->reset_done[wIndex-1] = 0; spin_unlock_irqrestore(&ehci->lock, flags); tegra_usb_phy_postresume(tegra->phy, false); spin_lock_irqsave(&ehci->lock, flags); #ifndef CONFIG_ARCH_TEGRA_2x_SOC if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { ehci->command |= CMD_RUN; /* * ehci run bit is disabled to avoid SOF. * 2LS WAR is executed by now enable the run bit. */ ehci_writel(ehci, ehci->command, &ehci->regs->command); } #endif } } 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; printk(KERN_INFO"%s retval=%d\n", __func__,retval); goto done; } printk(KERN_INFO"%s USB_PORT_FEAT_SUSPEND\n", __func__); tegra_usb_phy_presuspend(tegra->phy, false); pr_info("%s: SetPortFeature->USB_PORT_FEAT_SUSPEND\n", __func__); temp &= ~PORT_WKCONN_E; temp |= PORT_WKDISC_E | PORT_WKOC_E; ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); spin_unlock_irqrestore(&ehci->lock, flags); /* Need a 4ms delay before the controller goes to suspend */ mdelay(4); /* * If a transaction is in progress, there may be a delay in * suspending the port. Poll until the port is suspended. */ if (handshake(ehci, status_reg, PORT_SUSPEND, PORT_SUSPEND, 5000)) pr_err("%s: timeout waiting for SUSPEND\n", __func__); spin_lock_irqsave(&ehci->lock, flags); set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); spin_unlock_irqrestore(&ehci->lock, flags); tegra_usb_phy_postsuspend(tegra->phy, false); spin_lock_irqsave(&ehci->lock, flags); goto done; } /* * 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; tegra->port_resuming = 1; /* Disable disconnect detection during port resume */ spin_unlock_irqrestore(&ehci->lock, flags); tegra_usb_phy_preresume(tegra->phy, false); spin_lock_irqsave(&ehci->lock, flags); pr_info("%s: ClearPortFeature->USB_PORT_FEAT_SUSPEND\n", __func__); #ifndef CONFIG_ARCH_TEGRA_2x_SOC if (tegra->phy->usb_phy_type != TEGRA_USB_PHY_TYPE_UTMIP) { #endif ehci_dbg(ehci, "%s:USBSTS = 0x%x", __func__, ehci_readl(ehci, &ehci->regs->status)); usbsts_reg = ehci_readl(ehci, &ehci->regs->status); ehci_writel(ehci, usbsts_reg, &ehci->regs->status); usbsts_reg = ehci_readl(ehci, &ehci->regs->status); spin_unlock_irqrestore(&ehci->lock, flags); udelay(20); if (handshake(ehci, &ehci->regs->status, STS_SRI, STS_SRI, 2000)) pr_err("%s: timeout set for STS_SRI\n", __func__); spin_lock_irqsave(&ehci->lock, flags); spin_unlock_irqrestore(&ehci->lock, flags); usbsts_reg = ehci_readl(ehci, &ehci->regs->status); ehci_writel(ehci, usbsts_reg, &ehci->regs->status); if (handshake(ehci, &ehci->regs->status, STS_SRI, 0, 2000)) pr_err("%s: timeout clear STS_SRI\n", __func__); if (handshake(ehci, &ehci->regs->status, STS_SRI, STS_SRI, 2000)) pr_err("%s: timeout set STS_SRI\n", __func__); udelay(20); spin_lock_irqsave(&ehci->lock, flags); #ifndef CONFIG_ARCH_TEGRA_2x_SOC } #endif temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); /* start resume signaling */ ehci_writel(ehci, temp | PORT_RESUME, status_reg); ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); /* whoever resumes must GetPortStatus to complete it!! */ goto done; } /* Handle port reset here */ if ((hsic) && (typeReq == SetPortFeature) && ((wValue == USB_PORT_FEAT_RESET) || (wValue == USB_PORT_FEAT_POWER))) { selector = wIndex >> 8; wIndex &= 0xff; if (!wIndex || wIndex > ports) { retval = -EPIPE; goto done; } wIndex--; status = 0; temp = ehci_readl(ehci, status_reg); if (temp & PORT_OWNER) goto done; temp &= ~PORT_RWC_BITS; switch (wValue) { case USB_PORT_FEAT_RESET: { if (temp & PORT_RESUME) { retval = -EPIPE; goto done; } /* line status bits may report this as low speed, * which can be fine if this root hub has a * transaction translator built in. */ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT && !ehci_is_TDI(ehci) && PORT_USB11 (temp)) { ehci_dbg (ehci, "port %d low speed --> companion\n", wIndex + 1); temp |= PORT_OWNER; ehci_writel(ehci, temp, status_reg); } else { ehci_vdbg(ehci, "port %d reset\n", wIndex + 1); temp &= ~PORT_PE; /* * caller must wait, then call GetPortStatus * usb 2.0 spec says 50 ms resets on root */ ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(50); ehci_writel(ehci, temp, status_reg); if (hsic && (wIndex == 0)) { spin_unlock_irqrestore(&ehci->lock, flags); tegra_usb_phy_bus_reset(tegra->phy); spin_lock_irqsave(&ehci->lock, flags); } } break; } case USB_PORT_FEAT_POWER: { if (HCS_PPC(ehci->hcs_params)) ehci_writel(ehci, temp | PORT_POWER, status_reg); if (hsic && (wIndex == 0)) { spin_unlock_irqrestore(&ehci->lock, flags); tegra_usb_phy_bus_connect(tegra->phy); spin_lock_irqsave(&ehci->lock, flags); } break; } } goto done; }