diff options
Diffstat (limited to 'drivers/usb/host/ehci-hub.c')
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 68 |
1 files changed, 44 insertions, 24 deletions
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 1ad7a6572826..a165e0a0961c 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c | |||
@@ -314,41 +314,21 @@ static ssize_t show_companion(struct device *dev, | |||
314 | } | 314 | } |
315 | 315 | ||
316 | /* | 316 | /* |
317 | * Dedicate or undedicate a port to the companion controller. | 317 | * Sets the owner of a port |
318 | * Syntax is "[-]portnum", where a leading '-' sign means | ||
319 | * return control of the port to the EHCI controller. | ||
320 | */ | 318 | */ |
321 | static ssize_t store_companion(struct device *dev, | 319 | static void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner) |
322 | struct device_attribute *attr, | ||
323 | const char *buf, size_t count) | ||
324 | { | 320 | { |
325 | struct ehci_hcd *ehci; | ||
326 | int portnum, new_owner, try; | ||
327 | u32 __iomem *status_reg; | 321 | u32 __iomem *status_reg; |
328 | u32 port_status; | 322 | u32 port_status; |
323 | int try; | ||
329 | 324 | ||
330 | ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); | 325 | status_reg = &ehci->regs->port_status[portnum]; |
331 | new_owner = PORT_OWNER; /* Owned by companion */ | ||
332 | if (sscanf(buf, "%d", &portnum) != 1) | ||
333 | return -EINVAL; | ||
334 | if (portnum < 0) { | ||
335 | portnum = - portnum; | ||
336 | new_owner = 0; /* Owned by EHCI */ | ||
337 | } | ||
338 | if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params)) | ||
339 | return -ENOENT; | ||
340 | status_reg = &ehci->regs->port_status[--portnum]; | ||
341 | if (new_owner) | ||
342 | set_bit(portnum, &ehci->companion_ports); | ||
343 | else | ||
344 | clear_bit(portnum, &ehci->companion_ports); | ||
345 | 326 | ||
346 | /* | 327 | /* |
347 | * The controller won't set the OWNER bit if the port is | 328 | * The controller won't set the OWNER bit if the port is |
348 | * enabled, so this loop will sometimes require at least two | 329 | * enabled, so this loop will sometimes require at least two |
349 | * iterations: one to disable the port and one to set OWNER. | 330 | * iterations: one to disable the port and one to set OWNER. |
350 | */ | 331 | */ |
351 | |||
352 | for (try = 4; try > 0; --try) { | 332 | for (try = 4; try > 0; --try) { |
353 | spin_lock_irq(&ehci->lock); | 333 | spin_lock_irq(&ehci->lock); |
354 | port_status = ehci_readl(ehci, status_reg); | 334 | port_status = ehci_readl(ehci, status_reg); |
@@ -365,6 +345,36 @@ static ssize_t store_companion(struct device *dev, | |||
365 | if (try > 1) | 345 | if (try > 1) |
366 | msleep(5); | 346 | msleep(5); |
367 | } | 347 | } |
348 | } | ||
349 | |||
350 | /* | ||
351 | * Dedicate or undedicate a port to the companion controller. | ||
352 | * Syntax is "[-]portnum", where a leading '-' sign means | ||
353 | * return control of the port to the EHCI controller. | ||
354 | */ | ||
355 | static ssize_t store_companion(struct device *dev, | ||
356 | struct device_attribute *attr, | ||
357 | const char *buf, size_t count) | ||
358 | { | ||
359 | struct ehci_hcd *ehci; | ||
360 | int portnum, new_owner; | ||
361 | |||
362 | ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); | ||
363 | new_owner = PORT_OWNER; /* Owned by companion */ | ||
364 | if (sscanf(buf, "%d", &portnum) != 1) | ||
365 | return -EINVAL; | ||
366 | if (portnum < 0) { | ||
367 | portnum = - portnum; | ||
368 | new_owner = 0; /* Owned by EHCI */ | ||
369 | } | ||
370 | if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params)) | ||
371 | return -ENOENT; | ||
372 | portnum--; | ||
373 | if (new_owner) | ||
374 | set_bit(portnum, &ehci->companion_ports); | ||
375 | else | ||
376 | clear_bit(portnum, &ehci->companion_ports); | ||
377 | set_owner(ehci, portnum, new_owner); | ||
368 | return count; | 378 | return count; |
369 | } | 379 | } |
370 | static DEVICE_ATTR(companion, 0644, show_companion, store_companion); | 380 | static DEVICE_ATTR(companion, 0644, show_companion, store_companion); |
@@ -867,3 +877,13 @@ error: | |||
867 | spin_unlock_irqrestore (&ehci->lock, flags); | 877 | spin_unlock_irqrestore (&ehci->lock, flags); |
868 | return retval; | 878 | return retval; |
869 | } | 879 | } |
880 | |||
881 | static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum) | ||
882 | { | ||
883 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
884 | |||
885 | if (ehci_is_TDI(ehci)) | ||
886 | return; | ||
887 | set_owner(ehci, --portnum, PORT_OWNER); | ||
888 | } | ||
889 | |||