[PATCH 3/5] xHCI: Set link state support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds support for Set PORT_LINK_STATE request.

The most significant byte (bits 15..8) of the wIndex field specifies
the U state the host software wants to put the link connected to the
port into. This request is only valid when the PORT_ENABLE bit is set
and the PORT_LINK_STATE should not be above value '5' (Rx.Detect).

This request will be later used to replace the set/clear suspend USB3.0
protocol ports in hub driver.

Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx>
---
 drivers/usb/host/xhci-hub.c |   44 ++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 43 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 9d9965d..840aef2 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -387,6 +387,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 	u32 __iomem **port_array;
 	int slot_id;
 	struct xhci_bus_state *bus_state;
+	u16 link_state = 0;
 
 	if (hcd->speed == HCD_USB3) {
 		ports = xhci->num_usb3_ports;
@@ -497,7 +498,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 		put_unaligned(cpu_to_le32(status), (__le32 *) buf);
 		break;
 	case SetPortFeature:
-		wIndex &= 0xff;
+		if (wValue == USB_PORT_FEAT_LINK_STATE) {
+			link_state = (wIndex & 0xff00) >> 3;
+			wIndex &= 0xff;
+		}
 		if (!wIndex || wIndex > ports)
 			goto error;
 		wIndex--;
@@ -545,6 +549,44 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 			temp = xhci_readl(xhci, port_array[wIndex]);
 			bus_state->suspended_ports |= 1 << wIndex;
 			break;
+		case USB_PORT_FEAT_LINK_STATE:
+			temp = xhci_readl(xhci, port_array[wIndex]);
+			/* Software should not attempt to set
+			 * port link state above '5' (Rx.Detect) and the port
+			 * must be enabled.
+			 */
+			if ((temp & PORT_PE) == 0 ||
+				(link_state > USB_SS_PORT_LS_RX_DETECT)) {
+				xhci_warn(xhci, "Cannot set link state.\n");
+				goto error;
+			}
+
+			if (link_state == USB_SS_PORT_LS_U3) {
+				slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+						wIndex + 1);
+				if (slot_id) {
+					/* unlock to execute stop endpoint
+					 * commands */
+					spin_unlock_irqrestore(&xhci->lock,
+								flags);
+					xhci_stop_device(xhci, slot_id, 1);
+					spin_lock_irqsave(&xhci->lock, flags);
+				}
+			}
+
+			temp = xhci_port_state_to_neutral(temp);
+			temp &= ~PORT_PLS_MASK;
+			temp |= PORT_LINK_STROBE | link_state;
+			xhci_writel(xhci, temp, port_array[wIndex]);
+
+			spin_unlock_irqrestore(&xhci->lock, flags);
+			msleep(10); /* wait device to enter */
+			spin_lock_irqsave(&xhci->lock, flags);
+
+			temp = xhci_readl(xhci, port_array[wIndex]);
+			if (link_state == USB_SS_PORT_LS_U3)
+				bus_state->suspended_ports |= 1 << wIndex;
+			break;
 		case USB_PORT_FEAT_POWER:
 			/*
 			 * Turn on ports, even if there isn't per-port switching.
-- 
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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux