/*
* xHCI host controller driver
*
* Copyright (C) 2008 Intel Corp.
*
* Author: Sarah Sharp
* Some code borrowed from the Linux EHCI driver.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/gfp.h>
#include <asm/unaligned.h>
#include "xhci.h"
#define PORT_WAKE_BITS (PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E)
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \
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,
struct usb_hub_descriptor *desc, int ports)
{
u16 temp;
desc->bPwrOn2PwrGood = 10; /* xhci section 5.4.9 says 20ms max */
desc->bHubContrCurrent = 0;
desc->bNbrPorts = ports;
temp = 0;
/* Bits 1:0 - support per-port power switching, or power always on */
if (HCC_PPC(xhci->hcc_params))
temp |= HUB_CHAR_INDV_PORT_LPSM;
else
temp |= HUB_CHAR_NO_LPSM;
/* Bit 2 - root hubs are not part of a compound device */
/* Bits 4:3 - individual port over current protection */
temp |= HUB_CHAR_INDV_PORT_OCPM;
/* Bits 6:5 - no TTs in root ports */
/* Bit 7 - no port indicators */
desc->wHubCharacteristics = cpu_to_le16(temp);
}
/* Fill in the USB 2.0 roothub descriptor */
static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
struct usb_hub_descriptor *desc)
{
int ports;
u16 temp;
__u8 port_removable[(USB_MAXCHILDREN + 1 + 7) / 8];
u32 portsc;
unsigned int i;
ports = xhci->num_usb2_ports;
xhci_common_hub_descriptor(xhci, desc, ports);
desc->bDescriptorType = USB_DT_HUB;
temp = 1 + (ports / 8);
desc->bDescLength = USB_DT_HUB_NONVAR_SIZE + 2 * temp;
/* The Device Removable bits are reported on a byte granularity.
* If the port doesn't exist within that byte, the bit is set to 0.
*/
memset(port_removable, 0, sizeof(port_removable));
for (i = 0; i < ports; i++) {
portsc = xhci_readl(xhci, xhci->usb2_ports[i]);
/* If a device is removable, PORTSC reports a 0, same as in the
* hub descriptor DeviceRemovable bits.
*/
if (portsc & PORT_DEV_REMOVE)
/* This math is hairy because bit 0 of DeviceRemovable
* is reserved, and bit 1 is for port 1, etc.
*/
port_removable[(i + 1) / 8] |= 1 << ((i + 1) % 8);
}
/* ch11.h defines a hub descriptor that has room for USB_MAXCHILDREN
* ports on it. The USB 2.0 specification says that there are two
* variable length fields at the end of the hub descriptor:
* DeviceRemovable and PortPwrCtrlMask. But since we can have less than
* USB_MAXCHILDREN ports, we may need to use the DeviceRemovable array
* to set PortPwrCtrlMask bits. PortPwrCtrlMask must always be set to
* 0xFF, so we initialize the both arrays (DeviceRemovable and
* PortPwrCtrlMask) to 0xFF. Then we set the DeviceRemovable for each
* set of ports that actually exist.
*/
memset(desc->u.hs.DeviceRemovable, 0xff,
sizeof(desc->u.hs.DeviceRemovable));
memset(desc->u.hs.PortPwrCtrlMask, 0xff,
sizeof(desc->u.hs.PortPwrCtrlMask));
for (i = 0; i < (ports + 1 + 7) / 8; i++)
memset(&desc->u.hs.DeviceRemovable[i], port_removable[i],
sizeof(__u8));
}
/* Fill in the USB 3.0 roothub descriptor */