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 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 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; }