aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBalaji Rao <balajirrao@gmail.com>2007-11-21 15:28:14 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2008-02-01 17:34:52 -0500
commit90da096ee46b682011b7d549e52b81cf9742e60b (patch)
treef70838f277b69e82cb30525d96cad3728cc8b30b
parenteb0be47dbbdca133b1b94adc564297f25176b3ab (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>
-rw-r--r--drivers/usb/core/hcd.h3
-rw-r--r--drivers/usb/core/hub.c3
-rw-r--r--drivers/usb/host/ehci-au1xxx.c1
-rw-r--r--drivers/usb/host/ehci-fsl.c1
-rw-r--r--drivers/usb/host/ehci-hub.c68
-rw-r--r--drivers/usb/host/ehci-pci.c1
-rw-r--r--drivers/usb/host/ehci-ppc-soc.c1
-rw-r--r--drivers/usb/host/ehci-ps3.c1
8 files changed, 55 insertions, 24 deletions
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 98e24194a4a..080298ad599 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
215extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); 218extern 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 1b17f636f49..24c411697d1 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
2646done: 2647done:
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
2650static void hub_events(void) 2653static void hub_events(void)
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c
index 766ef68a0b4..da7532d38bf 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 430821cb95c..f35795fbd89 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
328static int ehci_fsl_drv_probe(struct platform_device *pdev) 329static 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 1ad7a657282..a165e0a0961 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 */
321static ssize_t store_companion(struct device *dev, 319static 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 */
355static 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}
370static DEVICE_ATTR(companion, 0644, show_companion, store_companion); 380static 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
881static 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 ad0d4965f2f..45e04000028 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 452d4b1bc85..a3249078c80 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
167static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev) 168static 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 03a6b2f4e6e..bbda58eb881 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
77static int ps3_ehci_probe(struct ps3_system_bus_device *dev) 78static int ps3_ehci_probe(struct ps3_system_bus_device *dev)