diff options
author | Andiry Xu <andiry.xu@amd.com> | 2011-04-27 18:07:39 +0800 |
---|---|---|
committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2011-05-02 16:42:52 -0700 |
commit | 2c44178032b046c4113c40d0d459a0d36e39b920 (patch) | |
tree | 998a4e5d9594b9d2da6f5cc35af4ac8be50c5171 /drivers/usb/host/xhci-hub.c | |
parent | 85387c0ea3e1cd85ad9d7215917ff5e71ca2aea3 (diff) | |
download | op-kernel-dev-2c44178032b046c4113c40d0d459a0d36e39b920.zip op-kernel-dev-2c44178032b046c4113c40d0d459a0d36e39b920.tar.gz |
xHCI: Set link state support
This patch adds support for Set Port Feature(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
protocol ports in hub driver.
Signed-off-by: Andiry Xu <andiry.xu@amd.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
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 f3bafba..b875354 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, __le32 __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,6 +498,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, put_unaligned(cpu_to_le32(status), (__le32 *) buf); break; case SetPortFeature: + if (wValue == USB_PORT_FEAT_LINK_STATE) + link_state = (wIndex & 0xff00) >> 3; wIndex &= 0xff; if (!wIndex || wIndex > ports) goto error; @@ -545,6 +548,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(20); /* 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. |