diff options
author | Dong Nguyen <Dong.Nguyen@amd.com> | 2010-07-21 19:56:08 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-08-10 17:35:40 -0400 |
commit | 43b86af83da7db8b2c6d85ca970203950e5bad88 (patch) | |
tree | dce6f46e7902bb295d2520c5ef9d3191e1e31489 /drivers | |
parent | 13dd0c9767349b280cf131c34461f85e5effc42a (diff) |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/core/hcd.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 166 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 2 |
3 files changed, 128 insertions, 41 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index c5753c797735..5cca00a6d09d 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c | |||
@@ -2085,6 +2085,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) | |||
2085 | local_irq_restore(flags); | 2085 | local_irq_restore(flags); |
2086 | return rc; | 2086 | return rc; |
2087 | } | 2087 | } |
2088 | EXPORT_SYMBOL_GPL(usb_hcd_irq); | ||
2088 | 2089 | ||
2089 | /*-------------------------------------------------------------------------*/ | 2090 | /*-------------------------------------------------------------------------*/ |
2090 | 2091 | ||
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 5e73386b3899..3106d22ae053 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c | |||
@@ -20,6 +20,7 @@ | |||
20 | * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 20 | * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
21 | */ | 21 | */ |
22 | 22 | ||
23 | #include <linux/pci.h> | ||
23 | #include <linux/irq.h> | 24 | #include <linux/irq.h> |
24 | #include <linux/log2.h> | 25 | #include <linux/log2.h> |
25 | #include <linux/module.h> | 26 | #include <linux/module.h> |
@@ -171,22 +172,95 @@ int xhci_reset(struct xhci_hcd *xhci) | |||
171 | return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000); | 172 | return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000); |
172 | } | 173 | } |
173 | 174 | ||
175 | static irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd) | ||
176 | { | ||
177 | irqreturn_t ret; | ||
174 | 178 | ||
175 | #if 0 | 179 | set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); |
176 | /* Set up MSI-X table for entry 0 (may claim other entries later) */ | 180 | |
177 | static int xhci_setup_msix(struct xhci_hcd *xhci) | 181 | ret = xhci_irq(hcd); |
182 | |||
183 | return ret; | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * Free IRQs | ||
188 | * free all IRQs request | ||
189 | */ | ||
190 | static void xhci_free_irq(struct xhci_hcd *xhci) | ||
191 | { | ||
192 | int i; | ||
193 | struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); | ||
194 | |||
195 | /* return if using legacy interrupt */ | ||
196 | if (xhci_to_hcd(xhci)->irq >= 0) | ||
197 | return; | ||
198 | |||
199 | if (xhci->msix_entries) { | ||
200 | for (i = 0; i < xhci->msix_count; i++) | ||
201 | if (xhci->msix_entries[i].vector) | ||
202 | free_irq(xhci->msix_entries[i].vector, | ||
203 | xhci_to_hcd(xhci)); | ||
204 | } else if (pdev->irq >= 0) | ||
205 | free_irq(pdev->irq, xhci_to_hcd(xhci)); | ||
206 | |||
207 | return; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Set up MSI | ||
212 | */ | ||
213 | static int xhci_setup_msi(struct xhci_hcd *xhci) | ||
178 | { | 214 | { |
179 | int ret; | 215 | int ret; |
216 | struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); | ||
217 | |||
218 | ret = pci_enable_msi(pdev); | ||
219 | if (ret) { | ||
220 | xhci_err(xhci, "failed to allocate MSI entry\n"); | ||
221 | return ret; | ||
222 | } | ||
223 | |||
224 | ret = request_irq(pdev->irq, (irq_handler_t)xhci_msi_irq, | ||
225 | 0, "xhci_hcd", xhci_to_hcd(xhci)); | ||
226 | if (ret) { | ||
227 | xhci_err(xhci, "disable MSI interrupt\n"); | ||
228 | pci_disable_msi(pdev); | ||
229 | } | ||
230 | |||
231 | return ret; | ||
232 | } | ||
233 | |||
234 | /* | ||
235 | * Set up MSI-X | ||
236 | */ | ||
237 | static int xhci_setup_msix(struct xhci_hcd *xhci) | ||
238 | { | ||
239 | int i, ret = 0; | ||
180 | struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); | 240 | struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); |
181 | 241 | ||
182 | xhci->msix_count = 0; | 242 | /* |
183 | /* XXX: did I do this right? ixgbe does kcalloc for more than one */ | 243 | * calculate number of msi-x vectors supported. |
184 | xhci->msix_entries = kmalloc(sizeof(struct msix_entry), GFP_KERNEL); | 244 | * - HCS_MAX_INTRS: the max number of interrupts the host can handle, |
245 | * with max number of interrupters based on the xhci HCSPARAMS1. | ||
246 | * - num_online_cpus: maximum msi-x vectors per CPUs core. | ||
247 | * Add additional 1 vector to ensure always available interrupt. | ||
248 | */ | ||
249 | xhci->msix_count = min(num_online_cpus() + 1, | ||
250 | HCS_MAX_INTRS(xhci->hcs_params1)); | ||
251 | |||
252 | xhci->msix_entries = | ||
253 | kmalloc((sizeof(struct msix_entry))*xhci->msix_count, | ||
254 | GFP_KERNEL); | ||
185 | if (!xhci->msix_entries) { | 255 | if (!xhci->msix_entries) { |
186 | xhci_err(xhci, "Failed to allocate MSI-X entries\n"); | 256 | xhci_err(xhci, "Failed to allocate MSI-X entries\n"); |
187 | return -ENOMEM; | 257 | return -ENOMEM; |
188 | } | 258 | } |
189 | xhci->msix_entries[0].entry = 0; | 259 | |
260 | for (i = 0; i < xhci->msix_count; i++) { | ||
261 | xhci->msix_entries[i].entry = i; | ||
262 | xhci->msix_entries[i].vector = 0; | ||
263 | } | ||
190 | 264 | ||
191 | ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count); | 265 | ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count); |
192 | if (ret) { | 266 | if (ret) { |
@@ -194,20 +268,19 @@ static int xhci_setup_msix(struct xhci_hcd *xhci) | |||
194 | goto free_entries; | 268 | goto free_entries; |
195 | } | 269 | } |
196 | 270 | ||
197 | /* | 271 | for (i = 0; i < xhci->msix_count; i++) { |
198 | * Pass the xhci pointer value as the request_irq "cookie". | 272 | ret = request_irq(xhci->msix_entries[i].vector, |
199 | * If more irqs are added, this will need to be unique for each one. | 273 | (irq_handler_t)xhci_msi_irq, |
200 | */ | 274 | 0, "xhci_hcd", xhci_to_hcd(xhci)); |
201 | ret = request_irq(xhci->msix_entries[0].vector, &xhci_irq, 0, | 275 | if (ret) |
202 | "xHCI", xhci_to_hcd(xhci)); | 276 | goto disable_msix; |
203 | if (ret) { | ||
204 | xhci_err(xhci, "Failed to allocate MSI-X interrupt\n"); | ||
205 | goto disable_msix; | ||
206 | } | 277 | } |
207 | xhci_dbg(xhci, "Finished setting up MSI-X\n"); | 278 | |
208 | return 0; | 279 | return ret; |
209 | 280 | ||
210 | disable_msix: | 281 | disable_msix: |
282 | xhci_err(xhci, "disable MSI-X interrupt\n"); | ||
283 | xhci_free_irq(xhci); | ||
211 | pci_disable_msix(pdev); | 284 | pci_disable_msix(pdev); |
212 | free_entries: | 285 | free_entries: |
213 | kfree(xhci->msix_entries); | 286 | kfree(xhci->msix_entries); |
@@ -215,21 +288,23 @@ free_entries: | |||
215 | return ret; | 288 | return ret; |
216 | } | 289 | } |
217 | 290 | ||
218 | /* XXX: code duplication; can xhci_setup_msix call this? */ | ||
219 | /* Free any IRQs and disable MSI-X */ | 291 | /* Free any IRQs and disable MSI-X */ |
220 | static void xhci_cleanup_msix(struct xhci_hcd *xhci) | 292 | static void xhci_cleanup_msix(struct xhci_hcd *xhci) |
221 | { | 293 | { |
222 | struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); | 294 | struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); |
223 | if (!xhci->msix_entries) | ||
224 | return; | ||
225 | 295 | ||
226 | free_irq(xhci->msix_entries[0].vector, xhci); | 296 | xhci_free_irq(xhci); |
227 | pci_disable_msix(pdev); | 297 | |
228 | kfree(xhci->msix_entries); | 298 | if (xhci->msix_entries) { |
229 | xhci->msix_entries = NULL; | 299 | pci_disable_msix(pdev); |
230 | xhci_dbg(xhci, "Finished cleaning up MSI-X\n"); | 300 | kfree(xhci->msix_entries); |
301 | xhci->msix_entries = NULL; | ||
302 | } else { | ||
303 | pci_disable_msi(pdev); | ||
304 | } | ||
305 | |||
306 | return; | ||
231 | } | 307 | } |
232 | #endif | ||
233 | 308 | ||
234 | /* | 309 | /* |
235 | * Initialize memory for HCD and xHC (one-time init). | 310 | * Initialize memory for HCD and xHC (one-time init). |
@@ -423,20 +498,36 @@ int xhci_run(struct usb_hcd *hcd) | |||
423 | { | 498 | { |
424 | u32 temp; | 499 | u32 temp; |
425 | u64 temp_64; | 500 | u64 temp_64; |
501 | u32 ret; | ||
426 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | 502 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
503 | struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); | ||
427 | void (*doorbell)(struct xhci_hcd *) = NULL; | 504 | void (*doorbell)(struct xhci_hcd *) = NULL; |
428 | 505 | ||
429 | hcd->uses_new_polling = 1; | 506 | hcd->uses_new_polling = 1; |
430 | 507 | ||
431 | xhci_dbg(xhci, "xhci_run\n"); | 508 | xhci_dbg(xhci, "xhci_run\n"); |
432 | #if 0 /* FIXME: MSI not setup yet */ | 509 | /* unregister the legacy interrupt */ |
433 | /* Do this at the very last minute */ | 510 | if (hcd->irq) |
511 | free_irq(hcd->irq, hcd); | ||
512 | hcd->irq = -1; | ||
513 | |||
434 | ret = xhci_setup_msix(xhci); | 514 | ret = xhci_setup_msix(xhci); |
435 | if (!ret) | 515 | if (ret) |
436 | return ret; | 516 | /* fall back to msi*/ |
517 | ret = xhci_setup_msi(xhci); | ||
518 | |||
519 | if (ret) { | ||
520 | /* fall back to legacy interrupt*/ | ||
521 | ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, | ||
522 | hcd->irq_descr, hcd); | ||
523 | if (ret) { | ||
524 | xhci_err(xhci, "request interrupt %d failed\n", | ||
525 | pdev->irq); | ||
526 | return ret; | ||
527 | } | ||
528 | hcd->irq = pdev->irq; | ||
529 | } | ||
437 | 530 | ||
438 | return -ENOSYS; | ||
439 | #endif | ||
440 | #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING | 531 | #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING |
441 | init_timer(&xhci->event_ring_timer); | 532 | init_timer(&xhci->event_ring_timer); |
442 | xhci->event_ring_timer.data = (unsigned long) xhci; | 533 | xhci->event_ring_timer.data = (unsigned long) xhci; |
@@ -520,11 +611,9 @@ void xhci_stop(struct usb_hcd *hcd) | |||
520 | spin_lock_irq(&xhci->lock); | 611 | spin_lock_irq(&xhci->lock); |
521 | xhci_halt(xhci); | 612 | xhci_halt(xhci); |
522 | xhci_reset(xhci); | 613 | xhci_reset(xhci); |
614 | xhci_cleanup_msix(xhci); | ||
523 | spin_unlock_irq(&xhci->lock); | 615 | spin_unlock_irq(&xhci->lock); |
524 | 616 | ||
525 | #if 0 /* No MSI yet */ | ||
526 | xhci_cleanup_msix(xhci); | ||
527 | #endif | ||
528 | #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING | 617 | #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING |
529 | /* Tell the event ring poll function not to reschedule */ | 618 | /* Tell the event ring poll function not to reschedule */ |
530 | xhci->zombie = 1; | 619 | xhci->zombie = 1; |
@@ -558,11 +647,8 @@ void xhci_shutdown(struct usb_hcd *hcd) | |||
558 | 647 | ||
559 | spin_lock_irq(&xhci->lock); | 648 | spin_lock_irq(&xhci->lock); |
560 | xhci_halt(xhci); | 649 | xhci_halt(xhci); |
561 | spin_unlock_irq(&xhci->lock); | ||
562 | |||
563 | #if 0 | ||
564 | xhci_cleanup_msix(xhci); | 650 | xhci_cleanup_msix(xhci); |
565 | #endif | 651 | spin_unlock_irq(&xhci->lock); |
566 | 652 | ||
567 | xhci_dbg(xhci, "xhci_shutdown completed - status = %x\n", | 653 | xhci_dbg(xhci, "xhci_shutdown completed - status = %x\n", |
568 | xhci_readl(xhci, &xhci->op_regs->status)); | 654 | xhci_readl(xhci, &xhci->op_regs->status)); |
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 6c7e3430ec93..5bc03d1c2beb 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h | |||
@@ -1130,7 +1130,7 @@ struct xhci_hcd { | |||
1130 | int page_size; | 1130 | int page_size; |
1131 | /* Valid values are 12 to 20, inclusive */ | 1131 | /* Valid values are 12 to 20, inclusive */ |
1132 | int page_shift; | 1132 | int page_shift; |
1133 | /* only one MSI vector for now, but might need more later */ | 1133 | /* msi-x vectors */ |
1134 | int msix_count; | 1134 | int msix_count; |
1135 | struct msix_entry *msix_entries; | 1135 | struct msix_entry *msix_entries; |
1136 | /* data structures */ | 1136 | /* data structures */ |