>From f4b85b13e4d3aa015197760cd333f110ab04f7bc Mon Sep 17 00:00:00 2001 From: Dong Nguyen <Dong.Nguyen@xxxxxxx> Date: Wed, 3 Mar 2010 12:31:06 -0800 Subject: [PATCH RFC] xHCI: supporting MSI/MSI-X This commit enables xHCI MSI and MSI-X support. In xhci_run(), driver will try to setup MSI-X; if fail, then try to setup MSI; if still fail, legacy IRQ will be used. Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx> --- drivers/usb/host/xhci-hcd.c | 213 +++++++++++++++++++++++++++++++++++-------- drivers/usb/host/xhci-pci.c | 5 + drivers/usb/host/xhci.h | 8 ++ 3 files changed, 187 insertions(+), 39 deletions(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 4cb69e0..ef16da6 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -20,6 +20,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/pci.h> #include <linux/irq.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -31,6 +32,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"); @@ -130,45 +132,148 @@ int xhci_reset(struct xhci_hcd *xhci) return handshake(xhci, &xhci->op_regs->command, CMD_RESET, 0, 250 * 1000); } +/* + * request IRQs from MSI-X. + * + * This requests for msix_count IRQs and set the xhci_flags properly. + */ +static int xhci_request_irq(struct xhci_hcd *xhci) +{ + int i, ret = 0; + irq_handler_t fn; + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + + if (xhci->xhci_flags & XHCI_FLAG_SUPPORT_MSIX) { + for (i = 0; i < xhci->msix_count; i++) { + fn = (irq_handler_t)xhci_msi_irq; + ret = request_irq(xhci->msix_entries[i].vector, + fn, 0, "xhci", xhci_to_hcd(xhci)); + if (ret) + goto disable_msix; + } + xhci->xhci_flags |= XHCI_FLAG_USING_MSIX; + } else if (xhci->xhci_flags & XHCI_FLAG_SUPPORT_MSI) { + ret = request_irq(pdev->irq, (irq_handler_t)xhci_msi_irq, + 0, "xhci", xhci_to_hcd(xhci)); + if (ret) + goto disable_msi; + + xhci->xhci_flags |= XHCI_FLAG_USING_MSI; + } else { + xhci_err(xhci, "Unknown xhci_flags for MSI-X interrupt\n"); + } + return 0; + +disable_msi: + xhci_err(xhci, "disable MSI interrupt\n"); + pci_disable_msi(pdev); + xhci->xhci_flags &= ~XHCI_FLAG_SUPPORT_MSI; + return ret; + +disable_msix: + xhci_err(xhci, "disable MSI-X interrupt\n"); + pci_disable_msix(pdev); + kfree(xhci->msix_entries); + xhci->msix_entries = NULL; + xhci->xhci_flags &= ~XHCI_FLAG_SUPPORT_MSIX; + return ret; +} + +/* + * 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->xhci_flags & XHCI_FLAG_USING_MSIX) { + if (!xhci->msix_entries) + return; -#if 0 -/* Set up MSI-X table for entry 0 (may claim other entries later) */ + for (i = 0; i < xhci->msix_count; i++) + free_irq(xhci->msix_entries[i].vector, + xhci_to_hcd(xhci)); + + xhci->xhci_flags &= ~XHCI_FLAG_USING_MSIX; + return; + } + + if (xhci->xhci_flags & XHCI_FLAG_USING_MSI) { + free_irq(pdev->irq, xhci_to_hcd(xhci)); + xhci->xhci_flags &= ~XHCI_FLAG_USING_MSI; + return; + } + + if (xhci->xhci_flags & XHCI_FLAG_USING_LEGACY_IRQ) { + free_irq(pdev->irq, xhci_to_hcd(xhci)); + xhci->xhci_flags &= ~XHCI_FLAG_USING_LEGACY_IRQ; + return; + } + + 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; + } + + xhci->xhci_flags |= XHCI_FLAG_SUPPORT_MSI; + ret = xhci_request_irq(xhci); + + return ret; +} + +/* + * setup MSI-X + * Set up MSI-X table for entry 0 (may claim other entries later) + */ 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; + xhci->msix_entries = NULL; + max_vecs = HCS_MAX_INTRS(xhci->hcs_params1); + msix_vecs = min(cpus + 1, max_vecs); + xhci->msix_count = msix_vecs; + /* 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 = + 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) { xhci_err(xhci, "Failed to enable MSI-X\n"); goto free_entries; } + xhci->xhci_flags |= XHCI_FLAG_SUPPORT_MSIX; + ret = xhci_request_irq(xhci); + return ret; - /* - * 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; - } - xhci_dbg(xhci, "Finished setting up MSI-X\n"); - return 0; - -disable_msix: - pci_disable_msix(pdev); free_entries: kfree(xhci->msix_entries); xhci->msix_entries = NULL; @@ -180,16 +285,28 @@ free_entries: 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) + + xhci_free_irq(xhci); + + if (xhci->xhci_flags & XHCI_FLAG_SUPPORT_MSIX) { + if (!xhci->msix_entries) + return; + + pci_disable_msix(pdev); + kfree(xhci->msix_entries); + xhci->msix_entries = NULL; + xhci->xhci_flags &= ~XHCI_FLAG_SUPPORT_MSIX; 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"); + if (xhci->xhci_flags & XHCI_FLAG_SUPPORT_MSI) { + pci_disable_msi(pdev); + xhci->xhci_flags &= ~XHCI_FLAG_SUPPORT_MSI; + return; + } + + return; } -#endif /* * Initialize memory for HCD and xHC (one-time init). @@ -312,6 +429,14 @@ hw_died: return IRQ_HANDLED; } +static irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd) +{ + irqreturn_t ret; + + ret = xhci_irq(hcd); + return ret; +} + #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING void xhci_event_ring_work(unsigned long arg) { @@ -385,23 +510,37 @@ void xhci_event_ring_work(unsigned long arg) */ int xhci_run(struct usb_hcd *hcd) { - u32 temp; + u32 temp, ret; u64 temp_64; 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) { + /* use msi*/ + ret = xhci_setup_msi(xhci); + if (ret) { + /* use legacy IRQ */ + ret = request_irq(pdev->irq, + (irq_handler_t)xhci_irq, + IRQF_SHARED, + "xhci", + 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; @@ -485,9 +624,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; @@ -522,10 +660,7 @@ void xhci_shutdown(struct usb_hcd *hcd) spin_lock_irq(&xhci->lock); 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)); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 21030a9..39e3402 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -111,7 +111,12 @@ static const struct hc_driver xhci_pci_hc_driver = { /* * generic hardware linkage */ + /* do not register irq for xhci HCD + * xhci use MSIx in xhci-hcd + */ +#if 0 .irq = xhci_irq, +#endif .flags = HCD_MEMORY | HCD_USB3, /* diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index e5eb09b..0d8b362 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1112,6 +1112,14 @@ struct xhci_hcd { unsigned int quirks; #define XHCI_LINK_TRB_QUIRK (1 << 0) #define XHCI_RESET_EP_QUIRK (1 << 1) + + u32 xhci_flags; +#define XHCI_FLAG_USING_LEGACY_IRQ 0x00000001 +#define XHCI_FLAG_SUPPORT_MSI 0x00000002 +#define XHCI_FLAG_SUPPORT_MSIX 0x00000004 +#define XHCI_FLAG_USING_MSI 0x00000008 +#define XHCI_FLAG_USING_MSIX 0x00000010 + }; /* For testing purposes */ -- 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