Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: EHCI, OHCI: handover changes

This patch (as887) changes the way ehci-hcd and ohci-hcd handle a loss
of VBUS power during suspend. In order for the USB-persist facility
to work correctly, it is necessary for low- and full-speed devices
attached to a high-speed port to be handed back to the companion
controller during resume processing.

This entails three changes: adding code to ehci-hcd to perform the
handover, removing code from ohci-hcd to turn off ports during
root-hub reinit, and adding code to ohci-hcd to turn on ports during
PCI controller resume. (Other bus glue resume methods for platforms
supporting high-speed controllers would need a similar change, if any
existed.)

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
383975d7 0458d5b4

+145 -28
+2
drivers/usb/host/ehci-hcd.c
··· 399 399 is_on ? SetPortFeature : ClearPortFeature, 400 400 USB_PORT_FEAT_POWER, 401 401 port--, NULL, 0); 402 + /* Flush those writes */ 403 + ehci_readl(ehci, &ehci->regs->command); 402 404 msleep(20); 403 405 } 404 406
+94 -5
drivers/usb/host/ehci-hub.c
··· 28 28 29 29 /*-------------------------------------------------------------------------*/ 30 30 31 + #ifdef CONFIG_USB_PERSIST 32 + 33 + static int ehci_hub_control( 34 + struct usb_hcd *hcd, 35 + u16 typeReq, 36 + u16 wValue, 37 + u16 wIndex, 38 + char *buf, 39 + u16 wLength 40 + ); 41 + 42 + /* After a power loss, ports that were owned by the companion must be 43 + * reset so that the companion can still own them. 44 + */ 45 + static void ehci_handover_companion_ports(struct ehci_hcd *ehci) 46 + { 47 + u32 __iomem *reg; 48 + u32 status; 49 + int port; 50 + __le32 buf; 51 + struct usb_hcd *hcd = ehci_to_hcd(ehci); 52 + 53 + if (!ehci->owned_ports) 54 + return; 55 + 56 + /* Give the connections some time to appear */ 57 + msleep(20); 58 + 59 + port = HCS_N_PORTS(ehci->hcs_params); 60 + while (port--) { 61 + if (test_bit(port, &ehci->owned_ports)) { 62 + reg = &ehci->regs->port_status[port]; 63 + status = ehci_readl(ehci, reg); 64 + 65 + /* Port already owned by companion? */ 66 + if (status & PORT_OWNER) 67 + clear_bit(port, &ehci->owned_ports); 68 + else 69 + ehci_hub_control(hcd, SetPortFeature, 70 + USB_PORT_FEAT_RESET, port + 1, 71 + NULL, 0); 72 + } 73 + } 74 + 75 + if (!ehci->owned_ports) 76 + return; 77 + msleep(90); /* Wait for resets to complete */ 78 + 79 + port = HCS_N_PORTS(ehci->hcs_params); 80 + while (port--) { 81 + if (test_bit(port, &ehci->owned_ports)) { 82 + ehci_hub_control(hcd, GetPortStatus, 83 + 0, port + 1, 84 + (char *) &buf, sizeof(buf)); 85 + 86 + /* The companion should now own the port, 87 + * but if something went wrong the port must not 88 + * remain enabled. 89 + */ 90 + reg = &ehci->regs->port_status[port]; 91 + status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; 92 + if (status & PORT_OWNER) 93 + ehci_writel(ehci, status | PORT_CSC, reg); 94 + else { 95 + ehci_dbg(ehci, "failed handover port %d: %x\n", 96 + port + 1, status); 97 + ehci_writel(ehci, status & ~PORT_PE, reg); 98 + } 99 + } 100 + } 101 + 102 + ehci->owned_ports = 0; 103 + } 104 + 105 + #else /* CONFIG_USB_PERSIST */ 106 + 107 + static inline void ehci_handover_companion_ports(struct ehci_hcd *ehci) 108 + { } 109 + 110 + #endif 111 + 31 112 #ifdef CONFIG_PM 32 113 33 114 static int ehci_bus_suspend (struct usb_hcd *hcd) ··· 141 60 * then manually resume them in the bus_resume() routine. 142 61 */ 143 62 ehci->bus_suspended = 0; 63 + ehci->owned_ports = 0; 144 64 while (port--) { 145 65 u32 __iomem *reg = &ehci->regs->port_status [port]; 146 66 u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; 147 67 u32 t2 = t1; 148 68 149 69 /* keep track of which ports we suspend */ 150 - if ((t1 & PORT_PE) && !(t1 & PORT_OWNER) && 151 - !(t1 & PORT_SUSPEND)) { 70 + if (t1 & PORT_OWNER) 71 + set_bit(port, &ehci->owned_ports); 72 + else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) { 152 73 t2 |= PORT_SUSPEND; 153 74 set_bit(port, &ehci->bus_suspended); 154 75 } ··· 191 108 { 192 109 struct ehci_hcd *ehci = hcd_to_ehci (hcd); 193 110 u32 temp; 111 + u32 power_okay; 194 112 int i; 195 113 196 114 if (time_before (jiffies, ehci->next_statechange)) ··· 204 120 * the last user of the controller, not reset/pm hardware keeping 205 121 * state we gave to it. 206 122 */ 207 - temp = ehci_readl(ehci, &ehci->regs->intr_enable); 208 - ehci_dbg(ehci, "resume root hub%s\n", temp ? "" : " after power loss"); 123 + power_okay = ehci_readl(ehci, &ehci->regs->intr_enable); 124 + ehci_dbg(ehci, "resume root hub%s\n", 125 + power_okay ? "" : " after power loss"); 209 126 210 127 /* at least some APM implementations will try to deliver 211 128 * IRQs right away, so delay them until we're ready. ··· 269 184 ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); 270 185 271 186 spin_unlock_irq (&ehci->lock); 187 + 188 + if (!power_okay) 189 + ehci_handover_companion_ports(ehci); 272 190 return 0; 273 191 } 274 192 ··· 536 448 ) { 537 449 struct ehci_hcd *ehci = hcd_to_ehci (hcd); 538 450 int ports = HCS_N_PORTS (ehci->hcs_params); 539 - u32 __iomem *status_reg = &ehci->regs->port_status[wIndex - 1]; 451 + u32 __iomem *status_reg = &ehci->regs->port_status[ 452 + (wIndex & 0xff) - 1]; 540 453 u32 temp, status; 541 454 unsigned long flags; 542 455 int retval = 0;
+4 -3
drivers/usb/host/ehci-pci.c
··· 312 312 ehci_work(ehci); 313 313 spin_unlock_irq(&ehci->lock); 314 314 315 - /* here we "know" root ports should always stay powered */ 316 - ehci_port_power(ehci, 1); 317 - 318 315 ehci_writel(ehci, ehci->command, &ehci->regs->command); 319 316 ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); 320 317 ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ 318 + 319 + /* here we "know" root ports should always stay powered */ 320 + ehci_port_power(ehci, 1); 321 + ehci_handover_companion_ports(ehci); 321 322 322 323 hcd->state = HC_STATE_SUSPENDED; 323 324 return 0;
+3
drivers/usb/host/ehci.h
··· 96 96 97 97 /* per root hub port */ 98 98 unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; 99 + 99 100 /* bit vectors (one bit per port) */ 100 101 unsigned long bus_suspended; /* which ports were 101 102 already suspended at the start of a bus suspend */ 102 103 unsigned long companion_ports; /* which ports are 103 104 dedicated to the companion controller */ 105 + unsigned long owned_ports; /* which ports are 106 + owned by the companion during a bus suspend */ 104 107 105 108 /* per-HC memory pools (could be per-bus, but ...) */ 106 109 struct dma_pool *qh_pool; /* qh per active urb */
+2 -19
drivers/usb/host/ohci-hcd.c
··· 510 510 // flush the writes 511 511 (void) ohci_readl (ohci, &ohci->regs->control); 512 512 msleep(temp); 513 - temp = roothub_a (ohci); 514 - if (!(temp & RH_A_NPS)) { 515 - /* power down each port */ 516 - for (temp = 0; temp < ohci->num_ports; temp++) 517 - ohci_writel (ohci, RH_PS_LSDA, 518 - &ohci->regs->roothub.portstatus [temp]); 519 - } 520 - // flush those writes 521 - (void) ohci_readl (ohci, &ohci->regs->control); 513 + 522 514 memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); 523 515 524 516 /* 2msec timelimit here means no irqs/preempt */ ··· 818 826 if ((temp = ohci_run (ohci)) < 0) { 819 827 ohci_err (ohci, "can't restart, %d\n", temp); 820 828 return temp; 821 - } else { 822 - /* here we "know" root ports should always stay powered, 823 - * and that if we try to turn them back on the root hub 824 - * will respond to CSC processing. 825 - */ 826 - i = ohci->num_ports; 827 - while (i--) 828 - ohci_writel (ohci, RH_PS_PSS, 829 - &ohci->regs->roothub.portstatus [i]); 830 - ohci_dbg (ohci, "restart complete\n"); 831 829 } 830 + ohci_dbg(ohci, "restart complete\n"); 832 831 return 0; 833 832 } 834 833 #endif
+40 -1
drivers/usb/host/ohci-pci.c
··· 202 202 return ret; 203 203 } 204 204 205 + #if defined(CONFIG_USB_PERSIST) && (defined(CONFIG_USB_EHCI_HCD) || \ 206 + defined(CONFIG_USB_EHCI_HCD_MODULE)) 207 + 208 + /* Following a power loss, we must prepare to regain control of the ports 209 + * we used to own. This means turning on the port power before ehci-hcd 210 + * tries to switch ownership. 211 + * 212 + * This isn't a 100% perfect solution. On most systems the OHCI controllers 213 + * lie at lower PCI addresses than the EHCI controller, so they will be 214 + * discovered (and hence resumed) first. But there is no guarantee things 215 + * will always work this way. If the EHCI controller is resumed first and 216 + * the OHCI ports are unpowered, then the handover will fail. 217 + */ 218 + static void prepare_for_handover(struct usb_hcd *hcd) 219 + { 220 + struct ohci_hcd *ohci = hcd_to_ohci(hcd); 221 + int port; 222 + 223 + /* Here we "know" root ports should always stay powered */ 224 + ohci_dbg(ohci, "powerup ports\n"); 225 + for (port = 0; port < ohci->num_ports; port++) 226 + ohci_writel(ohci, RH_PS_PPS, 227 + &ohci->regs->roothub.portstatus[port]); 228 + 229 + /* Flush those writes */ 230 + ohci_readl(ohci, &ohci->regs->control); 231 + msleep(20); 232 + } 233 + 234 + #else 235 + 236 + static inline void prepare_for_handover(struct usb_hcd *hcd) 237 + { } 238 + 239 + #endif /* CONFIG_USB_PERSIST etc. */ 240 + 205 241 #ifdef CONFIG_PM 206 242 207 243 static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) ··· 277 241 static int ohci_pci_resume (struct usb_hcd *hcd) 278 242 { 279 243 set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); 280 - usb_hcd_resume_root_hub(hcd); 244 + 245 + /* FIXME: we should try to detect loss of VBUS power here */ 246 + prepare_for_handover(hcd); 247 + 281 248 return 0; 282 249 } 283 250