diff options
author | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2009-04-27 22:54:26 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-16 00:44:48 -0400 |
commit | e7b7717247f61e2cf18ec47f91999065c59d1607 (patch) | |
tree | e644aa7eb5aa1063aaece0fddb14598c2f7404f3 /drivers/usb | |
parent | d2e9b4d6734db2327af3149d8ca7555307e10828 (diff) |
USB: Don't reset USB 3.0 devices on port change detection.
The USB 3.0 bus specification defines a new connection sequence for USB 3.0
hubs and roothubs. USB 3.0 devices are reset and link trained by the hub
before the port status change notification is sent to the host OS. This means
that an entire tree of devices can be trained in parallel on power up, and the
OS no longer needs to reset USB 3.0 devices. Change the USB core's hub port
init sequence so that it does not reset USB 3.0 devices.
The port status change from the roothub and from the USB 3.0 hub will report
the SuperSpeed connect correctly. This patch currently only handles the
roothub case.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/hub.c | 42 |
1 files changed, 36 insertions, 6 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index f3fe8dfaaad1..3c28bde6cbd5 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -2435,6 +2435,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, | |||
2435 | static DEFINE_MUTEX(usb_address0_mutex); | 2435 | static DEFINE_MUTEX(usb_address0_mutex); |
2436 | 2436 | ||
2437 | struct usb_device *hdev = hub->hdev; | 2437 | struct usb_device *hdev = hub->hdev; |
2438 | struct usb_hcd *hcd = bus_to_hcd(hdev->bus); | ||
2438 | int i, j, retval; | 2439 | int i, j, retval; |
2439 | unsigned delay = HUB_SHORT_RESET_TIME; | 2440 | unsigned delay = HUB_SHORT_RESET_TIME; |
2440 | enum usb_device_speed oldspeed = udev->speed; | 2441 | enum usb_device_speed oldspeed = udev->speed; |
@@ -2457,11 +2458,24 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, | |||
2457 | 2458 | ||
2458 | mutex_lock(&usb_address0_mutex); | 2459 | mutex_lock(&usb_address0_mutex); |
2459 | 2460 | ||
2460 | /* Reset the device; full speed may morph to high speed */ | 2461 | if ((hcd->driver->flags & HCD_USB3) && udev->config) { |
2461 | retval = hub_port_reset(hub, port1, udev, delay); | 2462 | /* FIXME this will need special handling by the xHCI driver. */ |
2462 | if (retval < 0) /* error or disconnect */ | 2463 | dev_dbg(&udev->dev, |
2464 | "xHCI reset of configured device " | ||
2465 | "not supported yet.\n"); | ||
2466 | retval = -EINVAL; | ||
2463 | goto fail; | 2467 | goto fail; |
2464 | /* success, speed is known */ | 2468 | } else if (!udev->config && oldspeed == USB_SPEED_SUPER) { |
2469 | /* Don't reset USB 3.0 devices during an initial setup */ | ||
2470 | usb_set_device_state(udev, USB_STATE_DEFAULT); | ||
2471 | } else { | ||
2472 | /* Reset the device; full speed may morph to high speed */ | ||
2473 | /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ | ||
2474 | retval = hub_port_reset(hub, port1, udev, delay); | ||
2475 | if (retval < 0) /* error or disconnect */ | ||
2476 | goto fail; | ||
2477 | /* success, speed is known */ | ||
2478 | } | ||
2465 | retval = -ENODEV; | 2479 | retval = -ENODEV; |
2466 | 2480 | ||
2467 | if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) { | 2481 | if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) { |
@@ -2859,7 +2873,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, | |||
2859 | } | 2873 | } |
2860 | 2874 | ||
2861 | usb_set_device_state(udev, USB_STATE_POWERED); | 2875 | usb_set_device_state(udev, USB_STATE_POWERED); |
2862 | udev->speed = USB_SPEED_UNKNOWN; | ||
2863 | udev->bus_mA = hub->mA_per_port; | 2876 | udev->bus_mA = hub->mA_per_port; |
2864 | udev->level = hdev->level + 1; | 2877 | udev->level = hdev->level + 1; |
2865 | udev->wusb = hub_is_wusb(hub); | 2878 | udev->wusb = hub_is_wusb(hub); |
@@ -2871,7 +2884,24 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, | |||
2871 | goto loop; | 2884 | goto loop; |
2872 | } | 2885 | } |
2873 | 2886 | ||
2874 | /* reset and get descriptor */ | 2887 | /* |
2888 | * USB 3.0 devices are reset automatically before the connect | ||
2889 | * port status change appears, and the root hub port status | ||
2890 | * shows the correct speed. We also get port change | ||
2891 | * notifications for USB 3.0 devices from the USB 3.0 portion of | ||
2892 | * an external USB 3.0 hub, but this isn't handled correctly yet | ||
2893 | * FIXME. | ||
2894 | */ | ||
2895 | |||
2896 | if (!(hcd->driver->flags & HCD_USB3)) | ||
2897 | udev->speed = USB_SPEED_UNKNOWN; | ||
2898 | else if ((hdev->parent == NULL) && | ||
2899 | (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED))) | ||
2900 | udev->speed = USB_SPEED_SUPER; | ||
2901 | else | ||
2902 | udev->speed = USB_SPEED_UNKNOWN; | ||
2903 | |||
2904 | /* reset (non-USB 3.0 devices) and get descriptor */ | ||
2875 | status = hub_port_init(hub, udev, port1, i); | 2905 | status = hub_port_init(hub, udev, port1, i); |
2876 | if (status < 0) | 2906 | if (status < 0) |
2877 | goto loop; | 2907 | goto loop; |