diff options
| author | Balaji Rao <balajirrao@gmail.com> | 2007-11-21 15:28:14 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-02-01 17:34:52 -0500 |
| commit | 90da096ee46b682011b7d549e52b81cf9742e60b (patch) | |
| tree | f70838f277b69e82cb30525d96cad3728cc8b30b /drivers/usb | |
| parent | eb0be47dbbdca133b1b94adc564297f25176b3ab (diff) | |
USB: force handover port to companion when hub_port_connect_change fails
This patch hands over the port to the companion when the
hub_port_connect_change fails.
Signed-off-by: Balaji Rao <balajirrao@gmail.com>
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
| -rw-r--r-- | drivers/usb/core/hcd.h | 3 | ||||
| -rw-r--r-- | drivers/usb/core/hub.c | 3 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-au1xxx.c | 1 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-fsl.c | 1 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-hub.c | 68 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-pci.c | 1 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-ppc-soc.c | 1 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-ps3.c | 1 |
8 files changed, 55 insertions, 24 deletions
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 98e24194a4..080298ad59 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h | |||
| @@ -210,6 +210,9 @@ struct hc_driver { | |||
| 210 | int (*start_port_reset)(struct usb_hcd *, unsigned port_num); | 210 | int (*start_port_reset)(struct usb_hcd *, unsigned port_num); |
| 211 | void (*hub_irq_enable)(struct usb_hcd *); | 211 | void (*hub_irq_enable)(struct usb_hcd *); |
| 212 | /* Needed only if port-change IRQs are level-triggered */ | 212 | /* Needed only if port-change IRQs are level-triggered */ |
| 213 | |||
| 214 | /* force handover of high-speed port to full-speed companion */ | ||
| 215 | void (*relinquish_port)(struct usb_hcd *, int); | ||
| 213 | }; | 216 | }; |
| 214 | 217 | ||
| 215 | extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); | 218 | extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1b17f636f4..24c411697d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
| @@ -2482,6 +2482,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, | |||
| 2482 | { | 2482 | { |
| 2483 | struct usb_device *hdev = hub->hdev; | 2483 | struct usb_device *hdev = hub->hdev; |
| 2484 | struct device *hub_dev = hub->intfdev; | 2484 | struct device *hub_dev = hub->intfdev; |
| 2485 | struct usb_hcd *hcd = bus_to_hcd(hdev->bus); | ||
| 2485 | u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); | 2486 | u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); |
| 2486 | int status, i; | 2487 | int status, i; |
| 2487 | 2488 | ||
| @@ -2645,6 +2646,8 @@ loop: | |||
| 2645 | 2646 | ||
| 2646 | done: | 2647 | done: |
| 2647 | hub_port_disable(hub, port1, 1); | 2648 | hub_port_disable(hub, port1, 1); |
| 2649 | if (hcd->driver->relinquish_port && !hub->hdev->parent) | ||
| 2650 | hcd->driver->relinquish_port(hcd, port1); | ||
| 2648 | } | 2651 | } |
| 2649 | 2652 | ||
| 2650 | static void hub_events(void) | 2653 | static void hub_events(void) |
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 766ef68a0b..da7532d38b 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c | |||
| @@ -222,6 +222,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = { | |||
| 222 | .hub_control = ehci_hub_control, | 222 | .hub_control = ehci_hub_control, |
| 223 | .bus_suspend = ehci_bus_suspend, | 223 | .bus_suspend = ehci_bus_suspend, |
| 224 | .bus_resume = ehci_bus_resume, | 224 | .bus_resume = ehci_bus_resume, |
| 225 | .relinquish_port = ehci_relinquish_port, | ||
| 225 | }; | 226 | }; |
| 226 | 227 | ||
| 227 | /*-------------------------------------------------------------------------*/ | 228 | /*-------------------------------------------------------------------------*/ |
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 430821cb95..f35795fbd8 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c | |||
| @@ -323,6 +323,7 @@ static const struct hc_driver ehci_fsl_hc_driver = { | |||
| 323 | .hub_control = ehci_hub_control, | 323 | .hub_control = ehci_hub_control, |
| 324 | .bus_suspend = ehci_bus_suspend, | 324 | .bus_suspend = ehci_bus_suspend, |
| 325 | .bus_resume = ehci_bus_resume, | 325 | .bus_resume = ehci_bus_resume, |
| 326 | .relinquish_port = ehci_relinquish_port, | ||
| 326 | }; | 327 | }; |
| 327 | 328 | ||
| 328 | static int ehci_fsl_drv_probe(struct platform_device *pdev) | 329 | static int ehci_fsl_drv_probe(struct platform_device *pdev) |
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 1ad7a65728..a165e0a096 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 | |||
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index ad0d4965f2..45e0400002 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c | |||
| @@ -364,6 +364,7 @@ static const struct hc_driver ehci_pci_hc_driver = { | |||
| 364 | .hub_control = ehci_hub_control, | 364 | .hub_control = ehci_hub_control, |
| 365 | .bus_suspend = ehci_bus_suspend, | 365 | .bus_suspend = ehci_bus_suspend, |
| 366 | .bus_resume = ehci_bus_resume, | 366 | .bus_resume = ehci_bus_resume, |
| 367 | .relinquish_port = ehci_relinquish_port, | ||
| 367 | }; | 368 | }; |
| 368 | 369 | ||
| 369 | /*-------------------------------------------------------------------------*/ | 370 | /*-------------------------------------------------------------------------*/ |
diff --git a/drivers/usb/host/ehci-ppc-soc.c b/drivers/usb/host/ehci-ppc-soc.c index 452d4b1bc8..a3249078c8 100644 --- a/drivers/usb/host/ehci-ppc-soc.c +++ b/drivers/usb/host/ehci-ppc-soc.c | |||
| @@ -162,6 +162,7 @@ static const struct hc_driver ehci_ppc_soc_hc_driver = { | |||
| 162 | .hub_control = ehci_hub_control, | 162 | .hub_control = ehci_hub_control, |
| 163 | .bus_suspend = ehci_bus_suspend, | 163 | .bus_suspend = ehci_bus_suspend, |
| 164 | .bus_resume = ehci_bus_resume, | 164 | .bus_resume = ehci_bus_resume, |
| 165 | .relinquish_port = ehci_relinquish_port, | ||
| 165 | }; | 166 | }; |
| 166 | 167 | ||
| 167 | static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev) | 168 | static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev) |
diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 03a6b2f4e6..bbda58eb88 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c | |||
| @@ -72,6 +72,7 @@ static const struct hc_driver ps3_ehci_hc_driver = { | |||
| 72 | .bus_suspend = ehci_bus_suspend, | 72 | .bus_suspend = ehci_bus_suspend, |
| 73 | .bus_resume = ehci_bus_resume, | 73 | .bus_resume = ehci_bus_resume, |
| 74 | #endif | 74 | #endif |
| 75 | .relinquish_port = ehci_relinquish_port, | ||
| 75 | }; | 76 | }; |
| 76 | 77 | ||
| 77 | static int ps3_ehci_probe(struct ps3_system_bus_device *dev) | 78 | static int ps3_ehci_probe(struct ps3_system_bus_device *dev) |
