>From f00f3c5df2393291a20f8b515d79d2d5638a85d1 Mon Sep 17 00:00:00 2001 From: Dong Nguyen <Dong.Nguyen@xxxxxxx> Date: Mon, 10 May 2010 18:35:39 +0800 Subject: [PATCH] xHCI: supporting MSI/MSI-X Enable MSI/MSI-X supporting in xhci driver. It also provides the mechanism to fall back using MSI and Legacy IRQs if MSI-X IRQs register failed. Signed-off-by: Dong Nguyen <Dong.Nguyen@xxxxxxx> Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx> --- drivers/usb/host/xhci-pci.c | 1 - drivers/usb/host/xhci.c | 152 ++++++++++++++++++++++++++++++++---------- drivers/usb/host/xhci.h | 2 +- 3 files changed, 117 insertions(+), 38 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 9714db9..f319112 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -113,7 +113,6 @@ static const struct hc_driver xhci_pci_hc_driver = { /* * generic hardware linkage */ - .irq = xhci_irq, .flags = HCD_MEMORY | HCD_USB3, /* diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index a22a1e6..43d7fc4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -20,6 +20,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/pci.h> #include <linux/irq.h> #include <linux/log2.h> #include <linux/module.h> @@ -33,6 +34,7 @@ /* Some 0.95 hardware can't handle the chain bit on a Link TRB being cleared */ static int link_quirk; +static irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd); module_param(link_quirk, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(link_quirk, "Don't clear the chain bit on a link TRB"); @@ -133,22 +135,78 @@ int xhci_reset(struct xhci_hcd *xhci) return handshake(xhci, &xhci->op_regs->command, CMD_RESET, 0, 250 * 1000); } +/* + * Free IRQs + * free all IRQs request + */ +static void xhci_free_irq(struct xhci_hcd *xhci) +{ + int i; + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + + if (xhci->msix_entries) { + for (i = 0; i < xhci->msix_count; i++) + if (xhci->msix_entries[i].vector) + free_irq(xhci->msix_entries[i].vector, + xhci_to_hcd(xhci)); + } else { + free_irq(pdev->irq, xhci_to_hcd(xhci)); + } + + return; +} + +/* + * Set up MSI + */ +static int xhci_setup_msi(struct xhci_hcd *xhci) +{ + int ret; + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + + ret = pci_enable_msi(pdev); + if (ret) { + xhci_err(xhci, "failed to allocate MSI entry\n"); + return ret; + } + + ret = request_irq(pdev->irq, (irq_handler_t)xhci_msi_irq, + 0, "xhci_hcd", xhci_to_hcd(xhci)); + if (ret) { + xhci_err(xhci, "disable MSI interrupt\n"); + pci_disable_msi(pdev); + } + + return ret; +} -#if 0 -/* Set up MSI-X table for entry 0 (may claim other entries later) */ +/* + * Set up MSI-X + */ static int xhci_setup_msix(struct xhci_hcd *xhci) { - int ret; + int i, ret = 0; + int cpus = num_online_cpus(); + int msix_vecs, max_vecs; struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); - xhci->msix_count = 0; - /* XXX: did I do this right? ixgbe does kcalloc for more than one */ - xhci->msix_entries = kmalloc(sizeof(struct msix_entry), GFP_KERNEL); + xhci->msix_entries = NULL; + max_vecs = HCS_MAX_INTRS(xhci->hcs_params1); + msix_vecs = min(cpus + 1, max_vecs); + xhci->msix_count = msix_vecs; + + xhci->msix_entries = + kmalloc((sizeof(struct msix_entry))*xhci->msix_count, + GFP_KERNEL); if (!xhci->msix_entries) { xhci_err(xhci, "Failed to allocate MSI-X entries\n"); return -ENOMEM; } - xhci->msix_entries[0].entry = 0; + + for (i = 0; i < xhci->msix_count; i++) { + xhci->msix_entries[i].entry = i; + xhci->msix_entries[i].vector = 0; + } ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count); if (ret) { @@ -156,20 +214,18 @@ static int xhci_setup_msix(struct xhci_hcd *xhci) goto free_entries; } - /* - * Pass the xhci pointer value as the request_irq "cookie". - * If more irqs are added, this will need to be unique for each one. - */ - ret = request_irq(xhci->msix_entries[0].vector, &xhci_irq, 0, - "xHCI", xhci_to_hcd(xhci)); - if (ret) { - xhci_err(xhci, "Failed to allocate MSI-X interrupt\n"); - goto disable_msix; + for (i = 0; i < xhci->msix_count; i++) { + ret = request_irq(xhci->msix_entries[i].vector, + (irq_handler_t)xhci_msi_irq, + 0, "xhci_hcd", xhci_to_hcd(xhci)); + if (ret) + goto disable_msix; } - xhci_dbg(xhci, "Finished setting up MSI-X\n"); - return 0; + return ret; disable_msix: + xhci_err(xhci, "disable MSI-X interrupt\n"); + xhci_free_irq(xhci); pci_disable_msix(pdev); free_entries: kfree(xhci->msix_entries); @@ -177,21 +233,23 @@ free_entries: return ret; } -/* XXX: code duplication; can xhci_setup_msix call this? */ /* Free any IRQs and disable MSI-X */ static void xhci_cleanup_msix(struct xhci_hcd *xhci) { struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); - if (!xhci->msix_entries) - return; - free_irq(xhci->msix_entries[0].vector, xhci); - pci_disable_msix(pdev); - kfree(xhci->msix_entries); - xhci->msix_entries = NULL; - xhci_dbg(xhci, "Finished cleaning up MSI-X\n"); + xhci_free_irq(xhci); + + if (xhci->msix_entries) { + pci_disable_msix(pdev); + kfree(xhci->msix_entries); + xhci->msix_entries = NULL; + } else { + pci_disable_msi(pdev); + } + + return; } -#endif /* * Initialize memory for HCD and xHC (one-time init). @@ -314,6 +372,16 @@ hw_died: return IRQ_HANDLED; } +static irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd) +{ + irqreturn_t ret; + + clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + + ret = xhci_irq(hcd); + return ret; +} + #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING void xhci_event_ring_work(unsigned long arg) { @@ -385,21 +453,35 @@ int xhci_run(struct usb_hcd *hcd) { u32 temp; u64 temp_64; + u32 ret; struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); void (*doorbell)(struct xhci_hcd *) = NULL; hcd->uses_new_polling = 1; hcd->poll_rh = 0; xhci_dbg(xhci, "xhci_run\n"); -#if 0 /* FIXME: MSI not setup yet */ + /* Do this at the very last minute */ ret = xhci_setup_msix(xhci); - if (!ret) - return ret; + if (ret) + /* fall back to msi*/ + ret = xhci_setup_msi(xhci); + if (ret) { + /* fall back to legacy IRQ */ + ret = request_irq(pdev->irq, + (irq_handler_t)xhci_irq, + IRQF_SHARED, + "xhci_hcd", + xhci_to_hcd(xhci)); + if (ret) { + xhci_err(xhci, + "Failed to request irq interrupt\n"); + return ret; + } + } - return -ENOSYS; -#endif #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING init_timer(&xhci->event_ring_timer); xhci->event_ring_timer.data = (unsigned long) xhci; @@ -483,9 +565,8 @@ void xhci_stop(struct usb_hcd *hcd) xhci_reset(xhci); spin_unlock_irq(&xhci->lock); -#if 0 /* No MSI yet */ xhci_cleanup_msix(xhci); -#endif + #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING /* Tell the event ring poll function not to reschedule */ xhci->zombie = 1; @@ -521,9 +602,7 @@ void xhci_shutdown(struct usb_hcd *hcd) xhci_halt(xhci); spin_unlock_irq(&xhci->lock); -#if 0 xhci_cleanup_msix(xhci); -#endif xhci_dbg(xhci, "xhci_shutdown completed - status = %x\n", xhci_readl(xhci, &xhci->op_regs->status)); @@ -803,6 +882,7 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) xhci = hcd_to_xhci(hcd); spin_lock_irqsave(&xhci->lock, flags); + /* Make sure the URB hasn't completed or been unlinked already */ ret = usb_hcd_check_unlink_urb(hcd, urb, status); if (ret || !urb->hcpriv) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index fb3440b..a32f644 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1121,7 +1121,7 @@ struct xhci_hcd { int page_size; /* Valid values are 12 to 20, inclusive */ int page_shift; - /* only one MSI vector for now, but might need more later */ + /* msi-x vectors */ int msix_count; struct msix_entry *msix_entries; /* data structures */ -- 1.6.3.3 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html