static void ehci_hub_descriptor ( struct ehci_hcd *ehci, struct usb_hub_descriptor *desc ) { int ports = HCS_N_PORTS (ehci->hcs_params); u16 temp; desc->bDescriptorType = 0x29; desc->bPwrOn2PwrGood = 0; /* FIXME: f(system power) */ desc->bHubContrCurrent = 0; desc->bNbrPorts = ports; temp = 1 + (ports / 8); desc->bDescLength = 7 + 2 * temp; /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ memset (&desc->bitmap [0], 0, temp); memset (&desc->bitmap [temp], 0xff, temp); temp = 0x0008; /* per-port overcurrent reporting */ if (HCS_PPC (ehci->hcs_params)) temp |= 0x0001; /* per-port power control */ if (HCS_INDICATOR (ehci->hcs_params)) temp |= 0x0080; /* per-port indicators (LEDs) */ desc->wHubCharacteristics = cpu_to_le16 (temp); }
static void ehci_hub_descriptor ( struct ehci_hcd *ehci, struct usb_hub_descriptor *desc ) { int ports = HCS_N_PORTS (ehci->hcs_params); u16 temp; desc->bDescriptorType = 0x29; desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */ desc->bHubContrCurrent = 0; desc->bNbrPorts = ports; temp = 1 + (ports / 8); desc->bDescLength = 7 + 2 * temp; /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ memset (&desc->bitmap [0], 0, temp); memset (&desc->bitmap [temp], 0xff, temp); temp = 0x0008; /* per-port overcurrent reporting */ if (HCS_PPC (ehci->hcs_params)) temp |= 0x0001; /* per-port power control */ else temp |= 0x0002; /* no power switching */ #if 0 // re-enable when we support USB_PORT_FEAT_INDICATOR below. if (HCS_INDICATOR (ehci->hcs_params)) temp |= 0x0080; /* per-port indicators (LEDs) */ #endif desc->wHubCharacteristics = (__force __u16)cpu_to_le16 (temp); }
static void ehci_port_power (struct ehci_hcd *ehci, int is_on) { unsigned port; if (!HCS_PPC (ehci->hcs_params)) return; ehci_dbg (ehci, "...power%s ports...\n", is_on ? "up" : "down"); for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) (void) ehci_hub_control(ehci_to_hcd(ehci), is_on ? SetPortFeature : ClearPortFeature, USB_PORT_FEAT_POWER, port--, NULL, 0); msleep(20); }
static void tegra_ehci_restart (struct usb_hcd *hcd, int state) { unsigned int temp; struct ehci_hcd *ehci = hcd_to_ehci(hcd); /* Set to Host mode by setting bit 0-1 of USB device mode register */ temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET); writel((temp | TEGRA_USB_USBMODE_HOST), (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET)); /* reset the ehci controller */ ehci->controller_resets_phy = 0; ehci_reset(ehci); ehci->controller_resets_phy = 1; /* setup the frame list and Async q heads */ ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next); /* setup the command register and set the controller in RUN mode */ ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); ehci->command |= CMD_RUN; ehci_writel(ehci, ehci->command, &ehci->regs->command); /* Enable the root Port Power */ if (HCS_PPC (ehci->hcs_params)) { int err = 0; temp = ehci_readl(ehci, &ehci->regs->port_status[0]); ehci_writel(ehci, temp | PORT_POWER, &ehci->regs->port_status[0]); /* Flush those writes */ ehci_readl(ehci, &ehci->regs->command); /* wait 20 msec after port power enable, before we enable the interupts*/ msleep(20); err = handshake (ehci, &ehci->regs->port_status[0], PORT_CONNECT, PORT_CONNECT, 20 * 1000); if (err) { printk("port connect detection error: err=%d \n", err); } } down_write(&ehci_cf_port_reset_rwsem); hcd->state = state; /* unblock posted writes */ ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); ehci_readl(ehci, &ehci->regs->command); up_write(&ehci_cf_port_reset_rwsem); /* Turn On Interrupts */ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); }
static void ehci_hsic_port_power(struct ehci_hcd *ehci, int is_on) { unsigned port; if (!HCS_PPC(ehci->hcs_params)) return; dev_dbg(&pci_dev->dev, "...power%s ports...\n", is_on ? "up" : "down"); for (port = HCS_N_PORTS(ehci->hcs_params); port > 0; ) (void) ehci_hub_control(ehci_to_hcd(ehci), is_on ? SetPortFeature : ClearPortFeature, USB_PORT_FEAT_POWER, port--, NULL, 0); /* Flush those writes */ ehci_readl(ehci, &ehci->regs->command); }
static void tegra_ehci_restart (struct usb_hcd *hcd) { unsigned int temp; struct ehci_hcd *ehci = hcd_to_ehci(hcd); /* Set to Host mode by setting bit 0-1 of USB device mode register */ temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET); writel((temp | TEGRA_USB_USBMODE_HOST), (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET)); /* reset the ehci controller */ ehci->controller_resets_phy = 0; ehci_reset(ehci); ehci->controller_resets_phy = 1; /* setup the frame list and Async q heads */ ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next); /* setup the command register and set the controller in RUN mode */ ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); ehci->command |= CMD_RUN; ehci_writel(ehci, ehci->command, &ehci->regs->command); /* Enable the root Port Power */ if (HCS_PPC (ehci->hcs_params)) { temp = ehci_readl(ehci, &ehci->regs->port_status[0]); ehci_writel(ehci, temp | PORT_POWER, &ehci->regs->port_status[0]); } down_write(&ehci_cf_port_reset_rwsem); hcd->state = HC_STATE_RUNNING; /* unblock posted writes */ ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); ehci_readl(ehci, &ehci->regs->command); up_write(&ehci_cf_port_reset_rwsem); /* Turn On Interrupts ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);*/ }
static void tegra_ehci_restart(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); unsigned int temp; ehci->controller_resets_phy = 0; tegra_ehci_pre_reset(tegra->phy, false); ehci_reset(ehci); tegra_ehci_post_reset(tegra->phy, false); if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI) ehci->controller_resets_phy = 1; /* setup the frame list and Async q heads */ ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next); /* setup the command register and set the controller in RUN mode */ ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); ehci->command |= CMD_RUN; ehci_writel(ehci, ehci->command, &ehci->regs->command); /* Enable the root Port Power */ if (HCS_PPC(ehci->hcs_params)) { temp = ehci_readl(ehci, &ehci->regs->port_status[0]); ehci_writel(ehci, temp | PORT_POWER, &ehci->regs->port_status[0]); } down_write(&ehci_cf_port_reset_rwsem); hcd->state = HC_STATE_RUNNING; ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); /* flush posted writes */ ehci_readl(ehci, &ehci->regs->command); up_write(&ehci_cf_port_reset_rwsem); /* Turn On Interrupts */ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); }
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; if (!tegra->host_resumed) { if (buf) memset (buf, 0, wLength); return retval; } 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) & ~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; tegra_usb_phy_postresume(tegra->phy, false); } } 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); 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 */ tegra_usb_phy_preresume(tegra->phy, false); 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); 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 ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) { uint8_t tmpbuf[4]; u16 typeReq; void *srcptr = NULL; int len, srclen; uint32_t reg; uint32_t *status_reg; int port = le16_to_cpu(req->index) & 0xff; struct ehci_ctrl *ctrl = ehci_get_ctrl(dev); srclen = 0; debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n", req->request, req->request, req->requesttype, req->requesttype, le16_to_cpu(req->value), le16_to_cpu(req->index)); typeReq = req->request | req->requesttype << 8; switch (typeReq) { case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): status_reg = ctrl->ops.get_portsc_register(ctrl, port - 1); if (!status_reg) return -1; break; default: status_reg = NULL; break; } switch (typeReq) { case DeviceRequest | USB_REQ_GET_DESCRIPTOR: switch (le16_to_cpu(req->value) >> 8) { case USB_DT_DEVICE: debug("USB_DT_DEVICE request\n"); srcptr = &descriptor.device; srclen = descriptor.device.bLength; break; case USB_DT_CONFIG: debug("USB_DT_CONFIG config\n"); srcptr = &descriptor.config; srclen = descriptor.config.bLength + descriptor.interface.bLength + descriptor.endpoint.bLength; break; case USB_DT_STRING: debug("USB_DT_STRING config\n"); switch (le16_to_cpu(req->value) & 0xff) { case 0: /* Language */ srcptr = "\4\3\1\0"; srclen = 4; break; case 1: /* Vendor */ srcptr = "\16\3u\0-\0b\0o\0o\0t\0"; srclen = 14; break; case 2: /* Product */ srcptr = "\52\3E\0H\0C\0I\0 " "\0H\0o\0s\0t\0 " "\0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0"; srclen = 42; break; default: debug("unknown value DT_STRING %x\n", le16_to_cpu(req->value)); goto unknown; } break; default: debug("unknown value %x\n", le16_to_cpu(req->value)); goto unknown; } break; case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8): switch (le16_to_cpu(req->value) >> 8) { case USB_DT_HUB: debug("USB_DT_HUB config\n"); srcptr = &descriptor.hub; srclen = descriptor.hub.bLength; break; default: debug("unknown value %x\n", le16_to_cpu(req->value)); goto unknown; } break; case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8): debug("USB_REQ_SET_ADDRESS\n"); ctrl->rootdev = le16_to_cpu(req->value); break; case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: debug("USB_REQ_SET_CONFIGURATION\n"); /* Nothing to do */ break; case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8): tmpbuf[0] = 1; /* USB_STATUS_SELFPOWERED */ tmpbuf[1] = 0; srcptr = tmpbuf; srclen = 2; break; case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): memset(tmpbuf, 0, 4); reg = ehci_readl(status_reg); if (reg & EHCI_PS_CS) tmpbuf[0] |= USB_PORT_STAT_CONNECTION; if (reg & EHCI_PS_PE) tmpbuf[0] |= USB_PORT_STAT_ENABLE; if (reg & EHCI_PS_SUSP) tmpbuf[0] |= USB_PORT_STAT_SUSPEND; if (reg & EHCI_PS_OCA) tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; if (reg & EHCI_PS_PR) tmpbuf[0] |= USB_PORT_STAT_RESET; if (reg & EHCI_PS_PP) tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; if (ehci_is_TDI()) { switch (ctrl->ops.get_port_speed(ctrl, reg)) { case PORTSC_PSPD_FS: break; case PORTSC_PSPD_LS: tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8; break; case PORTSC_PSPD_HS: default: tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; break; } } else { tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; } if (reg & EHCI_PS_CSC) tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION; if (reg & EHCI_PS_PEC) tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; if (reg & EHCI_PS_OCC) tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; if (ctrl->portreset & (1 << port)) tmpbuf[2] |= USB_PORT_STAT_C_RESET; srcptr = tmpbuf; srclen = 4; break; case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): reg = ehci_readl(status_reg); reg &= ~EHCI_PS_CLEAR; switch (le16_to_cpu(req->value)) { case USB_PORT_FEAT_ENABLE: reg |= EHCI_PS_PE; ehci_writel(status_reg, reg); break; case USB_PORT_FEAT_POWER: if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams))) { reg |= EHCI_PS_PP; ehci_writel(status_reg, reg); } break; case USB_PORT_FEAT_RESET: if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS && !ehci_is_TDI() && EHCI_PS_IS_LOWSPEED(reg)) { /* Low speed device, give up ownership. */ debug("port %d low speed --> companion\n", port - 1); reg |= EHCI_PS_PO; ehci_writel(status_reg, reg); return -ENXIO; } else { int ret; reg |= EHCI_PS_PR; reg &= ~EHCI_PS_PE; ehci_writel(status_reg, reg); /* * caller must wait, then call GetPortStatus * usb 2.0 specification say 50 ms resets on * root */ ctrl->ops.powerup_fixup(ctrl, status_reg, ®); ehci_writel(status_reg, reg & ~EHCI_PS_PR); /* * A host controller must terminate the reset * and stabilize the state of the port within * 2 milliseconds */ ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000); if (!ret) { reg = ehci_readl(status_reg); if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS && !ehci_is_TDI()) { debug("port %d full speed --> companion\n", port - 1); reg &= ~EHCI_PS_CLEAR; reg |= EHCI_PS_PO; ehci_writel(status_reg, reg); return -ENXIO; } else { ctrl->portreset |= 1 << port; } } else { printf("port(%d) reset error\n", port - 1); } } break; case USB_PORT_FEAT_TEST: ehci_shutdown(ctrl); reg &= ~(0xf << 16); reg |= ((le16_to_cpu(req->index) >> 8) & 0xf) << 16; ehci_writel(status_reg, reg); break; default: debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } /* unblock posted writes */ (void) ehci_readl(&ctrl->hcor->or_usbcmd); break; case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): reg = ehci_readl(status_reg); reg &= ~EHCI_PS_CLEAR; switch (le16_to_cpu(req->value)) { case USB_PORT_FEAT_ENABLE: reg &= ~EHCI_PS_PE; break; case USB_PORT_FEAT_C_ENABLE: reg |= EHCI_PS_PE; break; case USB_PORT_FEAT_POWER: if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams))) reg &= ~EHCI_PS_PP; break; case USB_PORT_FEAT_C_CONNECTION: reg |= EHCI_PS_CSC; break; case USB_PORT_FEAT_OVER_CURRENT: reg |= EHCI_PS_OCC; break; case USB_PORT_FEAT_C_RESET: ctrl->portreset &= ~(1 << port); break; default: debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } ehci_writel(status_reg, reg); /* unblock posted write */ (void) ehci_readl(&ctrl->hcor->or_usbcmd); break; default: debug("Unknown request\n"); goto unknown; }
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 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); // FIXME: for rtl8652 using... if you don't using rtl8652 EHCI driver, please enable this function. // 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 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 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); /* Seshendra patch: 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; } sp_pr_info("%s USB_PORT_FEAT_SUSPEND\n", __func__); tegra_usb_phy_presuspend(tegra->phy, false); sp_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); sp_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; } //dump_stack(); /* 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; }
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; }