diff options
Diffstat (limited to 'drivers/usb/host/xhci-hub.c')
| -rw-r--r-- | drivers/usb/host/xhci-hub.c | 41 | 
1 files changed, 41 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 557b6f32db8..673ad120c43 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -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;  |