/* * this function is used to check if the device support LPM * if yes, mark the PORTSC register with PORT_LPM bit */ static int __maybe_unused ehci_lpm_check(struct ehci_hcd *ehci, int port) { u32 __iomem *portsc ; u32 val32; int retval; portsc = &ehci->regs->port_status[port-1]; val32 = ehci_readl(ehci, portsc); if (!(val32 & PORT_DEV_ADDR)) { ehci_dbg(ehci, "LPM: no device attached\n"); return -ENODEV; } val32 |= PORT_LPM; ehci_writel(ehci, val32, portsc); msleep(5); val32 |= PORT_SUSPEND; ehci_dbg(ehci, "Sending LPM 0x%08x to port %d\n", val32, port); ehci_writel(ehci, val32, portsc); /* wait for ACK */ msleep(10); retval = handshake(ehci, &ehci->regs->port_status[port-1], PORT_SSTS, PORTSC_SUSPEND_STS_ACK, 125); dbg_port(ehci, "LPM", port, val32); if (retval != -ETIMEDOUT) { ehci_dbg(ehci, "LPM: device ACK for LPM\n"); val32 |= PORT_LPM; /* * now device should be in L1 sleep, let's wake up the device * so that we can complete enumeration. */ ehci_writel(ehci, val32, portsc); msleep(10); val32 |= PORT_RESUME; ehci_writel(ehci, val32, portsc); } else { ehci_dbg(ehci, "LPM: device does not ACK, disable LPM %d\n", retval); val32 &= ~PORT_LPM; retval = -ETIMEDOUT; ehci_writel(ehci, val32, portsc); } return retval; }
static int 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; unsigned long flags; int retval = 0; /* * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. * HCS_INDICATOR may say we can change LEDs to off/amber/green. * (track current state ourselves) ... blink for diagnostics, * power, "this is the one", etc. EHCI spec supports this. */ spin_lock_irqsave (&ehci->lock, flags); switch (typeReq) { case ClearHubFeature: switch (wValue) { case C_HUB_LOCAL_POWER: case C_HUB_OVER_CURRENT: /* no hub-wide feature/status flags */ break; default: goto error; } break; case ClearPortFeature: if (!wIndex || wIndex > ports) goto error; wIndex--; temp = readl (&ehci->regs->port_status [wIndex]); if (temp & PORT_OWNER) break; switch (wValue) { case USB_PORT_FEAT_ENABLE: writel (temp & ~PORT_PE, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_ENABLE: writel((temp & ~PORT_RWC_BITS) | PORT_PEC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_SUSPEND: if (temp & PORT_RESET) goto error; if (temp & PORT_SUSPEND) { if ((temp & PORT_PE) == 0) goto error; /* resume signaling for 20 msec */ temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); writel (temp | PORT_RESUME, &ehci->regs->port_status [wIndex]); ehci->reset_done [wIndex] = jiffies + msecs_to_jiffies (20); } break; case USB_PORT_FEAT_C_SUSPEND: /* we auto-clear this feature */ break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) writel (temp & ~(PORT_RWC_BITS | PORT_POWER), &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_CONNECTION: writel((temp & ~PORT_RWC_BITS) | PORT_CSC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_OVER_CURRENT: writel((temp & ~PORT_RWC_BITS) | PORT_OCC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_RESET: /* GetPortStatus clears reset */ break; default: goto error; } readl (&ehci->regs->command); /* unblock posted write */ break; case GetHubDescriptor: ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *) buf); break; case GetHubStatus: /* no hub-wide feature/status flags */ memset (buf, 0, 4); //cpu_to_le32s ((u32 *) buf); break; case GetPortStatus: if (!wIndex || wIndex > ports) goto error; wIndex--; status = 0; temp = readl (&ehci->regs->port_status [wIndex]); // wPortChange bits if (temp & PORT_CSC) status |= 1 << USB_PORT_FEAT_C_CONNECTION; if (temp & PORT_PEC) status |= 1 << USB_PORT_FEAT_C_ENABLE; if (temp & PORT_OCC) status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; /* whoever resumes must GetPortStatus to complete it!! */ if ((temp & PORT_RESUME) && time_after (jiffies, ehci->reset_done [wIndex])) { status |= 1 << USB_PORT_FEAT_C_SUSPEND; ehci->reset_done [wIndex] = 0; /* stop resume signaling */ temp = readl (&ehci->regs->port_status [wIndex]); writel (temp & ~(PORT_RWC_BITS | PORT_RESUME), &ehci->regs->port_status [wIndex]); retval = handshake ( &ehci->regs->port_status [wIndex], PORT_RESUME, 0, 2000 /* 2msec */); if (retval != 0) { ehci_err (ehci, "port %d resume error %d\n", wIndex + 1, retval); goto error; } temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); } /* whoever resets must GetPortStatus to complete it!! */ if ((temp & PORT_RESET) && time_after (jiffies, ehci->reset_done [wIndex])) { status |= 1 << USB_PORT_FEAT_C_RESET; ehci->reset_done [wIndex] = 0; /* force reset to complete */ writel (temp & ~(PORT_RWC_BITS | PORT_RESET), &ehci->regs->port_status [wIndex]); /* REVISIT: some hardware needs 550+ usec to clear * this bit; seems too long to spin routinely... */ retval = handshake ( &ehci->regs->port_status [wIndex], PORT_RESET, 0, 750); if (retval != 0) { ehci_err (ehci, "port %d reset error %d\n", wIndex + 1, retval); goto error; } /* see what we found out */ temp = check_reset_complete (ehci, wIndex, readl (&ehci->regs->port_status [wIndex])); } // don't show wPortStatus if it's owned by a companion hc if (!(temp & PORT_OWNER)) { if (temp & PORT_CONNECT) { status |= 1 << USB_PORT_FEAT_CONNECTION; // status may be from integrated TT status |= ehci_port_speed(ehci, temp); } if (temp & PORT_PE) status |= 1 << USB_PORT_FEAT_ENABLE; if (temp & (PORT_SUSPEND|PORT_RESUME)) status |= 1 << USB_PORT_FEAT_SUSPEND; if (temp & PORT_OC) status |= 1 << USB_PORT_FEAT_OVER_CURRENT; if (temp & PORT_RESET) status |= 1 << USB_PORT_FEAT_RESET; if (temp & PORT_POWER) status |= 1 << USB_PORT_FEAT_POWER; } #ifndef EHCI_VERBOSE_DEBUG if (status & ~0xffff) /* only if wPortChange is interesting */ #endif dbg_port (ehci, "GetStatus", wIndex + 1, temp); // we "know" this alignment is good, caller used kmalloc()... *((__le32 *) buf) = cpu_to_le32 (status); break; case SetHubFeature: switch (wValue) { case C_HUB_LOCAL_POWER: case C_HUB_OVER_CURRENT: /* no hub-wide feature/status flags */ break; default: goto error; } break; case SetPortFeature: if (!wIndex || wIndex > ports) goto error; wIndex--; temp = readl (&ehci->regs->port_status [wIndex]); if (temp & PORT_OWNER) break; temp &= ~PORT_RWC_BITS; switch (wValue) { case USB_PORT_FEAT_SUSPEND: if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) goto error; if (hcd->remote_wakeup) temp |= PORT_WAKE_BITS; writel (temp | PORT_SUSPEND, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) writel (temp | PORT_POWER, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_RESET: if (temp & PORT_RESUME) goto error; /* 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; } else { ehci_vdbg (ehci, "port %d reset\n", wIndex + 1); temp |= PORT_RESET; 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); } writel (temp, &ehci->regs->port_status [wIndex]); break; default: goto error; } readl (&ehci->regs->command); /* unblock posted writes */ break; default: error: /* "stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore (&ehci->lock, flags); return retval; }
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 usbcmd; 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; u32 usbcmd_bef_rsm, usbsts_bef_rsm, usbmode_bef_rsm, portsc_bef_rsm; u32 usbcmd_in_rsm, usbsts_in_rsm, usbmode_in_rsm, portsc_in_rsm; u32 rsm_idx; if (tegra->phy->instance == 1) { struct tegra_ulpi_config *config = tegra->phy->config; hsic = (config->inf_type == TEGRA_USB_UHSIC); } 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); ehci_writel(ehci, (temp & ~PORT_RWC_BITS) & ~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 */ tegra->port_resuming = 0; clear_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); ehci->reset_done[wIndex-1] = 0; tegra_usb_phy_postresume(tegra->phy, false); if (tegra->phy->instance == 1) { // Read the USBCMD, USBSTS, USBMODE, PORTSC register here, don't print it at this time. usbcmd_bef_rsm = ehci_readl(ehci, &ehci->regs->command); usbsts_bef_rsm = ehci_readl(ehci, &ehci->regs->status); usbmode_bef_rsm = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET); portsc_bef_rsm = ehci_readl(ehci, &ehci->regs-> port_status[(wIndex & 0xff) - 1]); if (((temp & (3 << 10)) == PORT_LS_J) && !(temp & PORT_PE)) { pr_err("%s: sig = j, resume failed\n", __func__); retval = -EPIPE; goto done; } else if (((temp & (3 << 10)) == PORT_LS_J) && (temp & PORT_PE)) { udelay(5); temp = ehci_readl(ehci, status_reg); dbg_port (ehci, "BusGetPortStatus", 0, temp); } printk("%s: usb-inst %d reg after resume USBCMD=%x, USBSTS=%x, USBMODE=%x, PORTSC=%x\n", __func__, tegra->phy->instance, usbcmd_bef_rsm, usbsts_bef_rsm, usbmode_bef_rsm, portsc_bef_rsm); } } } 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; } // Read the USBCMD, USBSTS, USBMODE, PORTSC register here, don't print it at this time. usbcmd_bef_rsm = ehci_readl(ehci, &ehci->regs->command); usbsts_bef_rsm = ehci_readl(ehci, &ehci->regs->status); usbmode_bef_rsm = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET); portsc_bef_rsm = ehci_readl(ehci, &ehci->regs-> port_status[(wIndex & 0xff) - 1]); printk("%s: usb-inst %d reg before suspend USBCMD=%x, USBSTS=%x, USBMODE=%x, PORTSC=%x\n", __func__, tegra->phy->instance, usbcmd_bef_rsm, usbsts_bef_rsm, usbmode_bef_rsm, portsc_bef_rsm); /* After above check the port must be connected. * Set appropriate bit thus could put phy into low power * mode if we have hostpc feature */ 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); printk("%s: usb-inst %d doing port suspend\n", __func__, tegra->phy->instance); if (handshake(ehci, status_reg, PORT_SUSPEND, PORT_SUSPEND, 5000)) pr_err("%s: timeout waiting for PORT_SUSPEND\n", __func__); set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); 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; if (temp & PORT_RESUME) { usbcmd = ehci_readl(ehci, &ehci->regs->command); usbcmd &= ~CMD_RUN; ehci_writel(ehci, usbcmd, &ehci->regs->command); /* detect remote wakeup */ ehci_dbg(ehci, "%s: usb-inst %d remote wakeup\n", __func__, tegra->phy->instance); spin_unlock_irq(&ehci->lock); msleep(20); spin_lock_irq(&ehci->lock); /* Poll until the controller clears RESUME and SUSPEND */ if (handshake(ehci, status_reg, PORT_RESUME, 0, 2000)) pr_err("%s: usb-inst %d timeout waiting for RESUME\n", __func__, tegra->phy->instance); if (handshake(ehci, status_reg, PORT_SUSPEND, 0, 2000)) pr_err("%s: usb-inst %d timeout waiting for SUSPEND\n", __func__, tegra->phy->instance); /* Since we skip remote wakeup event, put controller in suspend again and resume port later */ temp = ehci_readl(ehci, status_reg); temp |= PORT_SUSPEND; ehci_writel(ehci, temp, status_reg); mdelay(4); /* Wait until port suspend completes */ if (handshake(ehci, status_reg, PORT_SUSPEND, PORT_SUSPEND, 1000)) pr_err("%s: usb-inst %d timeout waiting for PORT_SUSPEND\n", __func__, tegra->phy->instance); } tegra->port_resuming = 1; tegra_usb_phy_preresume(tegra->phy, false); // Read the USBCMD, USBSTS, USBMODE, PORTSC register here, don't print it at this time. usbcmd_bef_rsm = ehci_readl(ehci, &ehci->regs->command); usbsts_bef_rsm = ehci_readl(ehci, &ehci->regs->status); usbmode_bef_rsm = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET); portsc_bef_rsm = ehci_readl(ehci, &ehci->regs-> port_status[(wIndex & 0xff) - 1]); 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: usb-inst %d timeout set for STS_SRI\n", __func__, tegra->phy->instance); 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: usb-inst %d timeout clear STS_SRI\n", __func__, tegra->phy->instance); if (handshake(ehci, &ehci->regs->status, STS_SRI, STS_SRI, 2000)) pr_err("%s: usb-inst %d timeout set STS_SRI\n", __func__, tegra->phy->instance); udelay(20); usbcmd = ehci_readl(ehci, &ehci->regs->command); usbcmd |= CMD_RUN; ehci_writel(ehci, usbcmd, &ehci->regs->command); 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); // wait for 1 msec before doing anything else udelay(1000); // Read the USBCMD, USBSTS, USBMODE, PORTSC register here, don't print it at this time. usbcmd_in_rsm = ehci_readl(ehci, &ehci->regs->command); usbsts_in_rsm = ehci_readl(ehci, &ehci->regs->status); usbmode_in_rsm = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET); portsc_in_rsm = ehci_readl(ehci, &ehci->regs-> port_status[(wIndex & 0xff) - 1]); // Print the registers here printk("%s: usb-inst %d reg before resume USBCMD=%x, USBSTS=%x, USBMODE=%x, PORTSC=%x\n", __func__, tegra->phy->instance, usbcmd_bef_rsm, usbsts_bef_rsm, usbmode_bef_rsm, portsc_bef_rsm); printk("%s: usb-inst %d reg during resume USBCMD=%x, USBSTS=%x, USBMODE=%x, PORTSC=%x\n", __func__, tegra->phy->instance, usbcmd_in_rsm, usbsts_in_rsm, usbmode_in_rsm, portsc_in_rsm); /* 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 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; unsigned long flags; int retval = 0; /* * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. * HCS_INDICATOR may say we can change LEDs to off/amber/green. * (track current state ourselves) ... blink for diagnostics, * power, "this is the one", etc. EHCI spec supports this. */ spin_lock_irqsave (&ehci->lock, flags); switch (typeReq) { case ClearHubFeature: switch (wValue) { case C_HUB_LOCAL_POWER: case C_HUB_OVER_CURRENT: /* no hub-wide feature/status flags */ break; default: goto error; } break; case ClearPortFeature: if (!wIndex || wIndex > ports) goto error; wIndex--; temp = readl (&ehci->regs->port_status [wIndex]); if (temp & PORT_OWNER) break; switch (wValue) { case USB_PORT_FEAT_ENABLE: writel (temp & ~PORT_PE, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_ENABLE: writel (temp | PORT_PEC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_SUSPEND: case USB_PORT_FEAT_C_SUSPEND: /* ? */ break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) writel (temp & ~PORT_POWER, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_CONNECTION: writel (temp | PORT_CSC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_OVER_CURRENT: writel (temp | PORT_OCC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_RESET: /* GetPortStatus clears reset */ break; default: goto error; } readl (&ehci->regs->command); /* unblock posted write */ break; case GetHubDescriptor: ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *) buf); break; case GetHubStatus: /* no hub-wide feature/status flags */ memset (buf, 0, 4); //cpu_to_le32s ((u32 *) buf); break; case GetPortStatus: if (!wIndex || wIndex > ports) goto error; wIndex--; status = 0; temp = readl (&ehci->regs->port_status [wIndex]); // wPortChange bits if (temp & PORT_CSC) status |= 1 << USB_PORT_FEAT_C_CONNECTION; if (temp & PORT_PEC) status |= 1 << USB_PORT_FEAT_C_ENABLE; // USB_PORT_FEAT_C_SUSPEND if (temp & PORT_OCC) status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; /* whoever resets must GetPortStatus to complete it!! */ if ((temp & PORT_RESET) && time_after (jiffies, ehci->reset_done [wIndex])) { status |= 1 << USB_PORT_FEAT_C_RESET; /* force reset to complete */ writel (temp & ~PORT_RESET, &ehci->regs->port_status [wIndex]); do { temp = readl ( &ehci->regs->port_status [wIndex]); udelay (10); } while (temp & PORT_RESET); /* see what we found out */ temp = check_reset_complete (ehci, wIndex, temp); } // don't show wPortStatus if it's owned by a companion hc if (!(temp & PORT_OWNER)) { if (temp & PORT_CONNECT) { status |= 1 << USB_PORT_FEAT_CONNECTION; status |= 1 << USB_PORT_FEAT_HIGHSPEED; } if (temp & PORT_PE) status |= 1 << USB_PORT_FEAT_ENABLE; if (temp & PORT_SUSPEND) status |= 1 << USB_PORT_FEAT_SUSPEND; if (temp & PORT_OC) status |= 1 << USB_PORT_FEAT_OVER_CURRENT; if (temp & PORT_RESET) status |= 1 << USB_PORT_FEAT_RESET; if (temp & PORT_POWER) status |= 1 << USB_PORT_FEAT_POWER; } #ifndef EHCI_VERBOSE_DEBUG if (status & ~0xffff) /* only if wPortChange is interesting */ #endif dbg_port (ehci, "GetStatus", wIndex + 1, temp); // we "know" this alignment is good, caller used kmalloc()... *((u32 *) buf) = cpu_to_le32 (status); break; case SetHubFeature: switch (wValue) { case C_HUB_LOCAL_POWER: case C_HUB_OVER_CURRENT: /* no hub-wide feature/status flags */ break; default: goto error; } break; case SetPortFeature: if (!wIndex || wIndex > ports) goto error; wIndex--; temp = readl (&ehci->regs->port_status [wIndex]); if (temp & PORT_OWNER) break; switch (wValue) { case USB_PORT_FEAT_SUSPEND: writel (temp | PORT_SUSPEND, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) writel (temp | PORT_POWER, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_RESET: /* line status bits may report this as low speed */ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT && PORT_USB11 (temp)) { ehci_dbg (ehci, "port %d low speed --> companion\n", wIndex + 1); temp |= PORT_OWNER; } else { ehci_vdbg (ehci, "port %d reset\n", wIndex + 1); temp |= PORT_RESET; temp &= ~PORT_PE; /* * caller must wait, then call GetPortStatus * usb 2.0 spec says 50 ms resets on root */ ehci->reset_done [wIndex] = jiffies + ((50 /* msec */ * HZ) / 1000); } writel (temp, &ehci->regs->port_status [wIndex]); break; default: goto error; } readl (&ehci->regs->command); /* unblock posted writes */ break; default: error: /* "stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore (&ehci->lock, flags); return retval; }
static int omap_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 = &ehci->regs->port_status[ (wIndex & 0xff) - 1]; u32 __iomem *hostpc_reg = NULL; u32 temp, temp1, status; unsigned long flags; int retval = 0; u32 runstop, temp_reg; spin_lock_irqsave(&ehci->lock, flags); switch (typeReq) { case GetPortStatus: wIndex--; status = 0; temp = ehci_readl(ehci, status_reg); /* wPortChange bits */ if (temp & PORT_CSC) status |= USB_PORT_STAT_C_CONNECTION << 16; if (temp & PORT_PEC) status |= USB_PORT_STAT_C_ENABLE << 16; if ((temp & PORT_OCC) && !ignore_oc) { status |= USB_PORT_STAT_C_OVERCURRENT << 16; /* * Hubs should disable port power on over-current. * However, not all EHCI implementations do this * automatically, even if they _do_ support per-port * power switching; they're allowed to just limit the * current. khubd will turn the power back on. */ if (HCS_PPC(ehci->hcs_params)) { ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_POWER), status_reg); } } /* whoever resumes must GetPortStatus to complete it!! */ if (temp & PORT_RESUME) { /* Remote Wakeup received? */ if (!ehci->reset_done[wIndex]) { /* resume signaling for 20 msec */ ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(20); /* check the port again */ mod_timer(&ehci_to_hcd(ehci)->rh_timer, ehci->reset_done[wIndex]); } /* resume completed? */ else if (time_after_eq(jiffies, ehci->reset_done[wIndex])) { clear_bit(wIndex, &ehci->suspended_ports); set_bit(wIndex, &ehci->port_c_suspend); ehci->reset_done[wIndex] = 0; /* * Workaround for OMAP errata: * To Stop Resume Signalling, it is required * to Stop the Host Controller and disable the * TLL Functional Clock. */ /* Stop the Host Controller */ runstop = ehci_readl(ehci, &ehci->regs->command); ehci_writel(ehci, (runstop & ~CMD_RUN), &ehci->regs->command); (void) ehci_readl(ehci, &ehci->regs->command); handshake(ehci, &ehci->regs->status, STS_HALT, STS_HALT, 2000); temp_reg = omap_readl(L3INIT_HSUSBTLL_CLKCTRL); temp_reg &= ~(1 << 9); /* stop resume signaling */ temp = ehci_readl(ehci, status_reg); ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESUME), status_reg); /* Disable the Channel 1 Optional Fclk */ omap_writel(temp_reg, L3INIT_HSUSBTLL_CLKCTRL); retval = handshake(ehci, status_reg, PORT_RESUME, 0, 2000 /* 2msec */); /* * Enable the Host Controller and start the * Channel 1 Optional Fclk since resume has * finished. */ udelay(2); omap_writel((temp_reg | (1 << 9)), L3INIT_HSUSBTLL_CLKCTRL); ehci_writel(ehci, (runstop), &ehci->regs->command); (void) ehci_readl(ehci, &ehci->regs->command); if (retval != 0) { ehci_err(ehci, "port %d resume error %d\n", wIndex + 1, retval); spin_unlock_irqrestore(&ehci->lock, flags); return -EPIPE; } temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); } } /* whoever resets must GetPortStatus to complete it!! */ if ((temp & PORT_RESET) && time_after_eq(jiffies, ehci->reset_done[wIndex])) { status |= USB_PORT_STAT_C_RESET << 16; ehci->reset_done[wIndex] = 0; /* force reset to complete */ ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET), status_reg); /* REVISIT: some hardware needs 550+ usec to clear * this bit; seems too long to spin routinely... */ retval = handshake(ehci, status_reg, PORT_RESET, 0, 1000); if (retval != 0) { ehci_err(ehci, "port %d reset error %d\n", wIndex + 1, retval); spin_unlock_irqrestore(&ehci->lock, flags); return -EPIPE; } /* see what we found out */ temp = check_reset_complete(ehci, wIndex, status_reg, ehci_readl(ehci, status_reg)); } if (!(temp & (PORT_RESUME|PORT_RESET))) ehci->reset_done[wIndex] = 0; /* transfer dedicated ports to the companion hc */ if ((temp & PORT_CONNECT) && test_bit(wIndex, &ehci->companion_ports)) { temp &= ~PORT_RWC_BITS; temp |= PORT_OWNER; ehci_writel(ehci, temp, status_reg); ehci_dbg(ehci, "port %d --> companion\n", wIndex + 1); temp = ehci_readl(ehci, status_reg); } /* * Even if OWNER is set, there's no harm letting khubd * see the wPortStatus values (they should all be 0 except * for PORT_POWER anyway). */ if (temp & PORT_CONNECT) { status |= USB_PORT_STAT_CONNECTION; /* status may be from integrated TT */ if (ehci->has_hostpc) { temp1 = ehci_readl(ehci, hostpc_reg); status |= ehci_port_speed(ehci, temp1); } else status |= ehci_port_speed(ehci, temp); } if (temp & PORT_PE) status |= USB_PORT_STAT_ENABLE; /* maybe the port was unsuspended without our knowledge */ if (temp & (PORT_SUSPEND|PORT_RESUME)) { status |= USB_PORT_STAT_SUSPEND; } else if (test_bit(wIndex, &ehci->suspended_ports)) { clear_bit(wIndex, &ehci->suspended_ports); ehci->reset_done[wIndex] = 0; if (temp & PORT_PE) set_bit(wIndex, &ehci->port_c_suspend); } if (temp & PORT_OC) status |= USB_PORT_STAT_OVERCURRENT; if (temp & PORT_RESET) status |= USB_PORT_STAT_RESET; if (temp & PORT_POWER) status |= USB_PORT_STAT_POWER; if (test_bit(wIndex, &ehci->port_c_suspend)) status |= USB_PORT_STAT_C_SUSPEND << 16; #ifndef VERBOSE_DEBUG if (status & ~0xffff) /* only if wPortChange is interesting */ #endif dbg_port(ehci, "GetStatus", wIndex + 1, temp); put_unaligned_le32(status, buf); break; default: spin_unlock_irqrestore(&ehci->lock, flags); retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); spin_lock_irqsave(&ehci->lock, flags); } spin_unlock_irqrestore(&ehci->lock, flags); return retval; }