X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=drivers%2Fusb%2Fcore%2Fhub.c;h=128a804c42f406b6c6b1f32f57110ff6b0810b41;hb=3e9a97082fa639394e905e1fc4a0a7f719ca7644;hp=7f380ff1f78645a3d727978e5b9a89bcec317207;hpb=d2e7c96af1e54b507ae2a6a7dd2baf588417a7e5;p=pandora-kernel.git diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 7f380ff1f786..128a804c42f4 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -82,7 +83,7 @@ struct usb_hub { u8 indicator[USB_MAXCHILDREN]; struct delayed_work leds; struct delayed_work init_work; - void **port_owners; + struct dev_state **port_owners; }; static inline int hub_is_superspeed(struct usb_device *hdev) @@ -1272,7 +1273,8 @@ static int hub_configure(struct usb_hub *hub, hdev->children = kzalloc(hdev->maxchild * sizeof(struct usb_device *), GFP_KERNEL); - hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL); + hub->port_owners = kzalloc(hdev->maxchild * sizeof(struct dev_state *), + GFP_KERNEL); if (!hdev->children || !hub->port_owners) { ret = -ENOMEM; goto fail; @@ -1650,7 +1652,7 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) * to one of these "claimed" ports, the program will "own" the device. */ static int find_port_owner(struct usb_device *hdev, unsigned port1, - void ***ppowner) + struct dev_state ***ppowner) { if (hdev->state == USB_STATE_NOTATTACHED) return -ENODEV; @@ -1665,10 +1667,11 @@ static int find_port_owner(struct usb_device *hdev, unsigned port1, } /* In the following three functions, the caller must hold hdev's lock */ -int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner) +int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, + struct dev_state *owner) { int rc; - void **powner; + struct dev_state **powner; rc = find_port_owner(hdev, port1, &powner); if (rc) @@ -1679,10 +1682,11 @@ int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner) return rc; } -int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner) +int usb_hub_release_port(struct usb_device *hdev, unsigned port1, + struct dev_state *owner) { int rc; - void **powner; + struct dev_state **powner; rc = find_port_owner(hdev, port1, &powner); if (rc) @@ -1693,10 +1697,10 @@ int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner) return rc; } -void usb_hub_release_all_ports(struct usb_device *hdev, void *owner) +void usb_hub_release_all_ports(struct usb_device *hdev, struct dev_state *owner) { int n; - void **powner; + struct dev_state **powner; n = find_port_owner(hdev, 1, &powner); if (n == 0) { @@ -2066,7 +2070,7 @@ static int usb_enumerate_device(struct usb_device *udev) if (err < 0) { dev_err(&udev->dev, "can't read configurations, error %d\n", err); - goto fail; + return err; } } if (udev->wusb == 1 && udev->authorized == 0) { @@ -2082,8 +2086,12 @@ static int usb_enumerate_device(struct usb_device *udev) udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber); } err = usb_enumerate_device_otg(udev); -fail: - return err; + if (err < 0) + return err; + + usb_detect_interface_quirks(udev); + + return 0; } static void set_usb_port_removable(struct usb_device *udev) @@ -2333,12 +2341,16 @@ static unsigned hub_is_wusb(struct usb_hub *hub) static int hub_port_reset(struct usb_hub *hub, int port1, struct usb_device *udev, unsigned int delay, bool warm); -/* Is a USB 3.0 port in the Inactive state? */ -static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus) +/* Is a USB 3.0 port in the Inactive or Complinance Mode state? + * Port worm reset is required to recover + */ +static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus) { return hub_is_superspeed(hub->hdev) && - (portstatus & USB_PORT_STAT_LINK_STATE) == - USB_SS_PORT_LS_SS_INACTIVE; + (((portstatus & USB_PORT_STAT_LINK_STATE) == + USB_SS_PORT_LS_SS_INACTIVE) || + ((portstatus & USB_PORT_STAT_LINK_STATE) == + USB_SS_PORT_LS_COMP_MOD)) ; } static int hub_port_wait_reset(struct usb_hub *hub, int port1, @@ -2374,7 +2386,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, * * See https://bugzilla.kernel.org/show_bug.cgi?id=41752 */ - if (hub_port_inactive(hub, portstatus)) { + if (hub_port_warm_reset_required(hub, portstatus)) { int ret; if ((portchange & USB_PORT_STAT_C_CONNECTION)) @@ -2616,6 +2628,50 @@ static int check_port_resume_type(struct usb_device *udev, return status; } +int usb_disable_ltm(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + /* Check if the roothub and device supports LTM. */ + if (!usb_device_supports_ltm(hcd->self.root_hub) || + !usb_device_supports_ltm(udev)) + return 0; + + /* Clear Feature LTM Enable can only be sent if the device is + * configured. + */ + if (!udev->actconfig) + return 0; + + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_LTM_ENABLE, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} +EXPORT_SYMBOL_GPL(usb_disable_ltm); + +void usb_enable_ltm(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + /* Check if the roothub and device supports LTM. */ + if (!usb_device_supports_ltm(hcd->self.root_hub) || + !usb_device_supports_ltm(udev)) + return; + + /* Set Feature LTM Enable can only be sent if the device is + * configured. + */ + if (!udev->actconfig) + return; + + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_LTM_ENABLE, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} +EXPORT_SYMBOL_GPL(usb_enable_ltm); + #ifdef CONFIG_USB_SUSPEND /* @@ -2711,6 +2767,11 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_enabled == 1) usb_set_usb2_hardware_lpm(udev, 0); + if (usb_disable_ltm(udev)) { + dev_err(&udev->dev, "%s Failed to disable LTM before suspend\n.", + __func__); + return -ENOMEM; + } if (usb_unlocked_disable_lpm(udev)) { dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.", __func__); @@ -2740,7 +2801,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_capable == 1) usb_set_usb2_hardware_lpm(udev, 1); - /* Try to enable USB3 LPM again */ + /* Try to enable USB3 LTM and LPM again */ + usb_enable_ltm(udev); usb_unlocked_enable_lpm(udev); /* System sleep transitions should never fail */ @@ -2941,7 +3003,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_capable == 1) usb_set_usb2_hardware_lpm(udev, 1); - /* Try to enable USB3 LPM */ + /* Try to enable USB3 LTM and LPM */ + usb_enable_ltm(udev); usb_unlocked_enable_lpm(udev); } @@ -3494,6 +3557,15 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm); void usb_unlocked_enable_lpm(struct usb_device *udev) { } EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); + +int usb_disable_ltm(struct usb_device *udev) +{ + return 0; +} +EXPORT_SYMBOL_GPL(usb_disable_ltm); + +void usb_enable_ltm(struct usb_device *udev) { } +EXPORT_SYMBOL_GPL(usb_enable_ltm); #endif @@ -4043,6 +4115,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, } } + if (hcd->phy && !hdev->parent) { + if (portstatus & USB_PORT_STAT_CONNECTION) + usb_phy_notify_connect(hcd->phy, port1); + else + usb_phy_notify_disconnect(hcd->phy, port1); + } + /* Return now if debouncing failed or nothing is connected or * the device was "removed". */ @@ -4417,9 +4496,7 @@ static void hub_events(void) /* Warm reset a USB3 protocol port if it's in * SS.Inactive state. */ - if (hub_is_superspeed(hub->hdev) && - (portstatus & USB_PORT_STAT_LINK_STATE) - == USB_SS_PORT_LS_SS_INACTIVE) { + if (hub_port_warm_reset_required(hub, portstatus)) { dev_dbg(hub_dev, "warm reset port %d\n", i); hub_port_reset(hub, i, NULL, HUB_BH_RESET_TIME, true); @@ -4679,6 +4756,23 @@ static int usb_reset_and_verify_device(struct usb_device *udev) } parent_hub = hdev_to_hub(parent_hdev); + /* Disable LPM and LTM while we reset the device and reinstall the alt + * settings. Device-initiated LPM settings, and system exit latency + * settings are cleared when the device is reset, so we have to set + * them up again. + */ + ret = usb_unlocked_disable_lpm(udev); + if (ret) { + dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); + goto re_enumerate; + } + ret = usb_disable_ltm(udev); + if (ret) { + dev_err(&udev->dev, "%s Failed to disable LTM\n.", + __func__); + goto re_enumerate; + } + set_bit(port1, parent_hub->busy_bits); for (i = 0; i < SET_CONFIG_TRIES; ++i) { @@ -4706,22 +4800,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev) goto done; mutex_lock(hcd->bandwidth_mutex); - /* Disable LPM while we reset the device and reinstall the alt settings. - * Device-initiated LPM settings, and system exit latency settings are - * cleared when the device is reset, so we have to set them up again. - */ - ret = usb_disable_lpm(udev); - if (ret) { - dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); - mutex_unlock(hcd->bandwidth_mutex); - goto done; - } ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL); if (ret < 0) { dev_warn(&udev->dev, "Busted HC? Not enough HCD resources for " "old configuration.\n"); - usb_enable_lpm(udev); mutex_unlock(hcd->bandwidth_mutex); goto re_enumerate; } @@ -4733,7 +4816,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev) dev_err(&udev->dev, "can't restore configuration #%d (error=%d)\n", udev->actconfig->desc.bConfigurationValue, ret); - usb_enable_lpm(udev); mutex_unlock(hcd->bandwidth_mutex); goto re_enumerate; } @@ -4772,17 +4854,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev) desc->bInterfaceNumber, desc->bAlternateSetting, ret); - usb_unlocked_enable_lpm(udev); goto re_enumerate; } } - /* Now that the alt settings are re-installed, enable LPM. */ - usb_unlocked_enable_lpm(udev); done: + /* Now that the alt settings are re-installed, enable LTM and LPM. */ + usb_unlocked_enable_lpm(udev); + usb_enable_ltm(udev); return 0; re_enumerate: + /* LPM state doesn't matter when we're about to destroy the device. */ hub_port_logical_disconnect(parent_hub, port1); return -ENODEV; }