USB3.0 specification has different wPortStatus and wPortChange definitions from USB2.0 specification. Since USB3 root hub and USB2 root hub are split now and USB3 hub only has USB3 protocol ports, we should modify the portstatus and portchange report of USB3 ports to comply with USB3.0 specification. Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx> --- drivers/usb/core/hub.c | 48 ++++++++++++++++++++++++++++-------------- drivers/usb/host/xhci-hub.c | 23 ++++++++++++++++---- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 0968157..b1a07d6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -379,15 +379,6 @@ static int hub_port_status(struct usb_hub *hub, int port1, *status = le16_to_cpu(hub->status->port.wPortStatus); *change = le16_to_cpu(hub->status->port.wPortChange); - if ((hub->hdev->parent != NULL) && - hub_is_superspeed(hub->hdev)) { - /* Translate the USB 3 port status */ - u16 tmp = *status & USB_SS_PORT_STAT_MASK; - if (*status & USB_SS_PORT_STAT_POWER) - tmp |= USB_PORT_STAT_POWER; - *status = tmp; - } - ret = 0; } mutex_unlock(&hub->status_mutex); @@ -2163,6 +2154,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1, #define MASK_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION | \ USB_PORT_STAT_SUSPEND) #define WANT_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION) +#define MASK_SS_BITS (USB_SS_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION) /* Determine whether the device on a port is ready for a normal resume, * is ready for a reset-resume, or should be disconnected. @@ -2171,16 +2163,30 @@ static int check_port_resume_type(struct usb_device *udev, struct usb_hub *hub, int port1, int status, unsigned portchange, unsigned portstatus) { + bool lost = false; + /* Is the device still present? */ - if (status || (portstatus & MASK_BITS) != WANT_BITS) { - if (status >= 0) - status = -ENODEV; + if (hub_is_superspeed(hub->hdev)) { + if (status || (portstatus & MASK_SS_BITS) != MASK_SS_BITS || + (portstatus & USB_PORT_STAT_LINK_STATE) + == USB_SS_PORT_LS_U3) { + lost = true; + if (status >= 0) + status = -ENODEV; + } + } else { + if (status || (portstatus & MASK_BITS) != WANT_BITS) { + lost = true; + if (status >= 0) + status = -ENODEV; + } } /* Can't do a normal resume if the port isn't enabled, * so try a reset-resume instead. */ - else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume) { + if (lost && !(portstatus & USB_PORT_STAT_ENABLE) && + !udev->reset_resume) { if (udev->persist_enabled) udev->reset_resume = 1; else @@ -2427,8 +2433,14 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) /* Skip the initial Clear-Suspend step for a remote wakeup */ status = hub_port_status(hub, port1, &portstatus, &portchange); - if (status == 0 && !(portstatus & USB_PORT_STAT_SUSPEND)) - goto SuspendCleared; + if (hub_is_superspeed(hub->hdev)) { + if (status == 0 && ((portstatus & USB_PORT_STAT_LINK_STATE) + != USB_SS_PORT_LS_U3)) + goto SuspendCleared; + } else { + if (status == 0 && !(portstatus & USB_PORT_STAT_SUSPEND)) + goto SuspendCleared; + } // dev_dbg(hub->intfdev, "resume port %d\n", port1); @@ -3139,8 +3151,12 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, /* maybe switch power back on (e.g. root hub was reset) */ if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2 - && !(portstatus & USB_PORT_STAT_POWER)) + && ((hub_is_superspeed(hub->hdev) && + !(portstatus & USB_SS_PORT_STAT_POWER)) + || (!hub_is_superspeed(hub->hdev) && + !(portstatus & USB_PORT_STAT_POWER)))) { set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); + } if (portstatus & USB_PORT_STAT_ENABLE) goto done; diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 840aef2..7a918de 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -441,13 +441,19 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, status |= USB_PORT_STAT_C_ENABLE << 16; if ((temp & PORT_OCC)) status |= USB_PORT_STAT_C_OVERCURRENT << 16; + if ((temp & PORT_PLC)) + status |= USB_PORT_STAT_C_LINK_STATE << 16; + if ((temp & PORT_WRC)) + status |= USB_PORT_STAT_C_BH_RESET << 16; /* * FIXME ignoring reset and USB 2.1/3.0 specific * changes */ - if ((temp & PORT_PLS_MASK) == XDEV_U3 - && (temp & PORT_POWER)) - status |= 1 << USB_PORT_FEAT_SUSPEND; + if (hcd->speed != HCD_USB3) { + if ((temp & PORT_PLS_MASK) == XDEV_U3 + && (temp & PORT_POWER)) + status |= USB_PORT_STAT_SUSPEND; + } if ((temp & PORT_PLS_MASK) == XDEV_RESUME) { if ((temp & PORT_RESET) || !(temp & PORT_PE)) goto error; @@ -490,8 +496,15 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, status |= USB_PORT_STAT_OVERCURRENT; if (temp & PORT_RESET) status |= USB_PORT_STAT_RESET; - if (temp & PORT_POWER) - status |= USB_PORT_STAT_POWER; + if (temp & PORT_POWER) { + if (hcd->speed == HCD_USB3) + status |= USB_SS_PORT_STAT_POWER; + else + status |= USB_PORT_STAT_POWER; + } + /* Port Link State */ + if (hcd->speed == HCD_USB3) + status |= (temp & PORT_PLS_MASK); if (bus_state->port_c_suspend & (1 << wIndex)) status |= 1 << USB_PORT_FEAT_C_SUSPEND; xhci_dbg(xhci, "Get port status returned 0x%x\n", status); -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html