From da13051cc756756f10b2da8ea97b05bdf84bd7bb Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 30 Nov 2010 15:55:51 -0800 Subject: USB: Remove bitmap #define from hcd.h Using a #define to redefine a common variable name is a bad thing, especially when the #define is in a header. include/linux/usb/hcd.h redefined bitmap to DeviceRemovable to avoid typing a long field in the hub descriptor. This has unintended side effects for files like drivers/usb/core/devio.c that include that file, since another header included after hcd.h has different variables named bitmap. Remove the bitmap #define and replace instances of it in the host controller code. Cleanup the spaces around function calls and square brackets while we're at it. Signed-off-by: Sarah Sharp Cc: Nobuhiro Iwamatsu Cc: Inaky Perez-Gonzalez Cc: Tony Olech Cc: "Robert P. J. Day" Cc: Max Vozeler Cc: Tejun Heo Cc: Yoshihiro Shimoda Cc: Rodolfo Giometti Cc: Mike Frysinger Cc: Anton Vorontsov Cc: Sebastian Siewior Cc: Lothar Wassmann Cc: Olav Kongas Cc: Martin Fuzzey Cc: Alan Stern Cc: David Brownell --- drivers/usb/host/xhci-hub.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb/host/xhci-hub.c') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 5d963e3..d83c27b 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -45,7 +45,6 @@ static void xhci_hub_descriptor(struct xhci_hcd *xhci, temp = 1 + (ports / 8); desc->bDescLength = 7 + 2 * temp; - /* Why does core/hcd.h define bitmap? It's just confusing. */ memset(&desc->DeviceRemovable[0], 0, temp); memset(&desc->DeviceRemovable[temp], 0xff, temp); -- cgit v1.1 From 1d5810b6923c76fc95e52d9d3491c91824c2f075 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 9 Dec 2010 14:52:41 -0800 Subject: xhci: Rework port suspend structures for limited ports. The USB core only allows up to 31 (USB_MAXCHILDREN) ports under a roothub. The xHCI driver keeps track of which ports are suspended, which ports have a suspend change bit set, and what time the port will be done resuming. It keeps track of the first two by setting a bit in a u32 variable, suspended_ports or port_c_suspend. The xHCI driver currently assumes we can have up to 256 ports under a roothub, so it allocates an array of 8 u32 variables for both suspended_ports and port_c_suspend. It also allocates a 256-element array to keep track of when the ports will be done resuming. Since we can only have 31 roothub ports, we only need to use one u32 for each of the suspend state and change variables. We simplify the bit math that's trying to index into those arrays and set the correct bit, if we assume wIndex never exceeds 30. (wIndex is zero-based after it's decremented from the value passed in from the USB core.) Finally, we change the resume_done array to only hold 31 elements. Signed-off-by: Sarah Sharp Cc: Andiry Xu --- drivers/usb/host/xhci-hub.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) (limited to 'drivers/usb/host/xhci-hub.c') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index d83c27b..50e250c 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -347,20 +347,15 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; } xhci_ring_device(xhci, slot_id); - xhci->port_c_suspend[wIndex >> 5] |= - 1 << (wIndex & 31); - xhci->suspended_ports[wIndex >> 5] &= - ~(1 << (wIndex & 31)); + xhci->port_c_suspend |= 1 << wIndex; + xhci->suspended_ports &= ~(1 << wIndex); } } if ((temp & PORT_PLS_MASK) == XDEV_U0 && (temp & PORT_POWER) - && (xhci->suspended_ports[wIndex >> 5] & - (1 << (wIndex & 31)))) { - xhci->suspended_ports[wIndex >> 5] &= - ~(1 << (wIndex & 31)); - xhci->port_c_suspend[wIndex >> 5] |= - 1 << (wIndex & 31); + && (xhci->suspended_ports & (1 << wIndex))) { + xhci->suspended_ports &= ~(1 << wIndex); + xhci->port_c_suspend |= 1 << wIndex; } if (temp & PORT_CONNECT) { status |= USB_PORT_STAT_CONNECTION; @@ -374,7 +369,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, status |= USB_PORT_STAT_RESET; if (temp & PORT_POWER) status |= USB_PORT_STAT_POWER; - if (xhci->port_c_suspend[wIndex >> 5] & (1 << (wIndex & 31))) + if (xhci->port_c_suspend & (1 << wIndex)) status |= 1 << USB_PORT_FEAT_C_SUSPEND; xhci_dbg(xhci, "Get port status returned 0x%x\n", status); put_unaligned(cpu_to_le32(status), (__le32 *) buf); @@ -421,8 +416,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, spin_lock_irqsave(&xhci->lock, flags); temp = xhci_readl(xhci, addr); - xhci->suspended_ports[wIndex >> 5] |= - 1 << (wIndex & (31)); + xhci->suspended_ports |= 1 << wIndex; break; case USB_PORT_FEAT_POWER: /* @@ -489,8 +483,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp |= PORT_LINK_STROBE | XDEV_U0; xhci_writel(xhci, temp, addr); } - xhci->port_c_suspend[wIndex >> 5] |= - 1 << (wIndex & 31); + xhci->port_c_suspend |= 1 << wIndex; } slot_id = xhci_find_slot_id_by_port(xhci, wIndex + 1); @@ -501,8 +494,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_ring_device(xhci, slot_id); break; case USB_PORT_FEAT_C_SUSPEND: - xhci->port_c_suspend[wIndex >> 5] &= - ~(1 << (wIndex & 31)); + xhci->port_c_suspend &= ~(1 << wIndex); case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_OVER_CURRENT: @@ -560,7 +552,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) NUM_PORT_REGS*i; temp = xhci_readl(xhci, addr); if ((temp & mask) != 0 || - (xhci->port_c_suspend[i >> 5] & 1 << (i & 31)) || + (xhci->port_c_suspend & 1 << i) || (xhci->resume_done[i] && time_after_eq( jiffies, xhci->resume_done[i]))) { buf[(i + 1) / 8] |= 1 << (i + 1) % 8; -- cgit v1.1 From 518e848ea8e2932ce18ce2daef8ad5f55a145f27 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 15 Dec 2010 11:56:29 -0800 Subject: xhci: Rename variables and reduce register reads. The xhci_bus_suspend() and xhci_bus_resume() functions are a bit hard to read, because they have an ambiguously named variable "port". Rename it to "port_index". Introduce a new temporary variable, "max_ports" that holds the maximum number of roothub ports the host controller supports. This will reduce the number of register reads, and make it easy to change the maximum number of ports when there are two roothubs. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) (limited to 'drivers/usb/host/xhci-hub.c') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 50e250c..004a465 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -568,42 +568,44 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) int xhci_bus_suspend(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); - int port; + int max_ports, port_index; unsigned long flags; xhci_dbg(xhci, "suspend root hub\n"); + max_ports = HCS_MAX_PORTS(xhci->hcs_params1); spin_lock_irqsave(&xhci->lock, flags); if (hcd->self.root_hub->do_remote_wakeup) { - port = HCS_MAX_PORTS(xhci->hcs_params1); - while (port--) { - if (xhci->resume_done[port] != 0) { + port_index = max_ports; + while (port_index--) { + if (xhci->resume_done[port_index] != 0) { spin_unlock_irqrestore(&xhci->lock, flags); xhci_dbg(xhci, "suspend failed because " "port %d is resuming\n", - port + 1); + port_index + 1); return -EBUSY; } } } - port = HCS_MAX_PORTS(xhci->hcs_params1); + port_index = max_ports; xhci->bus_suspended = 0; - while (port--) { + while (port_index--) { /* suspend the port if the port is not suspended */ u32 __iomem *addr; u32 t1, t2; int slot_id; addr = &xhci->op_regs->port_status_base + - NUM_PORT_REGS * (port & 0xff); + NUM_PORT_REGS * (port_index & 0xff); t1 = xhci_readl(xhci, addr); t2 = xhci_port_state_to_neutral(t1); if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) { - xhci_dbg(xhci, "port %d not suspended\n", port); - slot_id = xhci_find_slot_id_by_port(xhci, port + 1); + xhci_dbg(xhci, "port %d not suspended\n", port_index); + slot_id = xhci_find_slot_id_by_port(xhci, + port_index + 1); if (slot_id) { spin_unlock_irqrestore(&xhci->lock, flags); xhci_stop_device(xhci, slot_id, 1); @@ -611,7 +613,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) } t2 &= ~PORT_PLS_MASK; t2 |= PORT_LINK_STROBE | XDEV_U3; - set_bit(port, &xhci->bus_suspended); + set_bit(port_index, &xhci->bus_suspended); } if (hcd->self.root_hub->do_remote_wakeup) { if (t1 & PORT_CONNECT) { @@ -634,7 +636,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) u32 tmp; addr = &xhci->op_regs->port_power_base + - NUM_PORT_REGS * (port & 0xff); + NUM_PORT_REGS * (port_index & 0xff); tmp = xhci_readl(xhci, addr); tmp |= PORT_RWE; xhci_writel(xhci, tmp, addr); @@ -649,11 +651,12 @@ int xhci_bus_suspend(struct usb_hcd *hcd) int xhci_bus_resume(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); - int port; + int max_ports, port_index; u32 temp; unsigned long flags; xhci_dbg(xhci, "resume root hub\n"); + max_ports = HCS_MAX_PORTS(xhci->hcs_params1); if (time_before(jiffies, xhci->next_statechange)) msleep(5); @@ -669,8 +672,8 @@ int xhci_bus_resume(struct usb_hcd *hcd) temp &= ~CMD_EIE; xhci_writel(xhci, temp, &xhci->op_regs->command); - port = HCS_MAX_PORTS(xhci->hcs_params1); - while (port--) { + port_index = max_ports; + while (port_index--) { /* Check whether need resume ports. If needed resume port and disable remote wakeup */ u32 __iomem *addr; @@ -678,13 +681,13 @@ int xhci_bus_resume(struct usb_hcd *hcd) int slot_id; addr = &xhci->op_regs->port_status_base + - NUM_PORT_REGS * (port & 0xff); + NUM_PORT_REGS * (port_index & 0xff); temp = xhci_readl(xhci, addr); if (DEV_SUPERSPEED(temp)) temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); else temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); - if (test_bit(port, &xhci->bus_suspended) && + if (test_bit(port_index, &xhci->bus_suspended) && (temp & PORT_PLS_MASK)) { if (DEV_SUPERSPEED(temp)) { temp = xhci_port_state_to_neutral(temp); @@ -707,7 +710,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) temp |= PORT_LINK_STROBE | XDEV_U0; xhci_writel(xhci, temp, addr); } - slot_id = xhci_find_slot_id_by_port(xhci, port + 1); + slot_id = xhci_find_slot_id_by_port(xhci, port_index + 1); if (slot_id) xhci_ring_device(xhci, slot_id); } else @@ -719,7 +722,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) u32 tmp; addr = &xhci->op_regs->port_power_base + - NUM_PORT_REGS * (port & 0xff); + NUM_PORT_REGS * (port_index & 0xff); tmp = xhci_readl(xhci, addr); tmp &= ~PORT_RWE; xhci_writel(xhci, tmp, addr); -- cgit v1.1 From ad73dff32e04cad1ff2af89512bf489224b503cc Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 11 Mar 2011 13:49:55 -0800 Subject: xhci: Remove references to HC_STATE_RUNNING. The USB core will set hcd->state to HC_STATE_RUNNING before calling xhci_run, so there's no point in setting it twice. The USB core also doesn't pay attention to HC_STATE_RUNNING on the resume path anymore; it uses HCD_RH_RUNNING(), which looks at hcd->flags & (1U << HCD_FLAG_RH_RUNNING. Therefore, it's safe to remove the state set in xhci_bus_resume(). Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb/host/xhci-hub.c') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 004a465..43e0a09 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -732,7 +732,6 @@ int xhci_bus_resume(struct usb_hcd *hcd) (void) xhci_readl(xhci, &xhci->op_regs->command); xhci->next_statechange = jiffies + msecs_to_jiffies(5); - hcd->state = HC_STATE_RUNNING; /* re-enable irqs */ temp = xhci_readl(xhci, &xhci->op_regs->command); temp |= CMD_EIE; -- cgit v1.1 From dbe79bbe9dcb22cb3651c46f18943477141ca452 Mon Sep 17 00:00:00 2001 From: John Youn Date: Mon, 17 Sep 2001 00:00:00 -0700 Subject: USB 3.0 Hub Changes Update the USB core to deal with USB 3.0 hubs. These hubs have a slightly different hub descriptor than USB 2.0 hubs, with a fixed (rather than variable length) size. Change the USB core's hub descriptor to have a union for the last fields that differ. Change the host controller drivers that access those last fields (DeviceRemovable and PortPowerCtrlMask) to use the union. Translate the new version of the hub port status field into the old version that khubd understands. (Note: we need to fix it to translate the roothub's port status once we stop converting it to USB 2.0 hub status internally.) Add new code to handle link state change status. Send out new control messages that are needed for USB 3.0 hubs, like Set Hub Depth. This patch is a modified version of the original patch submitted by John Youn. It's updated to reflect the removal of the "bitmap" #define, and change the hub descriptor accesses of a couple new host controller drivers. Signed-off-by: John Youn Signed-off-by: Sarah Sharp Cc: Nobuhiro Iwamatsu Cc: Inaky Perez-Gonzalez Cc: Tony Olech Cc: "Robert P. J. Day" Cc: Max Vozeler Cc: Tejun Heo Cc: Yoshihiro Shimoda Cc: Rodolfo Giometti Cc: Mike Frysinger Cc: Anton Vorontsov Cc: Sebastian Siewior Cc: Lothar Wassmann Cc: Olav Kongas Cc: Martin Fuzzey Cc: Alan Stern Cc: David Brownell --- drivers/usb/host/xhci-hub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb/host/xhci-hub.c') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 43e0a09..847b071 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -45,8 +45,8 @@ static void xhci_hub_descriptor(struct xhci_hcd *xhci, temp = 1 + (ports / 8); desc->bDescLength = 7 + 2 * temp; - memset(&desc->DeviceRemovable[0], 0, temp); - memset(&desc->DeviceRemovable[temp], 0xff, temp); + memset(&desc->u.hs.DeviceRemovable[0], 0, temp); + memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); /* Ugh, these should be #defines, FIXME */ /* Using table 11-13 in USB 2.0 spec. */ -- cgit v1.1 From 5308a91b9fc1a8f94b860c2589b06908a97cba7e Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 1 Dec 2010 11:34:59 -0800 Subject: xhci: Index with a port array instead of PORTSC addresses. In the upcoming patches, the roothub emulation code will need to return port status and port change buffers based on whether they are called with the xHCI USB 2.0 or USB 3.0 roothub. To facilitate that, make the roothub code index into an array of port addresses with wIndex, rather than calculating the address using the offset and the address of the PORTSC registers. Later we can set the port array to be the array of USB 3.0 port addresses, or the USB 2.0 port addresses, depending on the roothub passed in. Create a temporary (statically sized) port array and fill it in with both USB 3.0 and USB 2.0 port addresses. This is inefficient to do for every roothub call, but this is needed for git bisect compatibility. The temporary port array will be deleted in a subsequent patch. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 126 +++++++++++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 47 deletions(-) (limited to 'drivers/usb/host/xhci-hub.c') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 847b071..89ff025 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -288,10 +288,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, unsigned long flags; u32 temp, temp1, status; int retval = 0; - u32 __iomem *addr; + u32 __iomem *port_array[15 + USB_MAXCHILDREN]; + int i; int slot_id; ports = HCS_MAX_PORTS(xhci->hcs_params1); + for (i = 0; i < ports; i++) { + if (i < xhci->num_usb3_ports) + port_array[i] = xhci->usb3_ports[i]; + else + port_array[i] = + xhci->usb2_ports[i - xhci->num_usb3_ports]; + } spin_lock_irqsave(&xhci->lock, flags); switch (typeReq) { @@ -307,8 +315,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; wIndex--; status = 0; - addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff); - temp = xhci_readl(xhci, addr); + temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp); /* wPortChange bits */ @@ -336,7 +343,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp1 = xhci_port_state_to_neutral(temp); temp1 &= ~PORT_PLS_MASK; temp1 |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp1, addr); + xhci_writel(xhci, temp1, port_array[wIndex]); xhci_dbg(xhci, "set port %d resume\n", wIndex + 1); @@ -379,12 +386,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (!wIndex || wIndex > ports) goto error; wIndex--; - addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff); - temp = xhci_readl(xhci, addr); + temp = xhci_readl(xhci, port_array[wIndex]); temp = xhci_port_state_to_neutral(temp); switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = xhci_readl(xhci, addr); + temp = xhci_readl(xhci, port_array[wIndex]); /* In spec software should not attempt to suspend * a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. @@ -409,13 +415,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_port_state_to_neutral(temp); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | XDEV_U3; - xhci_writel(xhci, temp, addr); + 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, addr); + temp = xhci_readl(xhci, port_array[wIndex]); xhci->suspended_ports |= 1 << wIndex; break; case USB_PORT_FEAT_POWER: @@ -425,34 +431,34 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * However, khubd will ignore the roothub events until * the roothub is registered. */ - xhci_writel(xhci, temp | PORT_POWER, addr); + xhci_writel(xhci, temp | PORT_POWER, + port_array[wIndex]); - temp = xhci_readl(xhci, addr); + temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp); break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); - xhci_writel(xhci, temp, addr); + xhci_writel(xhci, temp, port_array[wIndex]); - temp = xhci_readl(xhci, addr); + temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp); break; default: goto error; } - temp = xhci_readl(xhci, addr); /* unblock any posted writes */ + /* unblock any posted writes */ + temp = xhci_readl(xhci, port_array[wIndex]); break; case ClearPortFeature: if (!wIndex || wIndex > ports) goto error; wIndex--; - addr = &xhci->op_regs->port_status_base + - NUM_PORT_REGS*(wIndex & 0xff); - temp = xhci_readl(xhci, addr); + temp = xhci_readl(xhci, port_array[wIndex]); temp = xhci_port_state_to_neutral(temp); switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = xhci_readl(xhci, addr); + temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n"); xhci_dbg(xhci, "PORTSC %04x\n", temp); if (temp & PORT_RESET) @@ -464,24 +470,28 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_port_state_to_neutral(temp); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, addr); - xhci_readl(xhci, addr); + xhci_writel(xhci, temp, + port_array[wIndex]); + xhci_readl(xhci, port_array[wIndex]); } else { temp = xhci_port_state_to_neutral(temp); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | XDEV_RESUME; - xhci_writel(xhci, temp, addr); + xhci_writel(xhci, temp, + port_array[wIndex]); spin_unlock_irqrestore(&xhci->lock, flags); msleep(20); spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_readl(xhci, addr); + temp = xhci_readl(xhci, + port_array[wIndex]); temp = xhci_port_state_to_neutral(temp); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, addr); + xhci_writel(xhci, temp, + port_array[wIndex]); } xhci->port_c_suspend |= 1 << wIndex; } @@ -500,10 +510,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_ENABLE: xhci_clear_port_change_bit(xhci, wValue, wIndex, - addr, temp); + port_array[wIndex], temp); break; case USB_PORT_FEAT_ENABLE: - xhci_disable_port(xhci, wIndex, addr, temp); + xhci_disable_port(xhci, wIndex, + port_array[wIndex], temp); break; default: goto error; @@ -534,9 +545,16 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) int i, retval; struct xhci_hcd *xhci = hcd_to_xhci(hcd); int ports; - u32 __iomem *addr; + u32 __iomem *port_array[15 + USB_MAXCHILDREN]; ports = HCS_MAX_PORTS(xhci->hcs_params1); + for (i = 0; i < ports; i++) { + if (i < xhci->num_usb3_ports) + port_array[i] = xhci->usb3_ports[i]; + else + port_array[i] = + xhci->usb2_ports[i - xhci->num_usb3_ports]; + } /* Initial status is no changes */ retval = (ports + 8) / 8; @@ -548,9 +566,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) spin_lock_irqsave(&xhci->lock, flags); /* For each port, did anything change? If so, set that bit in buf. */ for (i = 0; i < ports; i++) { - addr = &xhci->op_regs->port_status_base + - NUM_PORT_REGS*i; - temp = xhci_readl(xhci, addr); + temp = xhci_readl(xhci, port_array[i]); if ((temp & mask) != 0 || (xhci->port_c_suspend & 1 << i) || (xhci->resume_done[i] && time_after_eq( @@ -569,10 +585,19 @@ int xhci_bus_suspend(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int max_ports, port_index; + u32 __iomem *port_array[15 + USB_MAXCHILDREN]; + int i; unsigned long flags; xhci_dbg(xhci, "suspend root hub\n"); max_ports = HCS_MAX_PORTS(xhci->hcs_params1); + for (i = 0; i < max_ports; i++) { + if (i < xhci->num_usb3_ports) + port_array[i] = xhci->usb3_ports[i]; + else + port_array[i] = + xhci->usb2_ports[i - xhci->num_usb3_ports]; + } spin_lock_irqsave(&xhci->lock, flags); @@ -593,13 +618,10 @@ int xhci_bus_suspend(struct usb_hcd *hcd) xhci->bus_suspended = 0; while (port_index--) { /* suspend the port if the port is not suspended */ - u32 __iomem *addr; u32 t1, t2; int slot_id; - addr = &xhci->op_regs->port_status_base + - NUM_PORT_REGS * (port_index & 0xff); - t1 = xhci_readl(xhci, addr); + t1 = xhci_readl(xhci, port_array[port_index]); t2 = xhci_port_state_to_neutral(t1); if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) { @@ -628,15 +650,17 @@ int xhci_bus_suspend(struct usb_hcd *hcd) t1 = xhci_port_state_to_neutral(t1); if (t1 != t2) - xhci_writel(xhci, t2, addr); + xhci_writel(xhci, t2, port_array[port_index]); if (DEV_HIGHSPEED(t1)) { /* enable remote wake up for USB 2.0 */ u32 __iomem *addr; u32 tmp; - addr = &xhci->op_regs->port_power_base + - NUM_PORT_REGS * (port_index & 0xff); + /* Add one to the port status register address to get + * the port power control register address. + */ + addr = port_array[port_index] + 1; tmp = xhci_readl(xhci, addr); tmp |= PORT_RWE; xhci_writel(xhci, tmp, addr); @@ -652,11 +676,20 @@ int xhci_bus_resume(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int max_ports, port_index; + u32 __iomem *port_array[15 + USB_MAXCHILDREN]; + int i; u32 temp; unsigned long flags; xhci_dbg(xhci, "resume root hub\n"); max_ports = HCS_MAX_PORTS(xhci->hcs_params1); + for (i = 0; i < max_ports; i++) { + if (i < xhci->num_usb3_ports) + port_array[i] = xhci->usb3_ports[i]; + else + port_array[i] = + xhci->usb2_ports[i - xhci->num_usb3_ports]; + } if (time_before(jiffies, xhci->next_statechange)) msleep(5); @@ -676,13 +709,10 @@ int xhci_bus_resume(struct usb_hcd *hcd) while (port_index--) { /* Check whether need resume ports. If needed resume port and disable remote wakeup */ - u32 __iomem *addr; u32 temp; int slot_id; - addr = &xhci->op_regs->port_status_base + - NUM_PORT_REGS * (port_index & 0xff); - temp = xhci_readl(xhci, addr); + temp = xhci_readl(xhci, port_array[port_index]); if (DEV_SUPERSPEED(temp)) temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); else @@ -693,36 +723,38 @@ int xhci_bus_resume(struct usb_hcd *hcd) temp = xhci_port_state_to_neutral(temp); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, addr); + xhci_writel(xhci, temp, port_array[port_index]); } else { temp = xhci_port_state_to_neutral(temp); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | XDEV_RESUME; - xhci_writel(xhci, temp, addr); + xhci_writel(xhci, temp, port_array[port_index]); spin_unlock_irqrestore(&xhci->lock, flags); msleep(20); spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_readl(xhci, addr); + temp = xhci_readl(xhci, port_array[port_index]); temp = xhci_port_state_to_neutral(temp); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, addr); + xhci_writel(xhci, temp, port_array[port_index]); } slot_id = xhci_find_slot_id_by_port(xhci, port_index + 1); if (slot_id) xhci_ring_device(xhci, slot_id); } else - xhci_writel(xhci, temp, addr); + xhci_writel(xhci, temp, port_array[port_index]); if (DEV_HIGHSPEED(temp)) { /* disable remote wake up for USB 2.0 */ u32 __iomem *addr; u32 tmp; - addr = &xhci->op_regs->port_power_base + - NUM_PORT_REGS * (port_index & 0xff); + /* Add one to the port status register address to get + * the port power control register address. + */ + addr = port_array[port_index] + 1; tmp = xhci_readl(xhci, addr); tmp &= ~PORT_RWE; xhci_writel(xhci, tmp, addr); -- cgit v1.1 From 20b67cf51fa606442bb343afad0ee1a393a6afb3 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 15 Dec 2010 12:47:14 -0800 Subject: xhci: Refactor bus suspend state into a struct. There are several variables in the xhci_hcd structure that are related to bus suspend and resume state. There are a couple different port status arrays that are accessed by port index. Move those variables into a separate structure, xhci_bus_state. Stash that structure in xhci_hcd. When we have two roothhubs that can be suspended and resumed separately, we can have two xhci_bus_states, and index into the port arrays in each structure with the fake roothub port index (not the real hardware port index). Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 50 ++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 21 deletions(-) (limited to 'drivers/usb/host/xhci-hub.c') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 89ff025..4944ba1 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -291,6 +291,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u32 __iomem *port_array[15 + USB_MAXCHILDREN]; int i; int slot_id; + struct xhci_bus_state *bus_state; ports = HCS_MAX_PORTS(xhci->hcs_params1); for (i = 0; i < ports; i++) { @@ -300,6 +301,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, port_array[i] = xhci->usb2_ports[i - xhci->num_usb3_ports]; } + bus_state = &xhci->bus_state[hcd_index(hcd)]; spin_lock_irqsave(&xhci->lock, flags); switch (typeReq) { @@ -336,10 +338,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if ((temp & PORT_RESET) || !(temp & PORT_PE)) goto error; if (!DEV_SUPERSPEED(temp) && time_after_eq(jiffies, - xhci->resume_done[wIndex])) { + bus_state->resume_done[wIndex])) { xhci_dbg(xhci, "Resume USB2 port %d\n", wIndex + 1); - xhci->resume_done[wIndex] = 0; + bus_state->resume_done[wIndex] = 0; temp1 = xhci_port_state_to_neutral(temp); temp1 &= ~PORT_PLS_MASK; temp1 |= PORT_LINK_STROBE | XDEV_U0; @@ -354,15 +356,15 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; } xhci_ring_device(xhci, slot_id); - xhci->port_c_suspend |= 1 << wIndex; - xhci->suspended_ports &= ~(1 << wIndex); + bus_state->port_c_suspend |= 1 << wIndex; + bus_state->suspended_ports &= ~(1 << wIndex); } } if ((temp & PORT_PLS_MASK) == XDEV_U0 && (temp & PORT_POWER) - && (xhci->suspended_ports & (1 << wIndex))) { - xhci->suspended_ports &= ~(1 << wIndex); - xhci->port_c_suspend |= 1 << wIndex; + && (bus_state->suspended_ports & (1 << wIndex))) { + bus_state->suspended_ports &= ~(1 << wIndex); + bus_state->port_c_suspend |= 1 << wIndex; } if (temp & PORT_CONNECT) { status |= USB_PORT_STAT_CONNECTION; @@ -376,7 +378,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, status |= USB_PORT_STAT_RESET; if (temp & PORT_POWER) status |= USB_PORT_STAT_POWER; - if (xhci->port_c_suspend & (1 << wIndex)) + if (bus_state->port_c_suspend & (1 << wIndex)) status |= 1 << USB_PORT_FEAT_C_SUSPEND; xhci_dbg(xhci, "Get port status returned 0x%x\n", status); put_unaligned(cpu_to_le32(status), (__le32 *) buf); @@ -422,7 +424,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, spin_lock_irqsave(&xhci->lock, flags); temp = xhci_readl(xhci, port_array[wIndex]); - xhci->suspended_ports |= 1 << wIndex; + bus_state->suspended_ports |= 1 << wIndex; break; case USB_PORT_FEAT_POWER: /* @@ -493,7 +495,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_writel(xhci, temp, port_array[wIndex]); } - xhci->port_c_suspend |= 1 << wIndex; + bus_state->port_c_suspend |= 1 << wIndex; } slot_id = xhci_find_slot_id_by_port(xhci, wIndex + 1); @@ -504,7 +506,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_ring_device(xhci, slot_id); break; case USB_PORT_FEAT_C_SUSPEND: - xhci->port_c_suspend &= ~(1 << wIndex); + bus_state->port_c_suspend &= ~(1 << wIndex); case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_OVER_CURRENT: @@ -546,6 +548,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) struct xhci_hcd *xhci = hcd_to_xhci(hcd); int ports; u32 __iomem *port_array[15 + USB_MAXCHILDREN]; + struct xhci_bus_state *bus_state; ports = HCS_MAX_PORTS(xhci->hcs_params1); for (i = 0; i < ports; i++) { @@ -555,6 +558,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) port_array[i] = xhci->usb2_ports[i - xhci->num_usb3_ports]; } + bus_state = &xhci->bus_state[hcd_index(hcd)]; /* Initial status is no changes */ retval = (ports + 8) / 8; @@ -568,9 +572,9 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) for (i = 0; i < ports; i++) { temp = xhci_readl(xhci, port_array[i]); if ((temp & mask) != 0 || - (xhci->port_c_suspend & 1 << i) || - (xhci->resume_done[i] && time_after_eq( - jiffies, xhci->resume_done[i]))) { + (bus_state->port_c_suspend & 1 << i) || + (bus_state->resume_done[i] && time_after_eq( + jiffies, bus_state->resume_done[i]))) { buf[(i + 1) / 8] |= 1 << (i + 1) % 8; status = 1; } @@ -587,6 +591,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) int max_ports, port_index; u32 __iomem *port_array[15 + USB_MAXCHILDREN]; int i; + struct xhci_bus_state *bus_state; unsigned long flags; xhci_dbg(xhci, "suspend root hub\n"); @@ -598,13 +603,14 @@ int xhci_bus_suspend(struct usb_hcd *hcd) port_array[i] = xhci->usb2_ports[i - xhci->num_usb3_ports]; } + bus_state = &xhci->bus_state[hcd_index(hcd)]; spin_lock_irqsave(&xhci->lock, flags); if (hcd->self.root_hub->do_remote_wakeup) { port_index = max_ports; while (port_index--) { - if (xhci->resume_done[port_index] != 0) { + if (bus_state->resume_done[port_index] != 0) { spin_unlock_irqrestore(&xhci->lock, flags); xhci_dbg(xhci, "suspend failed because " "port %d is resuming\n", @@ -615,7 +621,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) } port_index = max_ports; - xhci->bus_suspended = 0; + bus_state->bus_suspended = 0; while (port_index--) { /* suspend the port if the port is not suspended */ u32 t1, t2; @@ -635,7 +641,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) } t2 &= ~PORT_PLS_MASK; t2 |= PORT_LINK_STROBE | XDEV_U3; - set_bit(port_index, &xhci->bus_suspended); + set_bit(port_index, &bus_state->bus_suspended); } if (hcd->self.root_hub->do_remote_wakeup) { if (t1 & PORT_CONNECT) { @@ -667,7 +673,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) } } hcd->state = HC_STATE_SUSPENDED; - xhci->next_statechange = jiffies + msecs_to_jiffies(10); + bus_state->next_statechange = jiffies + msecs_to_jiffies(10); spin_unlock_irqrestore(&xhci->lock, flags); return 0; } @@ -678,6 +684,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) int max_ports, port_index; u32 __iomem *port_array[15 + USB_MAXCHILDREN]; int i; + struct xhci_bus_state *bus_state; u32 temp; unsigned long flags; @@ -690,8 +697,9 @@ int xhci_bus_resume(struct usb_hcd *hcd) port_array[i] = xhci->usb2_ports[i - xhci->num_usb3_ports]; } + bus_state = &xhci->bus_state[hcd_index(hcd)]; - if (time_before(jiffies, xhci->next_statechange)) + if (time_before(jiffies, bus_state->next_statechange)) msleep(5); spin_lock_irqsave(&xhci->lock, flags); @@ -717,7 +725,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); else temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); - if (test_bit(port_index, &xhci->bus_suspended) && + if (test_bit(port_index, &bus_state->bus_suspended) && (temp & PORT_PLS_MASK)) { if (DEV_SUPERSPEED(temp)) { temp = xhci_port_state_to_neutral(temp); @@ -763,7 +771,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) (void) xhci_readl(xhci, &xhci->op_regs->command); - xhci->next_statechange = jiffies + msecs_to_jiffies(5); + bus_state->next_statechange = jiffies + msecs_to_jiffies(5); /* re-enable irqs */ temp = xhci_readl(xhci, &xhci->op_regs->command); temp |= CMD_EIE; -- cgit v1.1 From 5233630fcdd6f7d415dcbed264031439cab73f9d Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 16 Dec 2010 10:49:09 -0800 Subject: xhci: Change xhci_find_slot_id_by_port() API. xhci_find_slot_id_by_port() tries to map the port index to the slot ID for the USB device. In the future, there will be two xHCI roothubs, and their port indices will overlap. Therefore, xhci_find_slot_id_by_port() will need to use information in the roothub's usb_hcd structure to map the port index and roothub speed to the right slot ID. Add a new parameter to xhci_find_slot_id_by_port(), in order to pass in the roothub's usb_hcd structure. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/usb/host/xhci-hub.c') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 4944ba1..4c3788c 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -135,7 +135,8 @@ u32 xhci_port_state_to_neutral(u32 state) /* * find slot id based on port number. */ -int xhci_find_slot_id_by_port(struct xhci_hcd *xhci, u16 port) +int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, + u16 port) { int slot_id; int i; @@ -349,7 +350,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_dbg(xhci, "set port %d resume\n", wIndex + 1); - slot_id = xhci_find_slot_id_by_port(xhci, + slot_id = xhci_find_slot_id_by_port(hcd, xhci, wIndex + 1); if (!slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); @@ -404,7 +405,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; } - slot_id = xhci_find_slot_id_by_port(xhci, wIndex + 1); + slot_id = xhci_find_slot_id_by_port(hcd, xhci, + wIndex + 1); if (!slot_id) { xhci_warn(xhci, "slot_id is zero\n"); goto error; @@ -498,7 +500,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, bus_state->port_c_suspend |= 1 << wIndex; } - slot_id = xhci_find_slot_id_by_port(xhci, wIndex + 1); + slot_id = xhci_find_slot_id_by_port(hcd, xhci, + wIndex + 1); if (!slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); goto error; @@ -632,7 +635,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) { xhci_dbg(xhci, "port %d not suspended\n", port_index); - slot_id = xhci_find_slot_id_by_port(xhci, + slot_id = xhci_find_slot_id_by_port(hcd, xhci, port_index + 1); if (slot_id) { spin_unlock_irqrestore(&xhci->lock, flags); @@ -748,7 +751,8 @@ int xhci_bus_resume(struct usb_hcd *hcd) temp |= PORT_LINK_STROBE | XDEV_U0; xhci_writel(xhci, temp, port_array[port_index]); } - slot_id = xhci_find_slot_id_by_port(xhci, port_index + 1); + slot_id = xhci_find_slot_id_by_port(hcd, + xhci, port_index + 1); if (slot_id) xhci_ring_device(xhci, slot_id); } else -- cgit v1.1 From f6ff0ac878eb420011fa2448851dd48c3a7e7b31 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 16 Dec 2010 11:21:10 -0800 Subject: xhci: Register second xHCI roothub. This patch changes the xHCI driver to allocate two roothubs. This touches the driver initialization and shutdown paths, roothub emulation code, and port status change event handlers. This is a rather large patch, but it can't be broken up, or it would break git-bisect. Make the xHCI driver register its own PCI probe function. This will call the USB core to create the USB 2.0 roothub, and then create the USB 3.0 roothub. This gets the code for registering a shared roothub out of the USB core, and allows other HCDs later to decide if and how many shared roothubs they want to allocate. Make sure the xHCI's reset method marks the xHCI host controller's primary roothub as the USB 2.0 roothub. This ensures that the high speed bus will be processed first when the PCI device is resumed, and any USB 3.0 devices that have migrated over to high speed will migrate back after being reset. This ensures that USB persist works with these odd devices. The reset method will also mark the xHCI USB2 roothub as having an integrated TT. Like EHCI host controllers with a "rate matching hub" the xHCI USB 2.0 roothub doesn't have an OHCI or UHCI companion controller. It doesn't really have a TT, but we'll lie and say it has an integrated TT. We need to do this because the USB core will reject LS/FS devices under a HS hub without a TT. Other details: ------------- The roothub emulation code is changed to return the correct number of ports for the two roothubs. For the USB 3.0 roothub, it only reports the USB 3.0 ports. For the USB 2.0 roothub, it reports all the LS/FS/HS ports. The code to disable a port now checks the speed of the roothub, and refuses to disable SuperSpeed ports under the USB 3.0 roothub. The code for initializing a new device context must be changed to set the proper roothub port number. Since we've split the xHCI host into two roothubs, we can't just use the port number in the ancestor hub. Instead, we loop through the array of hardware port status register speeds and find the Nth port with a similar speed. The port status change event handler is updated to figure out whether the port that reported the change is a USB 3.0 port, or a non-SuperSpeed port. Once it figures out the port speed, it kicks the proper roothub. The function to find a slot ID based on the port index is updated to take into account that the two roothubs will have over-lapping port indexes. It checks that the virtual device with a matching port index is the same speed as the passed in roothub. There's also changes to the driver initialization and shutdown paths: 1. Make sure that the xhci_hcd pointer is shared across the two usb_hcd structures. The xhci_hcd pointer is allocated and the registers are mapped in when xhci_pci_setup() is called with the primary HCD. When xhci_pci_setup() is called with the non-primary HCD, the xhci_hcd pointer is stored. 2. Make sure to set the sg_tablesize for both usb_hcd structures. Set the PCI DMA mask for the non-primary HCD to allow for 64-bit or 32-bit DMA. (The PCI DMA mask is set from the primary HCD further down in the xhci_pci_setup() function.) 3. Ensure that the host controller doesn't start kicking khubd in response to port status changes before both usb_hcd structures are registered. xhci_run() only starts the xHC running once it has been called with the non-primary roothub. Similarly, the xhci_stop() function only halts the host controller when it is called with the non-primary HCD. Then on the second call, it resets and cleans up the MSI-X irqs. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 97 ++++++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 45 deletions(-) (limited to 'drivers/usb/host/xhci-hub.c') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 4c3788c..ee4af07 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -28,13 +28,20 @@ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \ PORT_RC | PORT_PLC | PORT_PE) -static void xhci_hub_descriptor(struct xhci_hcd *xhci, +static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, struct usb_hub_descriptor *desc) { int ports; u16 temp; - ports = HCS_MAX_PORTS(xhci->hcs_params1); + if (hcd->speed == HCD_USB3) + ports = xhci->num_usb3_ports; + else + ports = xhci->num_usb2_ports; + + /* FIXME: return a USB 3.0 hub descriptor if this request was for the + * USB3 roothub. + */ /* USB 3.0 hubs have a different descriptor, but we fake this for now */ desc->bDescriptorType = 0x29; @@ -134,18 +141,22 @@ u32 xhci_port_state_to_neutral(u32 state) /* * find slot id based on port number. + * @port: The one-based port number from one of the two split roothubs. */ int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, u16 port) { int slot_id; int i; + enum usb_device_speed speed; slot_id = 0; for (i = 0; i < MAX_HC_SLOTS; i++) { if (!xhci->devs[i]) continue; - if (xhci->devs[i]->port == port) { + speed = xhci->devs[i]->udev->speed; + if (((speed == USB_SPEED_SUPER) == (hcd->speed == HCD_USB3)) + && xhci->devs[i]->port == port) { slot_id = i; break; } @@ -226,11 +237,11 @@ void xhci_ring_device(struct xhci_hcd *xhci, int slot_id) return; } -static void xhci_disable_port(struct xhci_hcd *xhci, u16 wIndex, - u32 __iomem *addr, u32 port_status) +static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, + u16 wIndex, u32 __iomem *addr, u32 port_status) { /* Don't allow the USB core to disable SuperSpeed ports. */ - if (xhci->port_array[wIndex] == 0x03) { + if (hcd->speed == HCD_USB3) { xhci_dbg(xhci, "Ignoring request to disable " "SuperSpeed port.\n"); return; @@ -289,18 +300,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, unsigned long flags; u32 temp, temp1, status; int retval = 0; - u32 __iomem *port_array[15 + USB_MAXCHILDREN]; - int i; + u32 __iomem **port_array; int slot_id; struct xhci_bus_state *bus_state; - ports = HCS_MAX_PORTS(xhci->hcs_params1); - for (i = 0; i < ports; i++) { - if (i < xhci->num_usb3_ports) - port_array[i] = xhci->usb3_ports[i]; - else - port_array[i] = - xhci->usb2_ports[i - xhci->num_usb3_ports]; + if (hcd->speed == HCD_USB3) { + ports = xhci->num_usb3_ports; + port_array = xhci->usb3_ports; + } else { + ports = xhci->num_usb2_ports; + port_array = xhci->usb2_ports; } bus_state = &xhci->bus_state[hcd_index(hcd)]; @@ -311,7 +320,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, memset(buf, 0, 4); break; case GetHubDescriptor: - xhci_hub_descriptor(xhci, (struct usb_hub_descriptor *) buf); + xhci_hub_descriptor(hcd, xhci, + (struct usb_hub_descriptor *) buf); break; case GetPortStatus: if (!wIndex || wIndex > ports) @@ -518,7 +528,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, port_array[wIndex], temp); break; case USB_PORT_FEAT_ENABLE: - xhci_disable_port(xhci, wIndex, + xhci_disable_port(hcd, xhci, wIndex, port_array[wIndex], temp); break; default: @@ -550,16 +560,15 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) int i, retval; struct xhci_hcd *xhci = hcd_to_xhci(hcd); int ports; - u32 __iomem *port_array[15 + USB_MAXCHILDREN]; + u32 __iomem **port_array; struct xhci_bus_state *bus_state; - ports = HCS_MAX_PORTS(xhci->hcs_params1); - for (i = 0; i < ports; i++) { - if (i < xhci->num_usb3_ports) - port_array[i] = xhci->usb3_ports[i]; - else - port_array[i] = - xhci->usb2_ports[i - xhci->num_usb3_ports]; + if (hcd->speed == HCD_USB3) { + ports = xhci->num_usb3_ports; + port_array = xhci->usb3_ports; + } else { + ports = xhci->num_usb2_ports; + port_array = xhci->usb2_ports; } bus_state = &xhci->bus_state[hcd_index(hcd)]; @@ -592,19 +601,18 @@ int xhci_bus_suspend(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int max_ports, port_index; - u32 __iomem *port_array[15 + USB_MAXCHILDREN]; - int i; + u32 __iomem **port_array; struct xhci_bus_state *bus_state; unsigned long flags; - xhci_dbg(xhci, "suspend root hub\n"); - max_ports = HCS_MAX_PORTS(xhci->hcs_params1); - for (i = 0; i < max_ports; i++) { - if (i < xhci->num_usb3_ports) - port_array[i] = xhci->usb3_ports[i]; - else - port_array[i] = - xhci->usb2_ports[i - xhci->num_usb3_ports]; + if (hcd->speed == HCD_USB3) { + max_ports = xhci->num_usb3_ports; + port_array = xhci->usb3_ports; + xhci_dbg(xhci, "suspend USB 3.0 root hub\n"); + } else { + max_ports = xhci->num_usb2_ports; + port_array = xhci->usb2_ports; + xhci_dbg(xhci, "suspend USB 2.0 root hub\n"); } bus_state = &xhci->bus_state[hcd_index(hcd)]; @@ -685,20 +693,19 @@ int xhci_bus_resume(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int max_ports, port_index; - u32 __iomem *port_array[15 + USB_MAXCHILDREN]; - int i; + u32 __iomem **port_array; struct xhci_bus_state *bus_state; u32 temp; unsigned long flags; - xhci_dbg(xhci, "resume root hub\n"); - max_ports = HCS_MAX_PORTS(xhci->hcs_params1); - for (i = 0; i < max_ports; i++) { - if (i < xhci->num_usb3_ports) - port_array[i] = xhci->usb3_ports[i]; - else - port_array[i] = - xhci->usb2_ports[i - xhci->num_usb3_ports]; + if (hcd->speed == HCD_USB3) { + max_ports = xhci->num_usb3_ports; + port_array = xhci->usb3_ports; + xhci_dbg(xhci, "resume USB 3.0 root hub\n"); + } else { + max_ports = xhci->num_usb2_ports; + port_array = xhci->usb2_ports; + xhci_dbg(xhci, "resume USB 2.0 root hub\n"); } bus_state = &xhci->bus_state[hcd_index(hcd)]; -- cgit v1.1 From 4bbb0ace9a3de8392527e3c87926309d541d3b00 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 29 Nov 2010 16:14:37 -0800 Subject: xhci: Return a USB 3.0 hub descriptor for USB3 roothub. Return the correct xHCI roothub descriptor, based on whether the roothub is marked as USB 3.0 or USB 2.0 in usb_hcd->bcdUSB. Fill in DeviceRemovable for the USB 2.0 and USB 3.0 roothub descriptors, using the Device Removable bit in the port status and control registers. xHCI is the first host controller to actually properly set these bits (other hosts say all devices are removable). When userspace asks for a USB 2.0-style hub descriptor for the USB 3.0 roothub, stall the endpoint. This is what real external USB 3.0 hubs do, and we don't want to return a descriptor that userspace didn't ask for. The USB core is already fixed to always ask for USB 3.0-style hub descriptors. Only usbfs (typically lsusb) will ask for the USB 2.0-style hub descriptors. This has already been fixed in usbutils version 0.91, but the kernel needs to deal with older usbutils versions. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 134 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 114 insertions(+), 20 deletions(-) (limited to 'drivers/usb/host/xhci-hub.c') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index ee4af07..191ebc5 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -28,33 +28,15 @@ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \ PORT_RC | PORT_PLC | PORT_PE) -static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, - struct usb_hub_descriptor *desc) +static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, + struct usb_hub_descriptor *desc, int ports) { - int ports; u16 temp; - if (hcd->speed == HCD_USB3) - ports = xhci->num_usb3_ports; - else - ports = xhci->num_usb2_ports; - - /* FIXME: return a USB 3.0 hub descriptor if this request was for the - * USB3 roothub. - */ - - /* USB 3.0 hubs have a different descriptor, but we fake this for now */ - desc->bDescriptorType = 0x29; desc->bPwrOn2PwrGood = 10; /* xhci section 5.4.9 says 20ms max */ desc->bHubContrCurrent = 0; desc->bNbrPorts = ports; - temp = 1 + (ports / 8); - desc->bDescLength = 7 + 2 * temp; - - memset(&desc->u.hs.DeviceRemovable[0], 0, temp); - memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); - /* Ugh, these should be #defines, FIXME */ /* Using table 11-13 in USB 2.0 spec. */ temp = 0; @@ -71,6 +53,102 @@ static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, desc->wHubCharacteristics = (__force __u16) 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 = 0x29; + temp = 1 + (ports / 8); + desc->bDescLength = 7 + 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->usb3_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 */ +static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, + struct usb_hub_descriptor *desc) +{ + int ports; + u16 port_removable; + u32 portsc; + unsigned int i; + + ports = xhci->num_usb3_ports; + xhci_common_hub_descriptor(xhci, desc, ports); + desc->bDescriptorType = 0x2a; + desc->bDescLength = 12; + + /* header decode latency should be zero for roothubs, + * see section 4.23.5.2. + */ + desc->u.ss.bHubHdrDecLat = 0; + desc->u.ss.wHubDelay = 0; + + port_removable = 0; + /* bit 0 is reserved, bit 1 is for port 1, etc. */ + for (i = 0; i < ports; i++) { + portsc = xhci_readl(xhci, xhci->usb3_ports[i]); + if (portsc & PORT_DEV_REMOVE) + port_removable |= 1 << (i + 1); + } + memset(&desc->u.ss.DeviceRemovable, + (__force __u16) cpu_to_le16(port_removable), + sizeof(__u16)); +} + +static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, + struct usb_hub_descriptor *desc) +{ + + if (hcd->speed == HCD_USB3) + xhci_usb3_hub_descriptor(hcd, xhci, desc); + else + xhci_usb2_hub_descriptor(hcd, xhci, desc); + +} + static unsigned int xhci_port_speed(unsigned int port_status) { if (DEV_LOWSPEED(port_status)) @@ -320,6 +398,17 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, memset(buf, 0, 4); break; case GetHubDescriptor: + /* Check to make sure userspace is asking for the USB 3.0 hub + * descriptor for the USB 3.0 roothub. If not, we stall the + * endpoint, like external hubs do. + */ + if (hcd->speed == HCD_USB3 && + (wLength < USB_DT_SS_HUB_SIZE || + wValue != (USB_DT_SS_HUB << 8))) { + xhci_dbg(xhci, "Wrong hub descriptor type for " + "USB 3.0 roothub.\n"); + goto error; + } xhci_hub_descriptor(hcd, xhci, (struct usb_hub_descriptor *) buf); break; @@ -331,6 +420,9 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp); + /* FIXME - should we return a port status value like the USB + * 3.0 external hubs do? + */ /* wPortChange bits */ if (temp & PORT_CSC) status |= USB_PORT_STAT_C_CONNECTION << 16; @@ -401,6 +493,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, wIndex--; temp = xhci_readl(xhci, port_array[wIndex]); temp = xhci_port_state_to_neutral(temp); + /* FIXME: What new port features do we need to support? */ switch (wValue) { case USB_PORT_FEAT_SUSPEND: temp = xhci_readl(xhci, port_array[wIndex]); @@ -469,6 +562,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; wIndex--; temp = xhci_readl(xhci, port_array[wIndex]); + /* FIXME: What new port features do we need to support? */ temp = xhci_port_state_to_neutral(temp); switch (wValue) { case USB_PORT_FEAT_SUSPEND: -- cgit v1.1 From f9de8151877b4f01cc8e124b0e213a6c6c78d970 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 29 Oct 2010 14:37:23 -0700 Subject: xhci: Make roothub functions deal with device removal. Return early in the roothub control and status functions if the xHCI host controller is not electrically present in the system (register reads return all "fs"). This issue only shows up when the xHCI driver registers two roothubs and the host controller is removed from the system. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/usb/host/xhci-hub.c') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 191ebc5..770f84c 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -418,6 +418,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, wIndex--; status = 0; temp = xhci_readl(xhci, port_array[wIndex]); + if (temp == 0xffffffff) { + retval = -ENODEV; + break; + } xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp); /* FIXME - should we return a port status value like the USB @@ -492,6 +496,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; wIndex--; temp = xhci_readl(xhci, port_array[wIndex]); + if (temp == 0xffffffff) { + retval = -ENODEV; + break; + } temp = xhci_port_state_to_neutral(temp); /* FIXME: What new port features do we need to support? */ switch (wValue) { @@ -562,6 +570,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; wIndex--; temp = xhci_readl(xhci, port_array[wIndex]); + if (temp == 0xffffffff) { + retval = -ENODEV; + break; + } /* FIXME: What new port features do we need to support? */ temp = xhci_port_state_to_neutral(temp); switch (wValue) { @@ -677,6 +689,10 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) /* For each port, did anything change? If so, set that bit in buf. */ for (i = 0; i < ports; i++) { temp = xhci_readl(xhci, port_array[i]); + if (temp == 0xffffffff) { + retval = -ENODEV; + break; + } if ((temp & mask) != 0 || (bus_state->port_c_suspend & 1 << i) || (bus_state->resume_done[i] && time_after_eq( -- cgit v1.1 From 131dec344d5e41f01e4791aa4c80eb4bdb1e5274 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 6 Dec 2010 21:00:19 -0800 Subject: USB: Remove bogus USB_PORT_STAT_SUPER_SPEED symbol. USB_PORT_STAT_SUPER_SPEED is a made up symbol that the USB core used to track whether USB ports had a SuperSpeed device attached. This is a linux-internal symbol that was used when SuperSpeed and non-SuperSpeed devices would show up under the same xHCI roothub. This particular port status is never returned by external USB 3.0 hubs. (Instead they have a USB_PORT_STAT_SPEED_5GBPS that uses a completely different speed mask.) Now that the xHCI driver registers two roothubs, USB 3.0 devices will only show up under USB 3.0 hubs. Rip out USB_PORT_STAT_SUPER_SPEED and replace it with calls to hub_is_superspeed(). Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb/host/xhci-hub.c') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 770f84c..a78f2eb 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -155,8 +155,6 @@ static unsigned int xhci_port_speed(unsigned int port_status) return USB_PORT_STAT_LOW_SPEED; if (DEV_HIGHSPEED(port_status)) return USB_PORT_STAT_HIGH_SPEED; - if (DEV_SUPERSPEED(port_status)) - return USB_PORT_STAT_SUPER_SPEED; /* * FIXME: Yes, we should check for full speed, but the core uses that as * a default in portspeed() in usb/core/hub.c (which is the only place -- cgit v1.1