Commit 48e82361 authored by Sarah Sharp's avatar Sarah Sharp Committed by Greg Kroah-Hartman

xHCI/USB: Make xHCI driver have a BOS descriptor.

To add USB 3.0 link power management (LPM), we need to know what the U1
and U2 exit latencies are for the xHCI host controller.  External USB 3.0
hubs report these values through the SuperSpeed Capabilities descriptor in
the BOS descriptor.  Make the USB 3.0 roothub for the xHCI host behave
like an external hub and return the BOS descriptors.

The U1 and U2 exit latencies will vary across each host controller, so we
need to dynamically fill those values in by reading the exit latencies out
of the xHC registers.  Make the roothub code in the USB core handle
hub_control() returning the length of the data copied.
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent fa3ae0c1
...@@ -442,7 +442,11 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) ...@@ -442,7 +442,11 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
struct usb_ctrlrequest *cmd; struct usb_ctrlrequest *cmd;
u16 typeReq, wValue, wIndex, wLength; u16 typeReq, wValue, wIndex, wLength;
u8 *ubuf = urb->transfer_buffer; u8 *ubuf = urb->transfer_buffer;
u8 tbuf [sizeof (struct usb_hub_descriptor)] /*
* tbuf should be as big as the BOS descriptor and
* the USB hub descriptor.
*/
u8 tbuf[USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE]
__attribute__((aligned(4))); __attribute__((aligned(4)));
const u8 *bufp = tbuf; const u8 *bufp = tbuf;
unsigned len = 0; unsigned len = 0;
...@@ -562,6 +566,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) ...@@ -562,6 +566,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
else /* unsupported IDs --> "protocol stall" */ else /* unsupported IDs --> "protocol stall" */
goto error; goto error;
break; break;
case USB_DT_BOS << 8:
goto nongeneric;
default: default:
goto error; goto error;
} }
...@@ -596,6 +602,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) ...@@ -596,6 +602,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
/* CLASS REQUESTS (and errors) */ /* CLASS REQUESTS (and errors) */
default: default:
nongeneric:
/* non-generic request */ /* non-generic request */
switch (typeReq) { switch (typeReq) {
case GetHubStatus: case GetHubStatus:
...@@ -605,6 +612,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) ...@@ -605,6 +612,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
case GetHubDescriptor: case GetHubDescriptor:
len = sizeof (struct usb_hub_descriptor); len = sizeof (struct usb_hub_descriptor);
break; break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
/* len is returned by hub_control */
break;
} }
status = hcd->driver->hub_control (hcd, status = hcd->driver->hub_control (hcd,
typeReq, wValue, wIndex, typeReq, wValue, wIndex,
...@@ -615,7 +625,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) ...@@ -615,7 +625,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
status = -EPIPE; status = -EPIPE;
} }
if (status) { if (status < 0) {
len = 0; len = 0;
if (status != -EPIPE) { if (status != -EPIPE) {
dev_dbg (hcd->self.controller, dev_dbg (hcd->self.controller,
...@@ -624,6 +634,10 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) ...@@ -624,6 +634,10 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
typeReq, wValue, wIndex, typeReq, wValue, wIndex,
wLength, status); wLength, status);
} }
} else if (status > 0) {
/* hub_control may return the length of data copied. */
len = status;
status = 0;
} }
if (len) { if (len) {
if (urb->transfer_buffer_length < len) if (urb->transfer_buffer_length < len)
......
...@@ -28,6 +28,25 @@ ...@@ -28,6 +28,25 @@
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \
PORT_RC | PORT_PLC | PORT_PE) PORT_RC | PORT_PLC | PORT_PE)
/* usb 1.1 root hub device descriptor */
static u8 usb_bos_descriptor [] = {
USB_DT_BOS_SIZE, /* __u8 bLength, 5 bytes */
USB_DT_BOS, /* __u8 bDescriptorType */
0x0F, 0x00, /* __le16 wTotalLength, 15 bytes */
0x1, /* __u8 bNumDeviceCaps */
/* First device capability */
USB_DT_USB_SS_CAP_SIZE, /* __u8 bLength, 10 bytes */
USB_DT_DEVICE_CAPABILITY, /* Device Capability */
USB_SS_CAP_TYPE, /* bDevCapabilityType, SUPERSPEED_USB */
0x00, /* bmAttributes, LTM off by default */
USB_5GBPS_OPERATION, 0x00, /* wSpeedsSupported, 5Gbps only */
0x03, /* bFunctionalitySupport,
USB 3.0 speed only */
0x00, /* bU1DevExitLat, set later. */
0x00, 0x00 /* __le16 bU2DevExitLat, set later. */
};
static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, static void xhci_common_hub_descriptor(struct xhci_hcd *xhci,
struct usb_hub_descriptor *desc, int ports) struct usb_hub_descriptor *desc, int ports)
{ {
...@@ -455,6 +474,21 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ...@@ -455,6 +474,21 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_hub_descriptor(hcd, xhci, xhci_hub_descriptor(hcd, xhci,
(struct usb_hub_descriptor *) buf); (struct usb_hub_descriptor *) buf);
break; break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
if ((wValue & 0xff00) != (USB_DT_BOS << 8))
goto error;
if (hcd->speed != HCD_USB3)
goto error;
memcpy(buf, &usb_bos_descriptor,
USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE);
temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
buf[12] = HCS_U1_LATENCY(temp);
put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]);
spin_unlock_irqrestore(&xhci->lock, flags);
return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE;
case GetPortStatus: case GetPortStatus:
if (!wIndex || wIndex > max_ports) if (!wIndex || wIndex > max_ports)
goto error; goto error;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment