summaryrefslogtreecommitdiff
path: root/drivers/usb/core/hub.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r--drivers/usb/core/hub.c47
1 files changed, 39 insertions, 8 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 87c938adbc7..abb0084795c 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1693,6 +1693,12 @@ void usb_disconnect(struct usb_device **pdev)
dev_info(&udev->dev, "USB disconnect, device number %d\n",
udev->devnum);
+#ifdef CONFIG_USB_OTG
+ if (udev->bus->hnp_support && udev->portnum == udev->bus->otg_port) {
+ cancel_delayed_work_sync(&udev->bus->hnp_polling);
+ udev->bus->hnp_support = 0;
+ }
+#endif
usb_lock_device(udev);
/* Free up all the children before we remove this device */
@@ -1797,15 +1803,10 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
(port1 == bus->otg_port)
? "" : "non-");
- /* enable HNP before suspend, it's simpler */
- if (port1 == bus->otg_port)
- bus->b_hnp_enable = 1;
err = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE, 0,
- bus->b_hnp_enable
- ? USB_DEVICE_B_HNP_ENABLE
- : USB_DEVICE_A_ALT_HNP_SUPPORT,
+ USB_DEVICE_A_HNP_SUPPORT,
0, NULL, 0, USB_CTRL_SET_TIMEOUT);
if (err < 0) {
/* OTG MESSAGE: report errors here,
@@ -1814,24 +1815,34 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
dev_info(&udev->dev,
"can't set HNP mode: %d\n",
err);
- bus->b_hnp_enable = 0;
+ bus->hnp_support = 0;
}
}
}
}
+out:
if (!is_targeted(udev)) {
/* Maybe it can talk to us, though we can't talk to it.
* (Includes HNP test device.)
*/
- if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
+ if (udev->bus->hnp_support) {
err = usb_port_suspend(udev, PMSG_SUSPEND);
if (err < 0)
dev_dbg(&udev->dev, "HNP fail, %d\n", err);
}
err = -ENOTSUPP;
goto fail;
+ } else if (udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port) {
+ /* HNP polling is introduced in OTG supplement Rev 2.0
+ * and older devices does not support. Work is not
+ * re-armed if device returns STALL. B-Host also performs
+ * HNP polling.
+ */
+ schedule_delayed_work(&udev->bus->hnp_polling,
+ msecs_to_jiffies(THOST_REQ_POLL));
}
fail:
#endif
@@ -2497,6 +2508,20 @@ 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);
+#ifdef CONFIG_USB_OTG
+ if (!udev->bus->is_b_host && udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port) {
+ status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE, 0,
+ USB_DEVICE_B_HNP_ENABLE,
+ 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (status < 0)
+ dev_dbg(&udev->dev, "can't enable HNP on port %d, "
+ "status %d\n", port1, status);
+ else
+ udev->bus->b_hnp_enable = 1;
+ }
+#endif
/* see 7.1.7.6 */
if (hub_is_superspeed(hub->hdev))
status = set_port_feature(hub->hdev,
@@ -2651,6 +2676,12 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
int status;
u16 portchange, portstatus;
+#ifdef CONFIG_USB_OTG
+ if (!udev->bus->is_b_host && udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port)
+ udev->bus->b_hnp_enable = 0;
+#endif
+
/* Skip the initial Clear-Suspend step for a remote wakeup */
status = hub_port_status(hub, port1, &portstatus, &portchange);
if (status == 0 && !port_is_suspended(hub, portstatus))