USB: xHCI: Supporting MSI/MSI-X

Enable MSI/MSI-X supporting in xhci driver.

Provide the mechanism to fall back using MSI and Legacy IRQs
if MSI-X IRQs register failed.

Signed-off-by: Dong Nguyen <Dong.Nguyen@amd.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>,
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Dong Nguyen 2010-07-21 16:56:08 -07:00 committed by Greg Kroah-Hartman
parent 13dd0c9767
commit 43b86af83d
3 changed files with 131 additions and 44 deletions

View file

@ -2085,6 +2085,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
local_irq_restore(flags); local_irq_restore(flags);
return rc; return rc;
} }
EXPORT_SYMBOL_GPL(usb_hcd_irq);
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/

View file

@ -20,6 +20,7 @@
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <linux/pci.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/module.h> #include <linux/module.h>
@ -171,22 +172,95 @@ int xhci_reset(struct xhci_hcd *xhci)
return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000); return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000);
} }
static irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd)
#if 0
/* Set up MSI-X table for entry 0 (may claim other entries later) */
static int xhci_setup_msix(struct xhci_hcd *xhci)
{ {
int ret; irqreturn_t ret;
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
ret = xhci_irq(hcd);
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); struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
xhci->msix_count = 0; /* return if using legacy interrupt */
/* XXX: did I do this right? ixgbe does kcalloc for more than one */ if (xhci_to_hcd(xhci)->irq >= 0)
xhci->msix_entries = kmalloc(sizeof(struct msix_entry), GFP_KERNEL); return;
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 if (pdev->irq >= 0)
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;
}
/*
* Set up MSI-X
*/
static int xhci_setup_msix(struct xhci_hcd *xhci)
{
int i, ret = 0;
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
/*
* calculate number of msi-x vectors supported.
* - HCS_MAX_INTRS: the max number of interrupts the host can handle,
* with max number of interrupters based on the xhci HCSPARAMS1.
* - num_online_cpus: maximum msi-x vectors per CPUs core.
* Add additional 1 vector to ensure always available interrupt.
*/
xhci->msix_count = min(num_online_cpus() + 1,
HCS_MAX_INTRS(xhci->hcs_params1));
xhci->msix_entries =
kmalloc((sizeof(struct msix_entry))*xhci->msix_count,
GFP_KERNEL);
if (!xhci->msix_entries) { if (!xhci->msix_entries) {
xhci_err(xhci, "Failed to allocate MSI-X entries\n"); xhci_err(xhci, "Failed to allocate MSI-X entries\n");
return -ENOMEM; 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); ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count);
if (ret) { if (ret) {
@ -194,20 +268,19 @@ static int xhci_setup_msix(struct xhci_hcd *xhci)
goto free_entries; goto free_entries;
} }
/* for (i = 0; i < xhci->msix_count; i++) {
* Pass the xhci pointer value as the request_irq "cookie". ret = request_irq(xhci->msix_entries[i].vector,
* If more irqs are added, this will need to be unique for each one. (irq_handler_t)xhci_msi_irq,
*/ 0, "xhci_hcd", xhci_to_hcd(xhci));
ret = request_irq(xhci->msix_entries[0].vector, &xhci_irq, 0, if (ret)
"xHCI", xhci_to_hcd(xhci)); goto disable_msix;
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; return ret;
disable_msix: disable_msix:
xhci_err(xhci, "disable MSI-X interrupt\n");
xhci_free_irq(xhci);
pci_disable_msix(pdev); pci_disable_msix(pdev);
free_entries: free_entries:
kfree(xhci->msix_entries); kfree(xhci->msix_entries);
@ -215,21 +288,23 @@ static int xhci_setup_msix(struct xhci_hcd *xhci)
return ret; return ret;
} }
/* XXX: code duplication; can xhci_setup_msix call this? */
/* Free any IRQs and disable MSI-X */ /* Free any IRQs and disable MSI-X */
static void xhci_cleanup_msix(struct xhci_hcd *xhci) static void xhci_cleanup_msix(struct xhci_hcd *xhci)
{ {
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); 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); xhci_free_irq(xhci);
pci_disable_msix(pdev);
kfree(xhci->msix_entries); if (xhci->msix_entries) {
xhci->msix_entries = NULL; pci_disable_msix(pdev);
xhci_dbg(xhci, "Finished cleaning up MSI-X\n"); kfree(xhci->msix_entries);
xhci->msix_entries = NULL;
} else {
pci_disable_msi(pdev);
}
return;
} }
#endif
/* /*
* Initialize memory for HCD and xHC (one-time init). * Initialize memory for HCD and xHC (one-time init).
@ -423,20 +498,36 @@ int xhci_run(struct usb_hcd *hcd)
{ {
u32 temp; u32 temp;
u64 temp_64; u64 temp_64;
u32 ret;
struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
void (*doorbell)(struct xhci_hcd *) = NULL; void (*doorbell)(struct xhci_hcd *) = NULL;
hcd->uses_new_polling = 1; hcd->uses_new_polling = 1;
xhci_dbg(xhci, "xhci_run\n"); xhci_dbg(xhci, "xhci_run\n");
#if 0 /* FIXME: MSI not setup yet */ /* unregister the legacy interrupt */
/* Do this at the very last minute */ if (hcd->irq)
ret = xhci_setup_msix(xhci); free_irq(hcd->irq, hcd);
if (!ret) hcd->irq = -1;
return ret;
ret = xhci_setup_msix(xhci);
if (ret)
/* fall back to msi*/
ret = xhci_setup_msi(xhci);
if (ret) {
/* fall back to legacy interrupt*/
ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
hcd->irq_descr, hcd);
if (ret) {
xhci_err(xhci, "request interrupt %d failed\n",
pdev->irq);
return ret;
}
hcd->irq = pdev->irq;
}
return -ENOSYS;
#endif
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
init_timer(&xhci->event_ring_timer); init_timer(&xhci->event_ring_timer);
xhci->event_ring_timer.data = (unsigned long) xhci; xhci->event_ring_timer.data = (unsigned long) xhci;
@ -520,11 +611,9 @@ void xhci_stop(struct usb_hcd *hcd)
spin_lock_irq(&xhci->lock); spin_lock_irq(&xhci->lock);
xhci_halt(xhci); xhci_halt(xhci);
xhci_reset(xhci); xhci_reset(xhci);
xhci_cleanup_msix(xhci);
spin_unlock_irq(&xhci->lock); spin_unlock_irq(&xhci->lock);
#if 0 /* No MSI yet */
xhci_cleanup_msix(xhci);
#endif
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
/* Tell the event ring poll function not to reschedule */ /* Tell the event ring poll function not to reschedule */
xhci->zombie = 1; xhci->zombie = 1;
@ -558,11 +647,8 @@ void xhci_shutdown(struct usb_hcd *hcd)
spin_lock_irq(&xhci->lock); spin_lock_irq(&xhci->lock);
xhci_halt(xhci); xhci_halt(xhci);
spin_unlock_irq(&xhci->lock);
#if 0
xhci_cleanup_msix(xhci); xhci_cleanup_msix(xhci);
#endif spin_unlock_irq(&xhci->lock);
xhci_dbg(xhci, "xhci_shutdown completed - status = %x\n", xhci_dbg(xhci, "xhci_shutdown completed - status = %x\n",
xhci_readl(xhci, &xhci->op_regs->status)); xhci_readl(xhci, &xhci->op_regs->status));

View file

@ -1130,7 +1130,7 @@ struct xhci_hcd {
int page_size; int page_size;
/* Valid values are 12 to 20, inclusive */ /* Valid values are 12 to 20, inclusive */
int page_shift; int page_shift;
/* only one MSI vector for now, but might need more later */ /* msi-x vectors */
int msix_count; int msix_count;
struct msix_entry *msix_entries; struct msix_entry *msix_entries;
/* data structures */ /* data structures */