diff options
Diffstat (limited to 'drivers/usb/host/ehci-hub.c')
| -rw-r--r-- | drivers/usb/host/ehci-hub.c | 794 |
1 files changed, 550 insertions, 244 deletions
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 2c6571c05f3..cc305c71ac3 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -27,19 +27,17 @@ */ /*-------------------------------------------------------------------------*/ +#include <linux/usb/otg.h> #define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) #ifdef CONFIG_PM -static int ehci_hub_control( - struct usb_hcd *hcd, - u16 typeReq, - u16 wValue, - u16 wIndex, - char *buf, - u16 wLength -); +static int persist_enabled_on_companion(struct usb_device *udev, void *unused) +{ + return !udev->maxchild && udev->persist_enabled && + udev->bus->root_hub->speed < USB_SPEED_HIGH; +} /* After a power loss, ports that were owned by the companion must be * reset so that the companion can still own them. @@ -55,9 +53,33 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) if (!ehci->owned_ports) return; + /* + * USB 1.1 devices are mostly HIDs, which don't need to persist across + * suspends. If we ensure that none of our companion's devices have + * persist_enabled (by looking through all USB 1.1 buses in the system), + * we can skip this and avoid slowing resume down. Devices without + * persist will just get reenumerated shortly after resume anyway. + */ + if (!usb_for_each_dev(NULL, persist_enabled_on_companion)) + return; + + /* Make sure the ports are powered */ + port = HCS_N_PORTS(ehci->hcs_params); + while (port--) { + if (test_bit(port, &ehci->owned_ports)) { + reg = &ehci->regs->port_status[port]; + status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; + if (!(status & PORT_POWER)) { + status |= PORT_POWER; + ehci_writel(ehci, status, reg); + } + } + } + /* Give the connections some time to appear */ msleep(20); + spin_lock_irq(&ehci->lock); port = HCS_N_PORTS(ehci->hcs_params); while (port--) { if (test_bit(port, &ehci->owned_ports)) { @@ -69,23 +91,30 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) clear_bit(port, &ehci->owned_ports); else if (test_bit(port, &ehci->companion_ports)) ehci_writel(ehci, status & ~PORT_PE, reg); - else + else { + spin_unlock_irq(&ehci->lock); ehci_hub_control(hcd, SetPortFeature, USB_PORT_FEAT_RESET, port + 1, NULL, 0); + spin_lock_irq(&ehci->lock); + } } } + spin_unlock_irq(&ehci->lock); if (!ehci->owned_ports) return; msleep(90); /* Wait for resets to complete */ + spin_lock_irq(&ehci->lock); port = HCS_N_PORTS(ehci->hcs_params); while (port--) { if (test_bit(port, &ehci->owned_ports)) { + spin_unlock_irq(&ehci->lock); ehci_hub_control(hcd, GetPortStatus, 0, port + 1, (char *) &buf, sizeof(buf)); + spin_lock_irq(&ehci->lock); /* The companion should now own the port, * but if something went wrong the port must not @@ -104,6 +133,94 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) } ehci->owned_ports = 0; + spin_unlock_irq(&ehci->lock); +} + +static int ehci_port_change(struct ehci_hcd *ehci) +{ + int i = HCS_N_PORTS(ehci->hcs_params); + + /* First check if the controller indicates a change event */ + + if (ehci_readl(ehci, &ehci->regs->status) & STS_PCD) + return 1; + + /* + * Not all controllers appear to update this while going from D3 to D0, + * so check the individual port status registers as well + */ + + while (i--) + if (ehci_readl(ehci, &ehci->regs->port_status[i]) & PORT_CSC) + return 1; + + return 0; +} + +static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, + bool suspending, bool do_wakeup) +{ + int port; + u32 temp; + + /* If remote wakeup is enabled for the root hub but disabled + * for the controller, we must adjust all the port wakeup flags + * when the controller is suspended or resumed. In all other + * cases they don't need to be changed. + */ + if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup) + return; + + spin_lock_irq(&ehci->lock); + + /* clear phy low-power mode before changing wakeup flags */ + if (ehci->has_tdi_phy_lpm) { + port = HCS_N_PORTS(ehci->hcs_params); + while (port--) { + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; + + temp = ehci_readl(ehci, hostpc_reg); + ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg); + } + spin_unlock_irq(&ehci->lock); + msleep(5); + spin_lock_irq(&ehci->lock); + } + + port = HCS_N_PORTS(ehci->hcs_params); + while (port--) { + u32 __iomem *reg = &ehci->regs->port_status[port]; + u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; + u32 t2 = t1 & ~PORT_WAKE_BITS; + + /* If we are suspending the controller, clear the flags. + * If we are resuming the controller, set the wakeup flags. + */ + if (!suspending) { + if (t1 & PORT_CONNECT) + t2 |= PORT_WKOC_E | PORT_WKDISC_E; + else + t2 |= PORT_WKOC_E | PORT_WKCONN_E; + } + ehci_writel(ehci, t2, reg); + } + + /* enter phy low-power mode again */ + if (ehci->has_tdi_phy_lpm) { + port = HCS_N_PORTS(ehci->hcs_params); + while (port--) { + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; + + temp = ehci_readl(ehci, hostpc_reg); + ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg); + } + } + + /* Does the root hub have a port wakeup pending? */ + if (!suspending && ehci_port_change(ehci)) + usb_hcd_resume_root_hub(ehci_to_hcd(ehci)); + + spin_unlock_irq(&ehci->lock); } static int ehci_bus_suspend (struct usb_hcd *hcd) @@ -111,25 +228,33 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci (hcd); int port; int mask; - u32 __iomem *hostpc_reg = NULL; + int changed; + bool fs_idle_delay; ehci_dbg(ehci, "suspend root hub\n"); if (time_before (jiffies, ehci->next_statechange)) msleep(5); - del_timer_sync(&ehci->watchdog); - del_timer_sync(&ehci->iaa_watchdog); - port = HCS_N_PORTS (ehci->hcs_params); + /* stop the schedules */ + ehci_quiesce(ehci); + spin_lock_irq (&ehci->lock); + if (ehci->rh_state < EHCI_RH_RUNNING) + goto done; - /* stop schedules, clean any completed work */ - if (HC_IS_RUNNING(hcd->state)) { - ehci_quiesce (ehci); - hcd->state = HC_STATE_QUIESCING; + /* Once the controller is stopped, port resumes that are already + * in progress won't complete. Hence if remote wakeup is enabled + * for the root hub and any ports are in the middle of a resume or + * remote wakeup, we must fail the suspend. + */ + if (hcd->self.root_hub->do_remote_wakeup) { + if (ehci->resuming_ports) { + spin_unlock_irq(&ehci->lock); + ehci_dbg(ehci, "suspend failed because a port is resuming\n"); + return -EBUSY; + } } - ehci->command = ehci_readl(ehci, &ehci->regs->command); - ehci_work(ehci); /* Unlike other USB host controller types, EHCI doesn't have * any notion of "global" or bus-wide suspend. The driver has @@ -138,14 +263,14 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) */ ehci->bus_suspended = 0; ehci->owned_ports = 0; + changed = 0; + fs_idle_delay = false; + port = HCS_N_PORTS(ehci->hcs_params); while (port--) { u32 __iomem *reg = &ehci->regs->port_status [port]; u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; - u32 t2 = t1; + u32 t2 = t1 & ~PORT_WAKE_BITS; - if (ehci->has_hostpc) - hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs - + HOSTPC0 + 4 * (port & 0xff)); /* keep track of which ports we suspend */ if (t1 & PORT_OWNER) set_bit(port, &ehci->owned_ports); @@ -154,39 +279,59 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) set_bit(port, &ehci->bus_suspended); } - /* enable remote wakeup on all ports */ + /* enable remote wakeup on all ports, if told to do so */ if (hcd->self.root_hub->do_remote_wakeup) { /* only enable appropriate wake bits, otherwise the * hardware can not go phy low power mode. If a race * condition happens here(connection change during bits * set), the port change detection will finally fix it. */ - if (t1 & PORT_CONNECT) { + if (t1 & PORT_CONNECT) t2 |= PORT_WKOC_E | PORT_WKDISC_E; - t2 &= ~PORT_WKCONN_E; - } else { + else t2 |= PORT_WKOC_E | PORT_WKCONN_E; - t2 &= ~PORT_WKDISC_E; - } - } else - t2 &= ~PORT_WAKE_BITS; + } if (t1 != t2) { - ehci_vdbg (ehci, "port %d, %08x -> %08x\n", - port + 1, t1, t2); + /* + * On some controllers, Wake-On-Disconnect will + * generate false wakeup signals until the bus + * switches over to full-speed idle. For their + * sake, add a delay if we need one. + */ + if ((t2 & PORT_WKDISC_E) && + ehci_port_speed(ehci, t2) == + USB_PORT_STAT_HIGH_SPEED) + fs_idle_delay = true; ehci_writel(ehci, t2, reg); - if (hostpc_reg) { - u32 t3; + changed = 1; + } + } + spin_unlock_irq(&ehci->lock); - msleep(5);/* 5ms for HCD enter low pwr mode */ - t3 = ehci_readl(ehci, hostpc_reg); - ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg); - t3 = ehci_readl(ehci, hostpc_reg); - ehci_dbg(ehci, "Port%d phy low pwr mode %s\n", + if ((changed && ehci->has_tdi_phy_lpm) || fs_idle_delay) { + /* + * Wait for HCD to enter low-power mode or for the bus + * to switch to full-speed idle. + */ + usleep_range(5000, 5500); + } + + if (changed && ehci->has_tdi_phy_lpm) { + spin_lock_irq(&ehci->lock); + port = HCS_N_PORTS(ehci->hcs_params); + while (port--) { + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; + u32 t3; + + t3 = ehci_readl(ehci, hostpc_reg); + ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg); + t3 = ehci_readl(ehci, hostpc_reg); + ehci_dbg(ehci, "Port %d phy low-power mode %s\n", port, (t3 & HOSTPC_PHCD) ? "succeeded" : "failed"); - } } + spin_unlock_irq(&ehci->lock); } /* Apparently some devices need a >= 1-uframe delay here */ @@ -195,10 +340,19 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) /* turn off now-idle HC */ ehci_halt (ehci); - hcd->state = HC_STATE_SUSPENDED; - if (ehci->reclaim) - end_unlink_async(ehci); + spin_lock_irq(&ehci->lock); + if (ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_POLL_DEAD)) + ehci_handle_controller_death(ehci); + if (ehci->rh_state != EHCI_RH_RUNNING) + goto done; + ehci->rh_state = EHCI_RH_SUSPENDED; + + end_unlink_async(ehci); + unlink_empty_async_suspended(ehci); + ehci_handle_start_intr_unlinks(ehci); + ehci_handle_intr_unlinks(ehci); + end_free_itds(ehci); /* allow remote wakeup */ mask = INTR_MASK; @@ -207,13 +361,13 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci_writel(ehci, mask, &ehci->regs->intr_enable); ehci_readl(ehci, &ehci->regs->intr_enable); + done: ehci->next_statechange = jiffies + msecs_to_jiffies(10); + ehci->enabled_hrtimer_events = 0; + ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT; spin_unlock_irq (&ehci->lock); - /* ehci_work() may have re-enabled the watchdog timer, which we do not - * want, and so we must delete any pending watchdog timer events. - */ - del_timer_sync(&ehci->watchdog); + hrtimer_cancel(&ehci->hrtimer); return 0; } @@ -225,21 +379,19 @@ static int ehci_bus_resume (struct usb_hcd *hcd) u32 temp; u32 power_okay; int i; - u8 resume_needed = 0; + unsigned long resume_needed = 0; if (time_before (jiffies, ehci->next_statechange)) msleep(5); spin_lock_irq (&ehci->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { - spin_unlock_irq(&ehci->lock); - return -ESHUTDOWN; - } + if (!HCD_HW_ACCESSIBLE(hcd) || ehci->shutdown) + goto shutdown; if (unlikely(ehci->debug)) { - if (!dbgp_reset_prep()) + if (!dbgp_reset_prep(hcd)) ehci->debug = NULL; else - dbgp_external_startup(); + dbgp_external_startup(hcd); } /* Ideally and we've got a real resume here, and no port's power @@ -263,13 +415,50 @@ static int ehci_bus_resume (struct usb_hcd *hcd) ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next); /* restore CMD_RUN, framelist size, and irq threshold */ + ehci->command |= CMD_RUN; ehci_writel(ehci, ehci->command, &ehci->regs->command); + ehci->rh_state = EHCI_RH_RUNNING; - /* Some controller/firmware combinations need a delay during which - * they set up the port statuses. See Bugzilla #8190. */ - spin_unlock_irq(&ehci->lock); - msleep(8); - spin_lock_irq(&ehci->lock); + /* + * According to Bugzilla #8190, the port status for some controllers + * will be wrong without a delay. At their wrong status, the port + * is enabled, but not suspended neither resumed. + */ + i = HCS_N_PORTS(ehci->hcs_params); + while (i--) { + temp = ehci_readl(ehci, &ehci->regs->port_status[i]); + if ((temp & PORT_PE) && + !(temp & (PORT_SUSPEND | PORT_RESUME))) { + ehci_dbg(ehci, "Port status(0x%x) is wrong\n", temp); + spin_unlock_irq(&ehci->lock); + msleep(8); + spin_lock_irq(&ehci->lock); + break; + } + } + + if (ehci->shutdown) + goto shutdown; + + /* clear phy low-power mode before resume */ + if (ehci->bus_suspended && ehci->has_tdi_phy_lpm) { + i = HCS_N_PORTS(ehci->hcs_params); + while (i--) { + if (test_bit(i, &ehci->bus_suspended)) { + u32 __iomem *hostpc_reg = + &ehci->regs->hostpc[i]; + + temp = ehci_readl(ehci, hostpc_reg); + ehci_writel(ehci, temp & ~HOSTPC_PHCD, + hostpc_reg); + } + } + spin_unlock_irq(&ehci->lock); + msleep(5); + spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto shutdown; + } /* manually resume the ports we suspended during bus_suspend() */ i = HCS_N_PORTS (ehci->hcs_params); @@ -279,7 +468,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) if (test_bit(i, &ehci->bus_suspended) && (temp & PORT_SUSPEND)) { temp |= PORT_RESUME; - resume_needed = 1; + set_bit(i, &resume_needed); } ehci_writel(ehci, temp, &ehci->regs->port_status [i]); } @@ -289,40 +478,37 @@ static int ehci_bus_resume (struct usb_hcd *hcd) spin_unlock_irq(&ehci->lock); msleep(20); spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto shutdown; } i = HCS_N_PORTS (ehci->hcs_params); while (i--) { temp = ehci_readl(ehci, &ehci->regs->port_status [i]); - if (test_bit(i, &ehci->bus_suspended) && - (temp & PORT_SUSPEND)) { - temp &= ~(PORT_RWC_BITS | PORT_RESUME); + if (test_bit(i, &resume_needed)) { + temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME); ehci_writel(ehci, temp, &ehci->regs->port_status [i]); - ehci_vdbg (ehci, "resumed port %d\n", i + 1); } } - (void) ehci_readl(ehci, &ehci->regs->command); - - /* maybe re-activate the schedule(s) */ - temp = 0; - if (ehci->async->qh_next.qh) - temp |= CMD_ASE; - if (ehci->periodic_sched) - temp |= CMD_PSE; - if (temp) { - ehci->command |= temp; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - } ehci->next_statechange = jiffies + msecs_to_jiffies(5); - hcd->state = HC_STATE_RUNNING; + spin_unlock_irq(&ehci->lock); + + ehci_handover_companion_ports(ehci); /* Now we can safely re-enable irqs */ + spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto shutdown; ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); + (void) ehci_readl(ehci, &ehci->regs->intr_enable); + spin_unlock_irq(&ehci->lock); - spin_unlock_irq (&ehci->lock); - ehci_handover_companion_ports(ehci); return 0; + + shutdown: + spin_unlock_irq(&ehci->lock); + return -ESHUTDOWN; } #else @@ -334,29 +520,6 @@ static int ehci_bus_resume (struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ -/* Display the ports dedicated to the companion controller */ -static ssize_t show_companion(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ehci_hcd *ehci; - int nports, index, n; - int count = PAGE_SIZE; - char *ptr = buf; - - ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); - nports = HCS_N_PORTS(ehci->hcs_params); - - for (index = 0; index < nports; ++index) { - if (test_bit(index, &ehci->companion_ports)) { - n = scnprintf(ptr, count, "%d\n", index + 1); - ptr += n; - count -= n; - } - } - return ptr - buf; -} - /* * Sets the owner of a port */ @@ -391,57 +554,6 @@ static void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner) } } -/* - * Dedicate or undedicate a port to the companion controller. - * Syntax is "[-]portnum", where a leading '-' sign means - * return control of the port to the EHCI controller. - */ -static ssize_t store_companion(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ehci_hcd *ehci; - int portnum, new_owner; - - ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); - new_owner = PORT_OWNER; /* Owned by companion */ - if (sscanf(buf, "%d", &portnum) != 1) - return -EINVAL; - if (portnum < 0) { - portnum = - portnum; - new_owner = 0; /* Owned by EHCI */ - } - if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params)) - return -ENOENT; - portnum--; - if (new_owner) - set_bit(portnum, &ehci->companion_ports); - else - clear_bit(portnum, &ehci->companion_ports); - set_owner(ehci, portnum, new_owner); - return count; -} -static DEVICE_ATTR(companion, 0644, show_companion, store_companion); - -static inline void create_companion_file(struct ehci_hcd *ehci) -{ - int i; - - /* with integrated TT there is no companion! */ - if (!ehci_is_TDI(ehci)) - i = device_create_file(ehci_to_hcd(ehci)->self.controller, - &dev_attr_companion); -} - -static inline void remove_companion_file(struct ehci_hcd *ehci) -{ - /* with integrated TT there is no companion! */ - if (!ehci_is_TDI(ehci)) - device_remove_file(ehci_to_hcd(ehci)->self.controller, - &dev_attr_companion); -} - - /*-------------------------------------------------------------------------*/ static int check_reset_complete ( @@ -476,7 +588,8 @@ static int check_reset_complete ( if (ehci->has_amcc_usb23) set_ohci_hcfs(ehci, 1); } else { - ehci_dbg (ehci, "port %d high speed\n", index + 1); + ehci_dbg(ehci, "port %d reset complete, port enabled\n", + index + 1); /* ensure 440EPx ohci controller state is suspended */ if (ehci->has_amcc_usb23) set_ohci_hcfs(ehci, 0); @@ -494,14 +607,11 @@ static int ehci_hub_status_data (struct usb_hcd *hcd, char *buf) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 temp, status = 0; + u32 temp, status; u32 mask; int ports, i, retval = 1; unsigned long flags; - - /* if !USB_SUSPEND, root hub timers won't get shut down ... */ - if (!HC_IS_RUNNING(hcd->state)) - return 0; + u32 ppcd = ~0; /* init status to no-changes */ buf [0] = 0; @@ -511,6 +621,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) retval++; } + /* Inform the core about resumes-in-progress by returning + * a non-zero value even if there are no status changes. + */ + status = ehci->resuming_ports; + /* Some boards (mostly VIA?) report bogus overcurrent indications, * causing massive log spam unless we completely ignore them. It * may be relevant that VIA VT8235 controllers, where PORT_POWER is @@ -527,8 +642,17 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) /* port N changes (bit N)? */ spin_lock_irqsave (&ehci->lock, flags); + + /* get per-port change detect bits */ + if (ehci->has_ppcd) + ppcd = ehci_readl(ehci, &ehci->regs->status) >> 16; + for (i = 0; i < ports; i++) { - temp = ehci_readl(ehci, &ehci->regs->port_status [i]); + /* leverage per-port change bits feature */ + if (ppcd & (1 << i)) + temp = ehci_readl(ehci, &ehci->regs->port_status[i]); + else + temp = 0; /* * Return status information even for ports with OWNER set. @@ -547,7 +671,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) status = STS_PCD; } } - /* FIXME autosuspend idle root hubs */ + + /* If a resume is in progress, make sure it can finish */ + if (ehci->resuming_ports) + mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(25)); + spin_unlock_irqrestore (&ehci->lock, flags); return status ? retval : 0; } @@ -571,8 +699,8 @@ ehci_hub_descriptor ( 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); + memset(&desc->u.hs.DeviceRemovable[0], 0, temp); + memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); temp = 0x0008; /* per-port overcurrent reporting */ if (HCS_PPC (ehci->hcs_params)) @@ -588,8 +716,147 @@ ehci_hub_descriptor ( } /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_HCD_TEST_MODE + +#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06 + +static void usb_ehset_completion(struct urb *urb) +{ + struct completion *done = urb->context; + + complete(done); +} +static int submit_single_step_set_feature( + struct usb_hcd *hcd, + struct urb *urb, + int is_setup +); + +/* + * Allocate and initialize a control URB. This request will be used by the + * EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages + * of the GetDescriptor request are sent 15 seconds after the SETUP stage. + * Return NULL if failed. + */ +static struct urb *request_single_step_set_feature_urb( + struct usb_device *udev, + void *dr, + void *buf, + struct completion *done +) { + struct urb *urb; + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + struct usb_host_endpoint *ep; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return NULL; + + urb->pipe = usb_rcvctrlpipe(udev, 0); + ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out) + [usb_pipeendpoint(urb->pipe)]; + if (!ep) { + usb_free_urb(urb); + return NULL; + } + + urb->ep = ep; + urb->dev = udev; + urb->setup_packet = (void *)dr; + urb->transfer_buffer = buf; + urb->transfer_buffer_length = USB_DT_DEVICE_SIZE; + urb->complete = usb_ehset_completion; + urb->status = -EINPROGRESS; + urb->actual_length = 0; + urb->transfer_flags = URB_DIR_IN; + usb_get_urb(urb); + atomic_inc(&urb->use_count); + atomic_inc(&urb->dev->urbnum); + urb->setup_dma = dma_map_single( + hcd->self.controller, + urb->setup_packet, + sizeof(struct usb_ctrlrequest), + DMA_TO_DEVICE); + urb->transfer_dma = dma_map_single( + hcd->self.controller, + urb->transfer_buffer, + urb->transfer_buffer_length, + DMA_FROM_DEVICE); + urb->context = done; + return urb; +} + +static int ehset_single_step_set_feature(struct usb_hcd *hcd, int port) +{ + int retval = -ENOMEM; + struct usb_ctrlrequest *dr; + struct urb *urb; + struct usb_device *udev; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct usb_device_descriptor *buf; + DECLARE_COMPLETION_ONSTACK(done); + + /* Obtain udev of the rhub's child port */ + udev = usb_hub_find_child(hcd->self.root_hub, port); + if (!udev) { + ehci_err(ehci, "No device attached to the RootHub\n"); + return -ENODEV; + } + buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + if (!dr) { + kfree(buf); + return -ENOMEM; + } + + /* Fill Setup packet for GetDescriptor */ + dr->bRequestType = USB_DIR_IN; + dr->bRequest = USB_REQ_GET_DESCRIPTOR; + dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8); + dr->wIndex = 0; + dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE); + urb = request_single_step_set_feature_urb(udev, dr, buf, &done); + if (!urb) + goto cleanup; + + /* Submit just the SETUP stage */ + retval = submit_single_step_set_feature(hcd, urb, 1); + if (retval) + goto out1; + if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) { + usb_kill_urb(urb); + retval = -ETIMEDOUT; + ehci_err(ehci, "%s SETUP stage timed out on ep0\n", __func__); + goto out1; + } + msleep(15 * 1000); + + /* Complete remaining DATA and STATUS stages using the same URB */ + urb->status = -EINPROGRESS; + usb_get_urb(urb); + atomic_inc(&urb->use_count); + atomic_inc(&urb->dev->urbnum); + retval = submit_single_step_set_feature(hcd, urb, 0); + if (!retval && !wait_for_completion_timeout(&done, + msecs_to_jiffies(2000))) { + usb_kill_urb(urb); + retval = -ETIMEDOUT; + ehci_err(ehci, "%s IN stage timed out on ep0\n", __func__); + } +out1: + usb_free_urb(urb); +cleanup: + kfree(dr); + kfree(buf); + return retval; +} +#endif /* CONFIG_USB_HCD_TEST_MODE */ +/*-------------------------------------------------------------------------*/ -static int ehci_hub_control ( +int ehci_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, @@ -601,7 +868,7 @@ static int ehci_hub_control ( int ports = HCS_N_PORTS (ehci->hcs_params); u32 __iomem *status_reg = &ehci->regs->port_status[ (wIndex & 0xff) - 1]; - u32 __iomem *hostpc_reg = NULL; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[(wIndex & 0xff) - 1]; u32 temp, temp1, status; unsigned long flags; int retval = 0; @@ -614,9 +881,6 @@ static int ehci_hub_control ( * power, "this is the one", etc. EHCI spec supports this. */ - if (ehci->has_hostpc) - hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs - + HOSTPC0 + 4 * ((wIndex & 0xff) - 1)); spin_lock_irqsave (&ehci->lock, flags); switch (typeReq) { case ClearHubFeature: @@ -634,12 +898,13 @@ static int ehci_hub_control ( goto error; wIndex--; temp = ehci_readl(ehci, status_reg); + temp &= ~PORT_RWC_BITS; /* * Even if OWNER is set, so the port is owned by the * companion controller, khubd needs to be able to clear * the port-change status bits (especially - * USB_PORT_FEAT_C_CONNECTION). + * USB_PORT_STAT_C_CONNECTION). */ switch (wValue) { @@ -647,41 +912,55 @@ static int ehci_hub_control ( ehci_writel(ehci, temp & ~PORT_PE, status_reg); break; case USB_PORT_FEAT_C_ENABLE: - ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_PEC, - status_reg); + ehci_writel(ehci, temp | PORT_PEC, status_reg); break; case USB_PORT_FEAT_SUSPEND: if (temp & PORT_RESET) goto error; if (ehci->no_selective_suspend) break; - if (temp & PORT_SUSPEND) { - if ((temp & PORT_PE) == 0) - goto error; - /* resume signaling for 20 msec */ - temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); - ehci_writel(ehci, temp | PORT_RESUME, - status_reg); - ehci->reset_done [wIndex] = jiffies - + msecs_to_jiffies (20); +#ifdef CONFIG_USB_OTG + if ((hcd->self.otg_port == (wIndex + 1)) + && hcd->self.b_hnp_enable) { + otg_start_hnp(hcd->phy->otg); + break; + } +#endif + if (!(temp & PORT_SUSPEND)) + break; + if ((temp & PORT_PE) == 0) + goto error; + + /* clear phy low-power mode before resume */ + if (ehci->has_tdi_phy_lpm) { + temp1 = ehci_readl(ehci, hostpc_reg); + ehci_writel(ehci, temp1 & ~HOSTPC_PHCD, + hostpc_reg); + spin_unlock_irqrestore(&ehci->lock, flags); + msleep(5);/* wait to leave low-power mode */ + spin_lock_irqsave(&ehci->lock, flags); } + /* resume signaling for 20 msec */ + temp &= ~PORT_WAKE_BITS; + ehci_writel(ehci, temp | PORT_RESUME, status_reg); + ehci->reset_done[wIndex] = jiffies + + msecs_to_jiffies(20); + set_bit(wIndex, &ehci->resuming_ports); + usb_hcd_start_port_resume(&hcd->self, wIndex); break; case USB_PORT_FEAT_C_SUSPEND: clear_bit(wIndex, &ehci->port_c_suspend); break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) - ehci_writel(ehci, - temp & ~(PORT_RWC_BITS | PORT_POWER), - status_reg); + ehci_writel(ehci, temp & ~PORT_POWER, + status_reg); break; case USB_PORT_FEAT_C_CONNECTION: - ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_CSC, - status_reg); + ehci_writel(ehci, temp | PORT_CSC, status_reg); break; case USB_PORT_FEAT_C_OVER_CURRENT: - ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_OCC, - status_reg); + ehci_writel(ehci, temp | PORT_OCC, status_reg); break; case USB_PORT_FEAT_C_RESET: /* GetPortStatus clears reset */ @@ -709,12 +988,12 @@ static int ehci_hub_control ( // wPortChange bits if (temp & PORT_CSC) - status |= 1 << USB_PORT_FEAT_C_CONNECTION; + status |= USB_PORT_STAT_C_CONNECTION << 16; if (temp & PORT_PEC) - status |= 1 << USB_PORT_FEAT_C_ENABLE; + status |= USB_PORT_STAT_C_ENABLE << 16; if ((temp & PORT_OCC) && !ignore_oc){ - status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; + status |= USB_PORT_STAT_C_OVERCURRENT << 16; /* * Hubs should disable port power on over-current. @@ -723,55 +1002,57 @@ static int ehci_hub_control ( * power switching; they're allowed to just limit the * current. khubd will turn the power back on. */ - if (HCS_PPC (ehci->hcs_params)){ + if (((temp & PORT_OC) || (ehci->need_oc_pp_cycle)) + && HCS_PPC(ehci->hcs_params)) { ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_POWER), status_reg); + temp = ehci_readl(ehci, status_reg); } } - /* whoever resumes must GetPortStatus to complete it!! */ - if (temp & PORT_RESUME) { + /* no reset or resume pending */ + if (!ehci->reset_done[wIndex]) { /* Remote Wakeup received? */ - if (!ehci->reset_done[wIndex]) { + if (temp & PORT_RESUME) { /* resume signaling for 20 msec */ ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(20); + usb_hcd_start_port_resume(&hcd->self, wIndex); + set_bit(wIndex, &ehci->resuming_ports); /* 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; + /* reset or resume not yet complete */ + } else if (!time_after_eq(jiffies, ehci->reset_done[wIndex])) { + ; /* wait until it is complete */ - /* stop resume signaling */ - temp = ehci_readl(ehci, status_reg); - ehci_writel(ehci, - temp & ~(PORT_RWC_BITS | PORT_RESUME), - status_reg); - retval = handshake(ehci, status_reg, - PORT_RESUME, 0, 2000 /* 2msec */); - if (retval != 0) { - ehci_err(ehci, - "port %d resume error %d\n", + /* resume completed */ + } else if (test_bit(wIndex, &ehci->resuming_ports)) { + clear_bit(wIndex, &ehci->suspended_ports); + set_bit(wIndex, &ehci->port_c_suspend); + ehci->reset_done[wIndex] = 0; + usb_hcd_end_port_resume(&hcd->self, wIndex); + + /* stop resume signaling */ + temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME); + ehci_writel(ehci, temp, status_reg); + clear_bit(wIndex, &ehci->resuming_ports); + retval = ehci_handshake(ehci, status_reg, + 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)); + goto error; } - } + temp = ehci_readl(ehci, status_reg); /* whoever resets must GetPortStatus to complete it!! */ - if ((temp & PORT_RESET) - && time_after_eq(jiffies, - ehci->reset_done[wIndex])) { - status |= 1 << USB_PORT_FEAT_C_RESET; + } else { + status |= USB_PORT_STAT_C_RESET << 16; ehci->reset_done [wIndex] = 0; /* force reset to complete */ @@ -780,8 +1061,8 @@ static int ehci_hub_control ( /* REVISIT: some hardware needs 550+ usec to clear * this bit; seems too long to spin routinely... */ - retval = handshake(ehci, status_reg, - PORT_RESET, 0, 750); + retval = ehci_handshake(ehci, status_reg, + PORT_RESET, 0, 1000); if (retval != 0) { ehci_err (ehci, "port %d reset error %d\n", wIndex + 1, retval); @@ -793,9 +1074,6 @@ static int ehci_hub_control ( 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)) { @@ -813,7 +1091,7 @@ static int ehci_hub_control ( */ if (temp & PORT_CONNECT) { - status |= 1 << USB_PORT_FEAT_CONNECTION; + status |= USB_PORT_STAT_CONNECTION; // status may be from integrated TT if (ehci->has_hostpc) { temp1 = ehci_readl(ehci, hostpc_reg); @@ -822,31 +1100,31 @@ static int ehci_hub_control ( status |= ehci_port_speed(ehci, temp); } if (temp & PORT_PE) - status |= 1 << USB_PORT_FEAT_ENABLE; + status |= USB_PORT_STAT_ENABLE; /* maybe the port was unsuspended without our knowledge */ if (temp & (PORT_SUSPEND|PORT_RESUME)) { - status |= 1 << USB_PORT_FEAT_SUSPEND; + status |= USB_PORT_STAT_SUSPEND; } else if (test_bit(wIndex, &ehci->suspended_ports)) { clear_bit(wIndex, &ehci->suspended_ports); + clear_bit(wIndex, &ehci->resuming_ports); ehci->reset_done[wIndex] = 0; if (temp & PORT_PE) set_bit(wIndex, &ehci->port_c_suspend); + usb_hcd_end_port_resume(&hcd->self, wIndex); } if (temp & PORT_OC) - status |= 1 << USB_PORT_FEAT_OVER_CURRENT; + status |= USB_PORT_STAT_OVERCURRENT; if (temp & PORT_RESET) - status |= 1 << USB_PORT_FEAT_RESET; + status |= USB_PORT_STAT_RESET; if (temp & PORT_POWER) - status |= 1 << USB_PORT_FEAT_POWER; + status |= USB_PORT_STAT_POWER; if (test_bit(wIndex, &ehci->port_c_suspend)) - status |= 1 << USB_PORT_FEAT_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); + if (status & ~0xffff) /* only if wPortChange is interesting */ + dbg_port(ehci, "GetStatus", wIndex + 1, temp); put_unaligned_le32(status, buf); break; case SetHubFeature: @@ -886,17 +1164,18 @@ static int ehci_hub_control ( if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) goto error; - ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); + /* After above check the port must be connected. * Set appropriate bit thus could put phy into low power - * mode if we have hostpc feature + * mode if we have tdi_phy_lpm feature */ - if (hostpc_reg) { - temp &= ~PORT_WKCONN_E; - temp |= (PORT_WKDISC_E | PORT_WKOC_E); - ehci_writel(ehci, temp | PORT_SUSPEND, - status_reg); + temp &= ~PORT_WKCONN_E; + temp |= PORT_WKDISC_E | PORT_WKOC_E; + ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); + if (ehci->has_tdi_phy_lpm) { + spin_unlock_irqrestore(&ehci->lock, flags); msleep(5);/* 5ms for HCD enter low pwr mode */ + spin_lock_irqsave(&ehci->lock, flags); temp1 = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp1 | HOSTPC_PHCD, hostpc_reg); @@ -913,7 +1192,7 @@ static int ehci_hub_control ( status_reg); break; case USB_PORT_FEAT_RESET: - if (temp & PORT_RESUME) + if (temp & (PORT_SUSPEND|PORT_RESUME)) goto error; /* line status bits may report this as low speed, * which can be fine if this root hub has a @@ -927,7 +1206,6 @@ static int ehci_hub_control ( wIndex + 1); temp |= PORT_OWNER; } else { - ehci_vdbg (ehci, "port %d reset\n", wIndex + 1); temp |= PORT_RESET; temp &= ~PORT_PE; @@ -948,10 +1226,37 @@ static int ehci_hub_control ( * about the EHCI-specific stuff. */ case USB_PORT_FEAT_TEST: +#ifdef CONFIG_USB_HCD_TEST_MODE + if (selector == EHSET_TEST_SINGLE_STEP_SET_FEATURE) { + spin_unlock_irqrestore(&ehci->lock, flags); + retval = ehset_single_step_set_feature(hcd, + wIndex); + spin_lock_irqsave(&ehci->lock, flags); + break; + } +#endif if (!selector || selector > 5) goto error; + spin_unlock_irqrestore(&ehci->lock, flags); ehci_quiesce(ehci); + spin_lock_irqsave(&ehci->lock, flags); + + /* Put all enabled ports into suspend */ + while (ports--) { + u32 __iomem *sreg = + &ehci->regs->port_status[ports]; + + temp = ehci_readl(ehci, sreg) & ~PORT_RWC_BITS; + if (temp & PORT_PE) + ehci_writel(ehci, temp | PORT_SUSPEND, + sreg); + } + + spin_unlock_irqrestore(&ehci->lock, flags); ehci_halt(ehci); + spin_lock_irqsave(&ehci->lock, flags); + + temp = ehci_readl(ehci, status_reg); temp |= selector << 16; ehci_writel(ehci, temp, status_reg); break; @@ -971,6 +1276,7 @@ error_exit: spin_unlock_irqrestore (&ehci->lock, flags); return retval; } +EXPORT_SYMBOL_GPL(ehci_hub_control); static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum) { |
