| From 653a39d1f61bdc9f277766736d21d2e9be0391cb Mon Sep 17 00:00:00 2001 |
| From: Sarah Sharp <sarah.a.sharp@linux.intel.com> |
| Date: Thu, 23 Dec 2010 11:12:42 -0800 |
| Subject: usb: Realloc xHCI structures after a hub is verified. |
| |
| From: Sarah Sharp <sarah.a.sharp@linux.intel.com> |
| |
| commit 653a39d1f61bdc9f277766736d21d2e9be0391cb upstream. |
| |
| When there's an xHCI host power loss after a suspend from memory, the USB |
| core attempts to reset and verify the USB devices that are attached to the |
| system. The xHCI driver has to reallocate those devices, since the |
| hardware lost all knowledge of them during the power loss. |
| |
| When a hub is plugged in, and the host loses power, the xHCI hardware |
| structures are not updated to say the device is a hub. This is usually |
| done in hub_configure() when the USB hub is detected. That function is |
| skipped during a reset and verify by the USB core, since the core restores |
| the old configuration and alternate settings, and the hub driver has no |
| idea this happened. This bug makes the xHCI host controller reject the |
| enumeration of low speed devices under the resumed hub. |
| |
| Therefore, make the USB core re-setup the internal xHCI hub device |
| information by calling update_hub_device() when hub_activate() is called |
| for a hub reset resume. After a host power loss, all devices under the |
| roothub get a reset-resume or a disconnect. |
| |
| This patch should be queued for the 2.6.37 stable tree. |
| |
| Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| drivers/usb/core/hub.c | 21 +++++++++++++++++++++ |
| 1 file changed, 21 insertions(+) |
| |
| --- a/drivers/usb/core/hub.c |
| +++ b/drivers/usb/core/hub.c |
| @@ -677,6 +677,8 @@ static void hub_init_func3(struct work_s |
| static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) |
| { |
| struct usb_device *hdev = hub->hdev; |
| + struct usb_hcd *hcd; |
| + int ret; |
| int port1; |
| int status; |
| bool need_debounce_delay = false; |
| @@ -715,6 +717,25 @@ static void hub_activate(struct usb_hub |
| usb_autopm_get_interface_no_resume( |
| to_usb_interface(hub->intfdev)); |
| return; /* Continues at init2: below */ |
| + } else if (type == HUB_RESET_RESUME) { |
| + /* The internal host controller state for the hub device |
| + * may be gone after a host power loss on system resume. |
| + * Update the device's info so the HW knows it's a hub. |
| + */ |
| + hcd = bus_to_hcd(hdev->bus); |
| + if (hcd->driver->update_hub_device) { |
| + ret = hcd->driver->update_hub_device(hcd, hdev, |
| + &hub->tt, GFP_NOIO); |
| + if (ret < 0) { |
| + dev_err(hub->intfdev, "Host not " |
| + "accepting hub info " |
| + "update.\n"); |
| + dev_err(hub->intfdev, "LS/FS devices " |
| + "and hubs may not work " |
| + "under this hub\n."); |
| + } |
| + } |
| + hub_power_on(hub, true); |
| } else { |
| hub_power_on(hub, true); |
| } |