|
@@ -461,8 +461,15 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/* Updates Link Status for USB 2.1 port */
|
|
|
+static void xhci_hub_report_usb2_link_state(u32 *status, u32 status_reg)
|
|
|
+{
|
|
|
+ if ((status_reg & PORT_PLS_MASK) == XDEV_U2)
|
|
|
+ *status |= USB_PORT_STAT_L1;
|
|
|
+}
|
|
|
+
|
|
|
/* Updates Link Status for super Speed port */
|
|
|
-static void xhci_hub_report_link_state(u32 *status, u32 status_reg)
|
|
|
+static void xhci_hub_report_usb3_link_state(u32 *status, u32 status_reg)
|
|
|
{
|
|
|
u32 pls = status_reg & PORT_PLS_MASK;
|
|
|
|
|
@@ -534,6 +541,120 @@ void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Converts a raw xHCI port status into the format that external USB 2.0 or USB
|
|
|
+ * 3.0 hubs use.
|
|
|
+ *
|
|
|
+ * Possible side effects:
|
|
|
+ * - Mark a port as being done with device resume,
|
|
|
+ * and ring the endpoint doorbells.
|
|
|
+ * - Stop the Synopsys redriver Compliance Mode polling.
|
|
|
+ */
|
|
|
+static u32 xhci_get_port_status(struct usb_hcd *hcd,
|
|
|
+ struct xhci_bus_state *bus_state,
|
|
|
+ __le32 __iomem **port_array,
|
|
|
+ u16 wIndex, u32 raw_port_status)
|
|
|
+{
|
|
|
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
|
|
+ u32 status = 0;
|
|
|
+ int slot_id;
|
|
|
+
|
|
|
+ /* wPortChange bits */
|
|
|
+ if (raw_port_status & PORT_CSC)
|
|
|
+ status |= USB_PORT_STAT_C_CONNECTION << 16;
|
|
|
+ if (raw_port_status & PORT_PEC)
|
|
|
+ status |= USB_PORT_STAT_C_ENABLE << 16;
|
|
|
+ if ((raw_port_status & PORT_OCC))
|
|
|
+ status |= USB_PORT_STAT_C_OVERCURRENT << 16;
|
|
|
+ if ((raw_port_status & PORT_RC))
|
|
|
+ status |= USB_PORT_STAT_C_RESET << 16;
|
|
|
+ /* USB3.0 only */
|
|
|
+ if (hcd->speed == HCD_USB3) {
|
|
|
+ if ((raw_port_status & PORT_PLC))
|
|
|
+ status |= USB_PORT_STAT_C_LINK_STATE << 16;
|
|
|
+ if ((raw_port_status & PORT_WRC))
|
|
|
+ status |= USB_PORT_STAT_C_BH_RESET << 16;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hcd->speed != HCD_USB3) {
|
|
|
+ if ((raw_port_status & PORT_PLS_MASK) == XDEV_U3
|
|
|
+ && (raw_port_status & PORT_POWER))
|
|
|
+ status |= USB_PORT_STAT_SUSPEND;
|
|
|
+ }
|
|
|
+ if ((raw_port_status & PORT_PLS_MASK) == XDEV_RESUME &&
|
|
|
+ !DEV_SUPERSPEED(raw_port_status)) {
|
|
|
+ if ((raw_port_status & PORT_RESET) ||
|
|
|
+ !(raw_port_status & PORT_PE))
|
|
|
+ return 0xffffffff;
|
|
|
+ if (time_after_eq(jiffies,
|
|
|
+ bus_state->resume_done[wIndex])) {
|
|
|
+ xhci_dbg(xhci, "Resume USB2 port %d\n",
|
|
|
+ wIndex + 1);
|
|
|
+ bus_state->resume_done[wIndex] = 0;
|
|
|
+ clear_bit(wIndex, &bus_state->resuming_ports);
|
|
|
+ xhci_set_link_state(xhci, port_array, wIndex,
|
|
|
+ XDEV_U0);
|
|
|
+ xhci_dbg(xhci, "set port %d resume\n",
|
|
|
+ wIndex + 1);
|
|
|
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci,
|
|
|
+ wIndex + 1);
|
|
|
+ if (!slot_id) {
|
|
|
+ xhci_dbg(xhci, "slot_id is zero\n");
|
|
|
+ return 0xffffffff;
|
|
|
+ }
|
|
|
+ xhci_ring_device(xhci, slot_id);
|
|
|
+ bus_state->port_c_suspend |= 1 << wIndex;
|
|
|
+ bus_state->suspended_ports &= ~(1 << wIndex);
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * The resume has been signaling for less than
|
|
|
+ * 20ms. Report the port status as SUSPEND,
|
|
|
+ * let the usbcore check port status again
|
|
|
+ * and clear resume signaling later.
|
|
|
+ */
|
|
|
+ status |= USB_PORT_STAT_SUSPEND;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0
|
|
|
+ && (raw_port_status & PORT_POWER)
|
|
|
+ && (bus_state->suspended_ports & (1 << wIndex))) {
|
|
|
+ bus_state->suspended_ports &= ~(1 << wIndex);
|
|
|
+ if (hcd->speed != HCD_USB3)
|
|
|
+ bus_state->port_c_suspend |= 1 << wIndex;
|
|
|
+ }
|
|
|
+ if (raw_port_status & PORT_CONNECT) {
|
|
|
+ status |= USB_PORT_STAT_CONNECTION;
|
|
|
+ status |= xhci_port_speed(raw_port_status);
|
|
|
+ }
|
|
|
+ if (raw_port_status & PORT_PE)
|
|
|
+ status |= USB_PORT_STAT_ENABLE;
|
|
|
+ if (raw_port_status & PORT_OC)
|
|
|
+ status |= USB_PORT_STAT_OVERCURRENT;
|
|
|
+ if (raw_port_status & PORT_RESET)
|
|
|
+ status |= USB_PORT_STAT_RESET;
|
|
|
+ if (raw_port_status & PORT_POWER) {
|
|
|
+ if (hcd->speed == HCD_USB3)
|
|
|
+ status |= USB_SS_PORT_STAT_POWER;
|
|
|
+ else
|
|
|
+ status |= USB_PORT_STAT_POWER;
|
|
|
+ }
|
|
|
+ /* Update Port Link State */
|
|
|
+ if (hcd->speed == HCD_USB3) {
|
|
|
+ xhci_hub_report_usb3_link_state(&status, raw_port_status);
|
|
|
+ /*
|
|
|
+ * Verify if all USB3 Ports Have entered U0 already.
|
|
|
+ * Delete Compliance Mode Timer if so.
|
|
|
+ */
|
|
|
+ xhci_del_comp_mod_timer(xhci, raw_port_status, wIndex);
|
|
|
+ } else {
|
|
|
+ xhci_hub_report_usb2_link_state(&status, raw_port_status);
|
|
|
+ }
|
|
|
+ if (bus_state->port_c_suspend & (1 << wIndex))
|
|
|
+ status |= 1 << USB_PORT_FEAT_C_SUSPEND;
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|
|
u16 wIndex, char *buf, u16 wLength)
|
|
|
{
|
|
@@ -598,104 +719,20 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|
|
if (!wIndex || wIndex > max_ports)
|
|
|
goto error;
|
|
|
wIndex--;
|
|
|
- status = 0;
|
|
|
temp = xhci_readl(xhci, port_array[wIndex]);
|
|
|
if (temp == 0xffffffff) {
|
|
|
retval = -ENODEV;
|
|
|
break;
|
|
|
}
|
|
|
- xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp);
|
|
|
-
|
|
|
- /* wPortChange bits */
|
|
|
- if (temp & PORT_CSC)
|
|
|
- status |= USB_PORT_STAT_C_CONNECTION << 16;
|
|
|
- if (temp & PORT_PEC)
|
|
|
- status |= USB_PORT_STAT_C_ENABLE << 16;
|
|
|
- if ((temp & PORT_OCC))
|
|
|
- status |= USB_PORT_STAT_C_OVERCURRENT << 16;
|
|
|
- if ((temp & PORT_RC))
|
|
|
- status |= USB_PORT_STAT_C_RESET << 16;
|
|
|
- /* USB3.0 only */
|
|
|
- if (hcd->speed == HCD_USB3) {
|
|
|
- if ((temp & PORT_PLC))
|
|
|
- status |= USB_PORT_STAT_C_LINK_STATE << 16;
|
|
|
- if ((temp & PORT_WRC))
|
|
|
- status |= USB_PORT_STAT_C_BH_RESET << 16;
|
|
|
- }
|
|
|
+ status = xhci_get_port_status(hcd, bus_state, port_array,
|
|
|
+ wIndex, temp);
|
|
|
+ if (status == 0xffffffff)
|
|
|
+ goto error;
|
|
|
|
|
|
- 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 &&
|
|
|
- !DEV_SUPERSPEED(temp)) {
|
|
|
- if ((temp & PORT_RESET) || !(temp & PORT_PE))
|
|
|
- goto error;
|
|
|
- if (time_after_eq(jiffies,
|
|
|
- bus_state->resume_done[wIndex])) {
|
|
|
- xhci_dbg(xhci, "Resume USB2 port %d\n",
|
|
|
- wIndex + 1);
|
|
|
- bus_state->resume_done[wIndex] = 0;
|
|
|
- clear_bit(wIndex, &bus_state->resuming_ports);
|
|
|
- xhci_set_link_state(xhci, port_array, wIndex,
|
|
|
- XDEV_U0);
|
|
|
- xhci_dbg(xhci, "set port %d resume\n",
|
|
|
- wIndex + 1);
|
|
|
- slot_id = xhci_find_slot_id_by_port(hcd, xhci,
|
|
|
- wIndex + 1);
|
|
|
- if (!slot_id) {
|
|
|
- xhci_dbg(xhci, "slot_id is zero\n");
|
|
|
- goto error;
|
|
|
- }
|
|
|
- xhci_ring_device(xhci, slot_id);
|
|
|
- bus_state->port_c_suspend |= 1 << wIndex;
|
|
|
- bus_state->suspended_ports &= ~(1 << wIndex);
|
|
|
- } else {
|
|
|
- /*
|
|
|
- * The resume has been signaling for less than
|
|
|
- * 20ms. Report the port status as SUSPEND,
|
|
|
- * let the usbcore check port status again
|
|
|
- * and clear resume signaling later.
|
|
|
- */
|
|
|
- status |= USB_PORT_STAT_SUSPEND;
|
|
|
- }
|
|
|
- }
|
|
|
- if ((temp & PORT_PLS_MASK) == XDEV_U0
|
|
|
- && (temp & PORT_POWER)
|
|
|
- && (bus_state->suspended_ports & (1 << wIndex))) {
|
|
|
- bus_state->suspended_ports &= ~(1 << wIndex);
|
|
|
- if (hcd->speed != HCD_USB3)
|
|
|
- bus_state->port_c_suspend |= 1 << wIndex;
|
|
|
- }
|
|
|
- if (temp & PORT_CONNECT) {
|
|
|
- status |= USB_PORT_STAT_CONNECTION;
|
|
|
- status |= xhci_port_speed(temp);
|
|
|
- }
|
|
|
- if (temp & PORT_PE)
|
|
|
- status |= USB_PORT_STAT_ENABLE;
|
|
|
- if (temp & PORT_OC)
|
|
|
- status |= USB_PORT_STAT_OVERCURRENT;
|
|
|
- if (temp & PORT_RESET)
|
|
|
- status |= USB_PORT_STAT_RESET;
|
|
|
- if (temp & PORT_POWER) {
|
|
|
- if (hcd->speed == HCD_USB3)
|
|
|
- status |= USB_SS_PORT_STAT_POWER;
|
|
|
- else
|
|
|
- status |= USB_PORT_STAT_POWER;
|
|
|
- }
|
|
|
- /* Update Port Link State for super speed ports*/
|
|
|
- if (hcd->speed == HCD_USB3) {
|
|
|
- xhci_hub_report_link_state(&status, temp);
|
|
|
- /*
|
|
|
- * Verify if all USB3 Ports Have entered U0 already.
|
|
|
- * Delete Compliance Mode Timer if so.
|
|
|
- */
|
|
|
- xhci_del_comp_mod_timer(xhci, temp, wIndex);
|
|
|
- }
|
|
|
- if (bus_state->port_c_suspend & (1 << wIndex))
|
|
|
- status |= 1 << USB_PORT_FEAT_C_SUSPEND;
|
|
|
+ xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n",
|
|
|
+ wIndex, temp);
|
|
|
xhci_dbg(xhci, "Get port status returned 0x%x\n", status);
|
|
|
+
|
|
|
put_unaligned(cpu_to_le32(status), (__le32 *) buf);
|
|
|
break;
|
|
|
case SetPortFeature:
|