| From: Mike Looijmans <mike.looijmans@topic.nl> |
| Date: Thu, 9 Nov 2017 13:16:46 +0100 |
| Subject: usb: hub: Cycle HUB power when initialization fails |
| |
| commit 973593a960ddac0f14f0d8877d2d0abe0afda795 upstream. |
| |
| Sometimes the USB device gets confused about the state of the initialization and |
| the connection fails. In particular, the device thinks that it's already set up |
| and running while the host thinks the device still needs to be configured. To |
| work around this issue, power-cycle the hub's output to issue a sort of "reset" |
| to the device. This makes the device restart its state machine and then the |
| initialization succeeds. |
| |
| This fixes problems where the kernel reports a list of errors like this: |
| |
| usb 1-1.3: device not accepting address 19, error -71 |
| |
| The end result is a non-functioning device. After this patch, the sequence |
| becomes like this: |
| |
| usb 1-1.3: new high-speed USB device number 18 using ci_hdrc |
| usb 1-1.3: device not accepting address 18, error -71 |
| usb 1-1.3: new high-speed USB device number 19 using ci_hdrc |
| usb 1-1.3: device not accepting address 19, error -71 |
| usb 1-1-port3: attempt power cycle |
| usb 1-1.3: new high-speed USB device number 21 using ci_hdrc |
| usb-storage 1-1.3:1.2: USB Mass Storage device detected |
| |
| Signed-off-by: Mike Looijmans <mike.looijmans@topic.nl> |
| Acked-by: Alan Stern <stern@rowland.harvard.edu> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/usb/core/hub.c | 9 +++++++++ |
| 1 file changed, 9 insertions(+) |
| |
| --- a/drivers/usb/core/hub.c |
| +++ b/drivers/usb/core/hub.c |
| @@ -4792,6 +4792,15 @@ loop: |
| usb_put_dev(udev); |
| if ((status == -ENOTCONN) || (status == -ENOTSUPP)) |
| break; |
| + |
| + /* When halfway through our retry count, power-cycle the port */ |
| + if (i == (SET_CONFIG_TRIES / 2) - 1) { |
| + dev_info(&port_dev->dev, "attempt power cycle\n"); |
| + usb_hub_set_port_power(hdev, hub, port1, false); |
| + msleep(2 * hub_power_on_good_delay(hub)); |
| + usb_hub_set_port_power(hdev, hub, port1, true); |
| + msleep(hub_power_on_good_delay(hub)); |
| + } |
| } |
| if (hub->hdev->parent || |
| !hcd->driver->port_handed_over || |