|
@@ -462,6 +462,42 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/* Updates Link Status for super Speed port */
|
|
|
+static void xhci_hub_report_link_state(u32 *status, u32 status_reg)
|
|
|
+{
|
|
|
+ u32 pls = status_reg & PORT_PLS_MASK;
|
|
|
+
|
|
|
+ /* resume state is a xHCI internal state.
|
|
|
+ * Do not report it to usb core.
|
|
|
+ */
|
|
|
+ if (pls == XDEV_RESUME)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* When the CAS bit is set then warm reset
|
|
|
+ * should be performed on port
|
|
|
+ */
|
|
|
+ if (status_reg & PORT_CAS) {
|
|
|
+ /* The CAS bit can be set while the port is
|
|
|
+ * in any link state.
|
|
|
+ * Only roothubs have CAS bit, so we
|
|
|
+ * pretend to be in compliance mode
|
|
|
+ * unless we're already in compliance
|
|
|
+ * or the inactive state.
|
|
|
+ */
|
|
|
+ if (pls != USB_SS_PORT_LS_COMP_MOD &&
|
|
|
+ pls != USB_SS_PORT_LS_SS_INACTIVE) {
|
|
|
+ pls = USB_SS_PORT_LS_COMP_MOD;
|
|
|
+ }
|
|
|
+ /* Return also connection bit -
|
|
|
+ * hub state machine resets port
|
|
|
+ * when this bit is set.
|
|
|
+ */
|
|
|
+ pls |= USB_PORT_STAT_CONNECTION;
|
|
|
+ }
|
|
|
+ /* update status field */
|
|
|
+ *status |= pls;
|
|
|
+}
|
|
|
+
|
|
|
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|
|
u16 wIndex, char *buf, u16 wLength)
|
|
|
{
|
|
@@ -606,13 +642,9 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|
|
else
|
|
|
status |= USB_PORT_STAT_POWER;
|
|
|
}
|
|
|
- /* Port Link State */
|
|
|
+ /* Update Port Link State for super speed ports*/
|
|
|
if (hcd->speed == HCD_USB3) {
|
|
|
- /* resume state is a xHCI internal state.
|
|
|
- * Do not report it to usb core.
|
|
|
- */
|
|
|
- if ((temp & PORT_PLS_MASK) != XDEV_RESUME)
|
|
|
- status |= (temp & PORT_PLS_MASK);
|
|
|
+ xhci_hub_report_link_state(&status, temp);
|
|
|
}
|
|
|
if (bus_state->port_c_suspend & (1 << wIndex))
|
|
|
status |= 1 << USB_PORT_FEAT_C_SUSPEND;
|