|
@@ -422,6 +422,32 @@ void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
|
|
|
xhci_writel(xhci, temp, port_array[port_id]);
|
|
|
}
|
|
|
|
|
|
+void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
|
|
|
+ __le32 __iomem **port_array, int port_id, u16 wake_mask)
|
|
|
+{
|
|
|
+ u32 temp;
|
|
|
+
|
|
|
+ temp = xhci_readl(xhci, port_array[port_id]);
|
|
|
+ temp = xhci_port_state_to_neutral(temp);
|
|
|
+
|
|
|
+ if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT)
|
|
|
+ temp |= PORT_WKCONN_E;
|
|
|
+ else
|
|
|
+ temp &= ~PORT_WKCONN_E;
|
|
|
+
|
|
|
+ if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT)
|
|
|
+ temp |= PORT_WKDISC_E;
|
|
|
+ else
|
|
|
+ temp &= ~PORT_WKDISC_E;
|
|
|
+
|
|
|
+ if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT)
|
|
|
+ temp |= PORT_WKOC_E;
|
|
|
+ else
|
|
|
+ temp &= ~PORT_WKOC_E;
|
|
|
+
|
|
|
+ xhci_writel(xhci, temp, port_array[port_id]);
|
|
|
+}
|
|
|
+
|
|
|
/* Test and clear port RWC bit */
|
|
|
void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
|
|
|
int port_id, u32 port_bit)
|
|
@@ -448,6 +474,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|
|
int slot_id;
|
|
|
struct xhci_bus_state *bus_state;
|
|
|
u16 link_state = 0;
|
|
|
+ u16 wake_mask = 0;
|
|
|
|
|
|
max_ports = xhci_get_ports(hcd, &port_array);
|
|
|
bus_state = &xhci->bus_state[hcd_index(hcd)];
|
|
@@ -593,6 +620,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|
|
case SetPortFeature:
|
|
|
if (wValue == USB_PORT_FEAT_LINK_STATE)
|
|
|
link_state = (wIndex & 0xff00) >> 3;
|
|
|
+ if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK)
|
|
|
+ wake_mask = wIndex & 0xff00;
|
|
|
wIndex &= 0xff;
|
|
|
if (!wIndex || wIndex > max_ports)
|
|
|
goto error;
|
|
@@ -703,6 +732,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|
|
temp = xhci_readl(xhci, port_array[wIndex]);
|
|
|
xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp);
|
|
|
break;
|
|
|
+ case USB_PORT_FEAT_REMOTE_WAKE_MASK:
|
|
|
+ xhci_set_remote_wake_mask(xhci, port_array,
|
|
|
+ wIndex, wake_mask);
|
|
|
+ temp = xhci_readl(xhci, port_array[wIndex]);
|
|
|
+ xhci_dbg(xhci, "set port remote wake mask, "
|
|
|
+ "actual port %d status = 0x%x\n",
|
|
|
+ wIndex, temp);
|
|
|
+ break;
|
|
|
case USB_PORT_FEAT_BH_PORT_RESET:
|
|
|
temp |= PORT_WR;
|
|
|
xhci_writel(xhci, temp, port_array[wIndex]);
|
|
@@ -883,6 +920,10 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
|
|
|
t2 |= PORT_LINK_STROBE | XDEV_U3;
|
|
|
set_bit(port_index, &bus_state->bus_suspended);
|
|
|
}
|
|
|
+ /* USB core sets remote wake mask for USB 3.0 hubs,
|
|
|
+ * including the USB 3.0 roothub, but only if CONFIG_USB_SUSPEND
|
|
|
+ * is enabled, so also enable remote wake here.
|
|
|
+ */
|
|
|
if (hcd->self.root_hub->do_remote_wakeup) {
|
|
|
if (t1 & PORT_CONNECT) {
|
|
|
t2 |= PORT_WKOC_E | PORT_WKDISC_E;
|