diff options
Diffstat (limited to 'drivers/usb/host/xhci.c')
-rw-r--r-- | drivers/usb/host/xhci.c | 343 |
1 files changed, 287 insertions, 56 deletions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index d5c550ea3e6..5d7d4e951ea 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -551,6 +551,218 @@ void xhci_shutdown(struct usb_hcd *hcd) xhci_readl(xhci, &xhci->op_regs->status)); } +#ifdef CONFIG_PM +static void xhci_save_registers(struct xhci_hcd *xhci) +{ + xhci->s3.command = xhci_readl(xhci, &xhci->op_regs->command); + xhci->s3.dev_nt = xhci_readl(xhci, &xhci->op_regs->dev_notification); + xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); + xhci->s3.config_reg = xhci_readl(xhci, &xhci->op_regs->config_reg); + xhci->s3.irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending); + xhci->s3.irq_control = xhci_readl(xhci, &xhci->ir_set->irq_control); + xhci->s3.erst_size = xhci_readl(xhci, &xhci->ir_set->erst_size); + xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base); + xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); +} + +static void xhci_restore_registers(struct xhci_hcd *xhci) +{ + xhci_writel(xhci, xhci->s3.command, &xhci->op_regs->command); + xhci_writel(xhci, xhci->s3.dev_nt, &xhci->op_regs->dev_notification); + xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr); + xhci_writel(xhci, xhci->s3.config_reg, &xhci->op_regs->config_reg); + xhci_writel(xhci, xhci->s3.irq_pending, &xhci->ir_set->irq_pending); + xhci_writel(xhci, xhci->s3.irq_control, &xhci->ir_set->irq_control); + xhci_writel(xhci, xhci->s3.erst_size, &xhci->ir_set->erst_size); + xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base); +} + +/* + * Stop HC (not bus-specific) + * + * This is called when the machine transition into S3/S4 mode. + * + */ +int xhci_suspend(struct xhci_hcd *xhci) +{ + int rc = 0; + struct usb_hcd *hcd = xhci_to_hcd(xhci); + u32 command; + + spin_lock_irq(&xhci->lock); + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + /* step 1: stop endpoint */ + /* skipped assuming that port suspend has done */ + + /* step 2: clear Run/Stop bit */ + command = xhci_readl(xhci, &xhci->op_regs->command); + command &= ~CMD_RUN; + xhci_writel(xhci, command, &xhci->op_regs->command); + if (handshake(xhci, &xhci->op_regs->status, + STS_HALT, STS_HALT, 100*100)) { + xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n"); + spin_unlock_irq(&xhci->lock); + return -ETIMEDOUT; + } + + /* step 3: save registers */ + xhci_save_registers(xhci); + + /* step 4: set CSS flag */ + command = xhci_readl(xhci, &xhci->op_regs->command); + command |= CMD_CSS; + xhci_writel(xhci, command, &xhci->op_regs->command); + if (handshake(xhci, &xhci->op_regs->status, STS_SAVE, 0, 10*100)) { + xhci_warn(xhci, "WARN: xHC CMD_CSS timeout\n"); + spin_unlock_irq(&xhci->lock); + return -ETIMEDOUT; + } + /* step 5: remove core well power */ + xhci_cleanup_msix(xhci); + spin_unlock_irq(&xhci->lock); + + return rc; +} + +/* + * start xHC (not bus-specific) + * + * This is called when the machine transition from S3/S4 mode. + * + */ +int xhci_resume(struct xhci_hcd *xhci, bool hibernated) +{ + u32 command, temp = 0; + struct usb_hcd *hcd = xhci_to_hcd(xhci); + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + u64 val_64; + int old_state, retval; + + old_state = hcd->state; + if (time_before(jiffies, xhci->next_statechange)) + msleep(100); + + spin_lock_irq(&xhci->lock); + + if (!hibernated) { + /* step 1: restore register */ + xhci_restore_registers(xhci); + /* step 2: initialize command ring buffer */ + val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); + val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) | + (xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, + xhci->cmd_ring->dequeue) & + (u64) ~CMD_RING_RSVD_BITS) | + xhci->cmd_ring->cycle_state; + xhci_dbg(xhci, "// Setting command ring address to 0x%llx\n", + (long unsigned long) val_64); + xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring); + /* step 3: restore state and start state*/ + /* step 3: set CRS flag */ + command = xhci_readl(xhci, &xhci->op_regs->command); + command |= CMD_CRS; + xhci_writel(xhci, command, &xhci->op_regs->command); + if (handshake(xhci, &xhci->op_regs->status, + STS_RESTORE, 0, 10*100)) { + xhci_dbg(xhci, "WARN: xHC CMD_CSS timeout\n"); + spin_unlock_irq(&xhci->lock); + return -ETIMEDOUT; + } + temp = xhci_readl(xhci, &xhci->op_regs->status); + } + + /* If restore operation fails, re-initialize the HC during resume */ + if ((temp & STS_SRE) || hibernated) { + usb_root_hub_lost_power(hcd->self.root_hub); + + xhci_dbg(xhci, "Stop HCD\n"); + xhci_halt(xhci); + xhci_reset(xhci); + if (hibernated) + xhci_cleanup_msix(xhci); + spin_unlock_irq(&xhci->lock); + +#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING + /* Tell the event ring poll function not to reschedule */ + xhci->zombie = 1; + del_timer_sync(&xhci->event_ring_timer); +#endif + + xhci_dbg(xhci, "// Disabling event ring interrupts\n"); + temp = xhci_readl(xhci, &xhci->op_regs->status); + xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status); + temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); + xhci_writel(xhci, ER_IRQ_DISABLE(temp), + &xhci->ir_set->irq_pending); + xhci_print_ir_set(xhci, xhci->ir_set, 0); + + xhci_dbg(xhci, "cleaning up memory\n"); + xhci_mem_cleanup(xhci); + xhci_dbg(xhci, "xhci_stop completed - status = %x\n", + xhci_readl(xhci, &xhci->op_regs->status)); + + xhci_dbg(xhci, "Initialize the HCD\n"); + retval = xhci_init(hcd); + if (retval) + return retval; + + xhci_dbg(xhci, "Start the HCD\n"); + retval = xhci_run(hcd); + if (!retval) + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + hcd->state = HC_STATE_SUSPENDED; + return retval; + } + + /* Re-setup MSI-X */ + if (hcd->irq) + free_irq(hcd->irq, hcd); + hcd->irq = -1; + + retval = xhci_setup_msix(xhci); + if (retval) + /* fall back to msi*/ + retval = xhci_setup_msi(xhci); + + if (retval) { + /* fall back to legacy interrupt*/ + retval = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, + hcd->irq_descr, hcd); + if (retval) { + xhci_err(xhci, "request interrupt %d failed\n", + pdev->irq); + return retval; + } + hcd->irq = pdev->irq; + } + + /* step 4: set Run/Stop bit */ + command = xhci_readl(xhci, &xhci->op_regs->command); + command |= CMD_RUN; + xhci_writel(xhci, command, &xhci->op_regs->command); + handshake(xhci, &xhci->op_regs->status, STS_HALT, + 0, 250 * 1000); + + /* step 5: walk topology and initialize portsc, + * portpmsc and portli + */ + /* this is done in bus_resume */ + + /* step 6: restart each of the previously + * Running endpoints by ringing their doorbells + */ + + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + if (!hibernated) + hcd->state = old_state; + else + hcd->state = HC_STATE_SUSPENDED; + + spin_unlock_irq(&xhci->lock); + return 0; +} +#endif /* CONFIG_PM */ + /*-------------------------------------------------------------------------*/ /** @@ -607,7 +819,11 @@ unsigned int xhci_last_valid_endpoint(u32 added_ctxs) * returns 0 this is a root hub; returns -EINVAL for NULL pointers. */ int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev, - struct usb_host_endpoint *ep, int check_ep, const char *func) { + struct usb_host_endpoint *ep, int check_ep, bool check_virt_dev, + const char *func) { + struct xhci_hcd *xhci; + struct xhci_virt_device *virt_dev; + if (!hcd || (check_ep && !ep) || !udev) { printk(KERN_DEBUG "xHCI %s called with invalid args\n", func); @@ -618,11 +834,24 @@ int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev, func); return 0; } - if (!udev->slot_id) { - printk(KERN_DEBUG "xHCI %s called with unaddressed device\n", - func); - return -EINVAL; + + if (check_virt_dev) { + xhci = hcd_to_xhci(hcd); + if (!udev->slot_id || !xhci->devs + || !xhci->devs[udev->slot_id]) { + printk(KERN_DEBUG "xHCI %s called with unaddressed " + "device\n", func); + return -EINVAL; + } + + virt_dev = xhci->devs[udev->slot_id]; + if (virt_dev->udev != udev) { + printk(KERN_DEBUG "xHCI %s called with udev and " + "virt_dev does not match\n", func); + return -EINVAL; + } } + return 1; } @@ -704,18 +933,13 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) struct urb_priv *urb_priv; int size, i; - if (!urb || xhci_check_args(hcd, urb->dev, urb->ep, true, __func__) <= 0) + if (!urb || xhci_check_args(hcd, urb->dev, urb->ep, + true, true, __func__) <= 0) return -EINVAL; slot_id = urb->dev->slot_id; ep_index = xhci_get_endpoint_index(&urb->ep->desc); - if (!xhci->devs || !xhci->devs[slot_id]) { - if (!in_interrupt()) - dev_warn(&urb->dev->dev, "WARN: urb submitted for dev with no Slot ID\n"); - ret = -EINVAL; - goto exit; - } if (!HCD_HW_ACCESSIBLE(hcd)) { if (!in_interrupt()) xhci_dbg(xhci, "urb submitted during PCI suspend\n"); @@ -956,7 +1180,7 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ep->stop_cmd_timer.expires = jiffies + XHCI_STOP_EP_CMD_TIMEOUT * HZ; add_timer(&ep->stop_cmd_timer); - xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index); + xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index, 0); xhci_ring_cmd_db(xhci); } done: @@ -991,7 +1215,7 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, u32 new_add_flags, new_drop_flags, new_slot_info; int ret; - ret = xhci_check_args(hcd, udev, ep, 1, __func__); + ret = xhci_check_args(hcd, udev, ep, 1, true, __func__); if (ret <= 0) return ret; xhci = hcd_to_xhci(hcd); @@ -1004,12 +1228,6 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, return 0; } - if (!xhci->devs || !xhci->devs[udev->slot_id]) { - xhci_warn(xhci, "xHCI %s called with unaddressed device\n", - __func__); - return -EINVAL; - } - in_ctx = xhci->devs[udev->slot_id]->in_ctx; out_ctx = xhci->devs[udev->slot_id]->out_ctx; ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); @@ -1078,7 +1296,7 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, u32 new_add_flags, new_drop_flags, new_slot_info; int ret = 0; - ret = xhci_check_args(hcd, udev, ep, 1, __func__); + ret = xhci_check_args(hcd, udev, ep, 1, true, __func__); if (ret <= 0) { /* So we won't queue a reset ep command for a root hub */ ep->hcpriv = NULL; @@ -1098,12 +1316,6 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, return 0; } - if (!xhci->devs || !xhci->devs[udev->slot_id]) { - xhci_warn(xhci, "xHCI %s called with unaddressed device\n", - __func__); - return -EINVAL; - } - in_ctx = xhci->devs[udev->slot_id]->in_ctx; out_ctx = xhci->devs[udev->slot_id]->out_ctx; ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); @@ -1346,16 +1558,11 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_input_control_ctx *ctrl_ctx; struct xhci_slot_ctx *slot_ctx; - ret = xhci_check_args(hcd, udev, NULL, 0, __func__); + ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__); if (ret <= 0) return ret; xhci = hcd_to_xhci(hcd); - if (!udev->slot_id || !xhci->devs || !xhci->devs[udev->slot_id]) { - xhci_warn(xhci, "xHCI %s called with unaddressed device\n", - __func__); - return -EINVAL; - } xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev); virt_dev = xhci->devs[udev->slot_id]; @@ -1405,16 +1612,11 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_virt_device *virt_dev; int i, ret; - ret = xhci_check_args(hcd, udev, NULL, 0, __func__); + ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__); if (ret <= 0) return; xhci = hcd_to_xhci(hcd); - if (!xhci->devs || !xhci->devs[udev->slot_id]) { - xhci_warn(xhci, "xHCI %s called with unaddressed device\n", - __func__); - return; - } xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev); virt_dev = xhci->devs[udev->slot_id]; /* Free any rings allocated for added endpoints */ @@ -1575,7 +1777,7 @@ static int xhci_check_streams_endpoint(struct xhci_hcd *xhci, if (!ep) return -EINVAL; - ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, 1, __func__); + ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, 1, true, __func__); if (ret <= 0) return -EINVAL; if (ep->ss_ep_comp.bmAttributes == 0) { @@ -1953,8 +2155,13 @@ int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, * Wait for the Reset Device command to finish. Remove all structures * associated with the endpoints that were disabled. Clear the input device * structure? Cache the rings? Reset the control endpoint 0 max packet size? + * + * If the virt_dev to be reset does not exist or does not match the udev, + * it means the device is lost, possibly due to the xHC restore error and + * re-initialization during S3/S4. In this case, call xhci_alloc_dev() to + * re-allocate the device. */ -int xhci_reset_device(struct usb_hcd *hcd, struct usb_device *udev) +int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) { int ret, i; unsigned long flags; @@ -1965,16 +2172,35 @@ int xhci_reset_device(struct usb_hcd *hcd, struct usb_device *udev) int timeleft; int last_freed_endpoint; - ret = xhci_check_args(hcd, udev, NULL, 0, __func__); + ret = xhci_check_args(hcd, udev, NULL, 0, false, __func__); if (ret <= 0) return ret; xhci = hcd_to_xhci(hcd); slot_id = udev->slot_id; virt_dev = xhci->devs[slot_id]; if (!virt_dev) { - xhci_dbg(xhci, "%s called with invalid slot ID %u\n", - __func__, slot_id); - return -EINVAL; + xhci_dbg(xhci, "The device to be reset with slot ID %u does " + "not exist. Re-allocate the device\n", slot_id); + ret = xhci_alloc_dev(hcd, udev); + if (ret == 1) + return 0; + else + return -EINVAL; + } + + if (virt_dev->udev != udev) { + /* If the virt_dev and the udev does not match, this virt_dev + * may belong to another udev. + * Re-allocate the device. + */ + xhci_dbg(xhci, "The device to be reset with slot ID %u does " + "not match the udev. Re-allocate the device\n", + slot_id); + ret = xhci_alloc_dev(hcd, udev); + if (ret == 1) + return 0; + else + return -EINVAL; } xhci_dbg(xhci, "Resetting device with slot ID %u\n", slot_id); @@ -2077,13 +2303,13 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_virt_device *virt_dev; unsigned long flags; u32 state; - int i; + int i, ret; - if (udev->slot_id == 0) + ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__); + if (ret <= 0) return; + virt_dev = xhci->devs[udev->slot_id]; - if (!virt_dev) - return; /* Stop any wayward timer functions (which may grab the lock) */ for (i = 0; i < 31; ++i) { @@ -2191,12 +2417,17 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) virt_dev = xhci->devs[udev->slot_id]; - /* If this is a Set Address to an unconfigured device, setup ep 0 */ - if (!udev->config) + slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); + /* + * If this is the first Set Address since device plug-in or + * virt_device realloaction after a resume with an xHCI power loss, + * then set up the slot context. + */ + if (!slot_ctx->dev_info) xhci_setup_addressable_virt_dev(xhci, udev); + /* Otherwise, update the control endpoint ring enqueue pointer. */ else xhci_copy_ep0_dequeue_into_input_ctx(xhci, udev); - /* Otherwise, assume the core has the device configured how it wants */ xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id); xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2); @@ -2268,15 +2499,15 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) * address given back to us by the HC. */ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx); - udev->devnum = (slot_ctx->dev_state & DEV_ADDR_MASK) + 1; + /* Use kernel assigned address for devices; store xHC assigned + * address locally. */ + virt_dev->address = (slot_ctx->dev_state & DEV_ADDR_MASK) + 1; /* Zero the input context control for later use */ ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx); ctrl_ctx->add_flags = 0; ctrl_ctx->drop_flags = 0; - xhci_dbg(xhci, "Device address = %d\n", udev->devnum); - /* XXX Meh, not sure if anyone else but choose_address uses this. */ - set_bit(udev->devnum, udev->bus->devmap.devicemap); + xhci_dbg(xhci, "Internal device address = %d\n", virt_dev->address); return 0; } |