diff options
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 97 | ||||
-rw-r--r-- | drivers/usb/host/xhci-mem.c | 66 | ||||
-rw-r--r-- | drivers/usb/host/xhci-pci.c | 98 | ||||
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 94 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 62 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 12 |
6 files changed, 341 insertions, 88 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 4c3788c128df..ee4af076a8fe 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)]; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index bc809cbd570a..180a2abbc868 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -814,14 +814,64 @@ void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci, ep0_ctx->deq |= ep_ring->cycle_state; } +/* + * The xHCI roothub may have ports of differing speeds in any order in the port + * status registers. xhci->port_array provides an array of the port speed for + * each offset into the port status registers. + * + * The xHCI hardware wants to know the roothub port number that the USB device + * is attached to (or the roothub port its ancestor hub is attached to). All we + * know is the index of that port under either the USB 2.0 or the USB 3.0 + * roothub, but that doesn't give us the real index into the HW port status + * registers. Scan through the xHCI roothub port array, looking for the Nth + * entry of the correct port speed. Return the port number of that entry. + */ +static u32 xhci_find_real_port_number(struct xhci_hcd *xhci, + struct usb_device *udev) +{ + struct usb_device *top_dev; + unsigned int num_similar_speed_ports; + unsigned int faked_port_num; + int i; + + for (top_dev = udev; top_dev->parent && top_dev->parent->parent; + top_dev = top_dev->parent) + /* Found device below root hub */; + faked_port_num = top_dev->portnum; + for (i = 0, num_similar_speed_ports = 0; + i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { + u8 port_speed = xhci->port_array[i]; + + /* + * Skip ports that don't have known speeds, or have duplicate + * Extended Capabilities port speed entries. + */ + if (port_speed == 0 || port_speed == -1) + continue; + + /* + * USB 3.0 ports are always under a USB 3.0 hub. USB 2.0 and + * 1.1 ports are under the USB 2.0 hub. If the port speed + * matches the device speed, it's a similar speed port. + */ + if ((port_speed == 0x03) == (udev->speed == USB_SPEED_SUPER)) + num_similar_speed_ports++; + if (num_similar_speed_ports == faked_port_num) + /* Roothub ports are numbered from 1 to N */ + return i+1; + } + return 0; +} + /* Setup an xHCI virtual device for a Set Address command */ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev) { struct xhci_virt_device *dev; struct xhci_ep_ctx *ep0_ctx; - struct usb_device *top_dev; struct xhci_slot_ctx *slot_ctx; struct xhci_input_control_ctx *ctrl_ctx; + u32 port_num; + struct usb_device *top_dev; dev = xhci->devs[udev->slot_id]; /* Slot ID 0 is reserved */ @@ -863,12 +913,17 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud BUG(); } /* Find the root hub port this device is under */ + port_num = xhci_find_real_port_number(xhci, udev); + if (!port_num) + return -EINVAL; + slot_ctx->dev_info2 |= (u32) ROOT_HUB_PORT(port_num); + /* Set the port number in the virtual_device to the faked port number */ for (top_dev = udev; top_dev->parent && top_dev->parent->parent; top_dev = top_dev->parent) /* Found device below root hub */; - slot_ctx->dev_info2 |= (u32) ROOT_HUB_PORT(top_dev->portnum); dev->port = top_dev->portnum; - xhci_dbg(xhci, "Set root hub portnum to %d\n", top_dev->portnum); + xhci_dbg(xhci, "Set root hub portnum to %d\n", port_num); + xhci_dbg(xhci, "Set fake root hub portnum to %d\n", dev->port); /* Is this a LS/FS device under an external HS hub? */ if (udev->tt && udev->tt->hub->parent) { @@ -1452,6 +1507,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->page_size = 0; xhci->page_shift = 0; xhci->bus_state[0].bus_suspended = 0; + xhci->bus_state[1].bus_suspended = 0; } static int xhci_test_trb_in_td(struct xhci_hcd *xhci, @@ -1970,8 +2026,10 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) init_completion(&xhci->addr_dev); for (i = 0; i < MAX_HC_SLOTS; ++i) xhci->devs[i] = NULL; - for (i = 0; i < USB_MAXCHILDREN; ++i) + for (i = 0; i < USB_MAXCHILDREN; ++i) { xhci->bus_state[0].resume_done[i] = 0; + xhci->bus_state[1].resume_done[i] = 0; + } if (scratchpad_alloc(xhci, flags)) goto fail; diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 009082829364..4a9d55e80f73 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -50,18 +50,44 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev) /* called during probe() after chip reset completes */ static int xhci_pci_setup(struct usb_hcd *hcd) { - struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_hcd *xhci; struct pci_dev *pdev = to_pci_dev(hcd->self.controller); int retval; u32 temp; hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2; - xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); - if (!xhci) - return -ENOMEM; - *((struct xhci_hcd **) hcd->hcd_priv) = xhci; - xhci->main_hcd = hcd; + if (usb_hcd_is_primary_hcd(hcd)) { + xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); + if (!xhci) + return -ENOMEM; + *((struct xhci_hcd **) hcd->hcd_priv) = xhci; + xhci->main_hcd = hcd; + /* Mark the first roothub as being USB 2.0. + * The xHCI driver will register the USB 3.0 roothub. + */ + hcd->speed = HCD_USB2; + hcd->self.root_hub->speed = USB_SPEED_HIGH; + /* + * USB 2.0 roothub under xHCI has an integrated TT, + * (rate matching hub) as opposed to having an OHCI/UHCI + * companion controller. + */ + hcd->has_tt = 1; + } else { + /* xHCI private pointer was set in xhci_pci_probe for the second + * registered roothub. + */ + xhci = hcd_to_xhci(hcd); + temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + if (HCC_64BIT_ADDR(temp)) { + xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); + } else { + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); + } + return 0; + } xhci->cap_regs = hcd->regs; xhci->op_regs = hcd->regs + @@ -128,11 +154,67 @@ error: return retval; } +/* + * We need to register our own PCI probe function (instead of the USB core's + * function) in order to create a second roothub under xHCI. + */ +static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int retval; + struct xhci_hcd *xhci; + struct hc_driver *driver; + struct usb_hcd *hcd; + + driver = (struct hc_driver *)id->driver_data; + /* Register the USB 2.0 roothub. + * FIXME: USB core must know to register the USB 2.0 roothub first. + * This is sort of silly, because we could just set the HCD driver flags + * to say USB 2.0, but I'm not sure what the implications would be in + * the other parts of the HCD code. + */ + retval = usb_hcd_pci_probe(dev, id); + + if (retval) + return retval; + + /* USB 2.0 roothub is stored in the PCI device now. */ + hcd = dev_get_drvdata(&dev->dev); + xhci = hcd_to_xhci(hcd); + xhci->shared_hcd = usb_create_shared_hcd(driver, &dev->dev, + pci_name(dev), hcd); + if (!xhci->shared_hcd) { + retval = -ENOMEM; + goto dealloc_usb2_hcd; + } + + /* Set the xHCI pointer before xhci_pci_setup() (aka hcd_driver.reset) + * is called by usb_add_hcd(). + */ + *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci; + + retval = usb_add_hcd(xhci->shared_hcd, dev->irq, + IRQF_DISABLED | IRQF_SHARED); + if (retval) + goto put_usb3_hcd; + /* Roothub already marked as USB 3.0 speed */ + return 0; + +put_usb3_hcd: + usb_put_hcd(xhci->shared_hcd); +dealloc_usb2_hcd: + usb_hcd_pci_remove(dev); + return retval; +} + static void xhci_pci_remove(struct pci_dev *dev) { struct xhci_hcd *xhci; xhci = hcd_to_xhci(pci_get_drvdata(dev)); + if (xhci->shared_hcd) { + usb_remove_hcd(xhci->shared_hcd); + usb_put_hcd(xhci->shared_hcd); + } usb_hcd_pci_remove(dev); kfree(xhci); } @@ -170,7 +252,7 @@ static const struct hc_driver xhci_pci_hc_driver = { * generic hardware linkage */ .irq = xhci_irq, - .flags = HCD_MEMORY | HCD_USB3, + .flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED, /* * basic lifecycle operations @@ -231,7 +313,7 @@ static struct pci_driver xhci_pci_driver = { .name = (char *) hcd_name, .id_table = pci_ids, - .probe = usb_hcd_pci_probe, + .probe = xhci_pci_probe, .remove = xhci_pci_remove, /* suspend and resume implemented later */ diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 7fe9aebd3922..3bdf30dd8ce6 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -866,7 +866,7 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) } spin_unlock(&xhci->lock); xhci_dbg(xhci, "Calling usb_hc_died()\n"); - usb_hc_died(xhci_to_hcd(xhci)); + usb_hc_died(xhci_to_hcd(xhci)->primary_hcd); xhci_dbg(xhci, "xHCI host controller is dead.\n"); } @@ -1155,20 +1155,56 @@ static void handle_vendor_event(struct xhci_hcd *xhci, handle_cmd_completion(xhci, &event->event_cmd); } +/* @port_id: the one-based port ID from the hardware (indexed from array of all + * port registers -- USB 3.0 and USB 2.0). + * + * Returns a zero-based port number, which is suitable for indexing into each of + * the split roothubs' port arrays and bus state arrays. + */ +static unsigned int find_faked_portnum_from_hw_portnum(struct usb_hcd *hcd, + struct xhci_hcd *xhci, u32 port_id) +{ + unsigned int i; + unsigned int num_similar_speed_ports = 0; + + /* port_id from the hardware is 1-based, but port_array[], usb3_ports[], + * and usb2_ports are 0-based indexes. Count the number of similar + * speed ports, up to 1 port before this port. + */ + for (i = 0; i < (port_id - 1); i++) { + u8 port_speed = xhci->port_array[i]; + + /* + * Skip ports that don't have known speeds, or have duplicate + * Extended Capabilities port speed entries. + */ + if (port_speed == 0 || port_speed == -1) + continue; + + /* + * USB 3.0 ports are always under a USB 3.0 hub. USB 2.0 and + * 1.1 ports are under the USB 2.0 hub. If the port speed + * matches the device speed, it's a similar speed port. + */ + if ((port_speed == 0x03) == (hcd->speed == HCD_USB3)) + num_similar_speed_ports++; + } + return num_similar_speed_ports; +} + static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) { - struct usb_hcd *hcd = xhci_to_hcd(xhci); + struct usb_hcd *hcd; u32 port_id; u32 temp, temp1; int max_ports; int slot_id; unsigned int faked_port_index; - u32 __iomem *port_array[15 + USB_MAXCHILDREN]; - int i; + u8 major_revision; struct xhci_bus_state *bus_state; + u32 __iomem **port_array; - bus_state = &xhci->bus_state[0]; /* Port status change events always have a successful completion code */ if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) { xhci_warn(xhci, "WARN: xHC returned failed port status event\n"); @@ -1183,15 +1219,43 @@ static void handle_port_status(struct xhci_hcd *xhci, goto cleanup; } - 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]; + /* Figure out which usb_hcd this port is attached to: + * is it a USB 3.0 port or a USB 2.0/1.1 port? + */ + major_revision = xhci->port_array[port_id - 1]; + if (major_revision == 0) { + xhci_warn(xhci, "Event for port %u not in " + "Extended Capabilities, ignoring.\n", + port_id); + goto cleanup; } + if (major_revision == (u8) -1) { + xhci_warn(xhci, "Event for port %u duplicated in" + "Extended Capabilities, ignoring.\n", + port_id); + goto cleanup; + } + + /* + * Hardware port IDs reported by a Port Status Change Event include USB + * 3.0 and USB 2.0 ports. We want to check if the port has reported a + * resume event, but we first need to translate the hardware port ID + * into the index into the ports on the correct split roothub, and the + * correct bus_state structure. + */ + /* Find the right roothub. */ + hcd = xhci_to_hcd(xhci); + if ((major_revision == 0x03) != (hcd->speed == HCD_USB3)) + hcd = xhci->shared_hcd; + bus_state = &xhci->bus_state[hcd_index(hcd)]; + if (hcd->speed == HCD_USB3) + port_array = xhci->usb3_ports; + else + port_array = xhci->usb2_ports; + /* Find the faked port hub number */ + faked_port_index = find_faked_portnum_from_hw_portnum(hcd, xhci, + port_id); - faked_port_index = port_id; temp = xhci_readl(xhci, port_array[faked_port_index]); if (hcd->state == HC_STATE_SUSPENDED) { xhci_dbg(xhci, "resume root hub\n"); @@ -1228,10 +1292,10 @@ static void handle_port_status(struct xhci_hcd *xhci, xhci_writel(xhci, temp, port_array[faked_port_index]); } else { xhci_dbg(xhci, "resume HS port %d\n", port_id); - bus_state->resume_done[port_id - 1] = jiffies + + bus_state->resume_done[faked_port_index] = jiffies + msecs_to_jiffies(20); mod_timer(&hcd->rh_timer, - bus_state->resume_done[port_id - 1]); + bus_state->resume_done[faked_port_index]); /* Do the rest in GetPortStatus */ } } @@ -1242,7 +1306,7 @@ cleanup: spin_unlock(&xhci->lock); /* Pass this up to the core */ - usb_hcd_poll_rh_status(xhci_to_hcd(xhci)); + usb_hcd_poll_rh_status(hcd); spin_lock(&xhci->lock); } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8d45bbde3da4..4549068758f5 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -375,6 +375,21 @@ void xhci_event_ring_work(unsigned long arg) } #endif +static int xhci_run_finished(struct xhci_hcd *xhci) +{ + if (xhci_start(xhci)) { + xhci_halt(xhci); + return -ENODEV; + } + xhci->shared_hcd->state = HC_STATE_RUNNING; + + if (xhci->quirks & XHCI_NEC_HOST) + xhci_ring_cmd_db(xhci); + + xhci_dbg(xhci, "Finished xhci_run for USB3 roothub\n"); + return 0; +} + /* * Start the HC after it was halted. * @@ -395,7 +410,13 @@ int xhci_run(struct usb_hcd *hcd) struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + /* Start the xHCI host controller running only after the USB 2.0 roothub + * is setup. + */ + hcd->uses_new_polling = 1; + if (!usb_hcd_is_primary_hcd(hcd)) + return xhci_run_finished(xhci); xhci_dbg(xhci, "xhci_run\n"); /* unregister the legacy interrupt */ @@ -469,16 +490,23 @@ int xhci_run(struct usb_hcd *hcd) xhci_queue_vendor_command(xhci, 0, 0, 0, TRB_TYPE(TRB_NEC_GET_FW)); - if (xhci_start(xhci)) { - xhci_halt(xhci); - return -ENODEV; - } + xhci_dbg(xhci, "Finished xhci_run for USB2 roothub\n"); + return 0; +} - if (xhci->quirks & XHCI_NEC_HOST) - xhci_ring_cmd_db(xhci); +static void xhci_only_stop_hcd(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); - xhci_dbg(xhci, "Finished xhci_run\n"); - return 0; + spin_lock_irq(&xhci->lock); + xhci_halt(xhci); + + /* The shared_hcd is going to be deallocated shortly (the USB core only + * calls this function when allocation fails in usb_add_hcd(), or + * usb_remove_hcd() is called). So we need to unset xHCI's pointer. + */ + xhci->shared_hcd = NULL; + spin_unlock_irq(&xhci->lock); } /* @@ -495,7 +523,15 @@ void xhci_stop(struct usb_hcd *hcd) u32 temp; struct xhci_hcd *xhci = hcd_to_xhci(hcd); + if (!usb_hcd_is_primary_hcd(hcd)) { + xhci_only_stop_hcd(xhci->shared_hcd); + return; + } + spin_lock_irq(&xhci->lock); + /* Make sure the xHC is halted for a USB3 roothub + * (xhci_stop() could be called as part of failed init). + */ xhci_halt(xhci); xhci_reset(xhci); spin_unlock_irq(&xhci->lock); @@ -528,6 +564,8 @@ void xhci_stop(struct usb_hcd *hcd) * This is called when the machine is rebooting or halting. We assume that the * machine will be powered off, and the HC's internal state will be reset. * Don't bother to free memory. + * + * This will only ever be called with the main usb_hcd (the USB3 roothub). */ void xhci_shutdown(struct usb_hcd *hcd) { @@ -694,10 +732,12 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) struct usb_hcd *hcd = xhci_to_hcd(xhci); int retval; - /* Wait a bit if the bus needs to settle from the transistion to - * suspend. + /* Wait a bit if either of the roothubs need to settle from the + * transistion into bus suspend. */ - if (time_before(jiffies, xhci->bus_state[0].next_statechange)) + if (time_before(jiffies, xhci->bus_state[0].next_statechange) || + time_before(jiffies, + xhci->bus_state[1].next_statechange)) msleep(100); spin_lock_irq(&xhci->lock); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 443d6333f280..e9217bb288ad 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1174,12 +1174,16 @@ struct xhci_bus_state { static inline unsigned int hcd_index(struct usb_hcd *hcd) { - return 0; + if (hcd->speed == HCD_USB3) + return 0; + else + return 1; } /* There is one ehci_hci structure per controller */ struct xhci_hcd { struct usb_hcd *main_hcd; + struct usb_hcd *shared_hcd; /* glue to PCI and HCD framework */ struct xhci_cap_regs __iomem *cap_regs; struct xhci_op_regs __iomem *op_regs; @@ -1262,10 +1266,8 @@ struct xhci_hcd { #define XHCI_LINK_TRB_QUIRK (1 << 0) #define XHCI_RESET_EP_QUIRK (1 << 1) #define XHCI_NEC_HOST (1 << 2) - /* There's only one roothub to keep track of bus suspend info for - * (right now). - */ - struct xhci_bus_state bus_state[1]; + /* There are two roothubs to keep track of bus suspend info for */ + struct xhci_bus_state bus_state[2]; /* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */ u8 *port_array; /* Array of pointers to USB 3.0 PORTSC registers */ |