diff options
author | Michael Ellerman <michael@ellerman.id.au> | 2007-04-18 05:39:21 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-05-02 22:02:38 -0400 |
commit | 9c8313343c83c0ca731ceb8d2a4ab1e022ed9c94 (patch) | |
tree | 1e37ef3ebcdcb344adb74c1667ad04cf87010f5e /drivers | |
parent | 7fe3730de729b758e9f69b862b9255d998671b5f (diff) |
MSI: Give archs the option to allocate all MSI/Xs at once.
This patch introduces an optional function, arch_setup_msi_irqs(),
(note the plural) which gives an arch the opportunity to do per-device
setup for MSI/X and then allocate all the requested MSI/Xs at once.
If that's not required by the arch, the default version simply calls
arch_setup_msi_irq() for each MSI irq required.
arch_setup_msi_irqs() is passed a pdev, attached to the pdev is a list
of msi_descs with irq == 0, it is up to the arch to connect these up to
an irq (via set_irq_msi()) or return an error. For convenience the number
of vectors and the type are passed also.
All msi_descs with irq != 0 are considered allocated, and the arch
teardown routine will be called on them when necessary.
The existing semantics of pci_enable_msix() are that if the requested
number of irqs can not be allocated, the maximum number that _could_ be
allocated is returned. To support that, we define that in case of an
error from arch_setup_msi_irqs(), the number of msi_descs with irq != 0
are considered allocated, and are counted toward the "max that could be
allocated".
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/msi.c | 63 |
1 files changed, 44 insertions, 19 deletions
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 88362f1bd9cf..c71e8e4c7168 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c | |||
@@ -334,13 +334,15 @@ static int msi_capability_init(struct pci_dev *dev) | |||
334 | msi_mask_bits_reg(pos, is_64bit_address(control)), | 334 | msi_mask_bits_reg(pos, is_64bit_address(control)), |
335 | maskbits); | 335 | maskbits); |
336 | } | 336 | } |
337 | list_add(&entry->list, &dev->msi_list); | ||
338 | |||
337 | /* Configure MSI capability structure */ | 339 | /* Configure MSI capability structure */ |
338 | ret = arch_setup_msi_irq(dev, entry); | 340 | ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI); |
339 | if (ret) { | 341 | if (ret) { |
342 | list_del(&entry->list); | ||
340 | kfree(entry); | 343 | kfree(entry); |
341 | return ret; | 344 | return ret; |
342 | } | 345 | } |
343 | list_add(&entry->list, &dev->msi_list); | ||
344 | 346 | ||
345 | /* Set MSI enabled bits */ | 347 | /* Set MSI enabled bits */ |
346 | pci_intx(dev, 0); /* disable intx */ | 348 | pci_intx(dev, 0); /* disable intx */ |
@@ -365,7 +367,7 @@ static int msix_capability_init(struct pci_dev *dev, | |||
365 | struct msix_entry *entries, int nvec) | 367 | struct msix_entry *entries, int nvec) |
366 | { | 368 | { |
367 | struct msi_desc *entry; | 369 | struct msi_desc *entry; |
368 | int irq, pos, i, j, nr_entries, ret; | 370 | int pos, i, j, nr_entries, ret; |
369 | unsigned long phys_addr; | 371 | unsigned long phys_addr; |
370 | u32 table_offset; | 372 | u32 table_offset; |
371 | u16 control; | 373 | u16 control; |
@@ -404,30 +406,33 @@ static int msix_capability_init(struct pci_dev *dev, | |||
404 | entry->dev = dev; | 406 | entry->dev = dev; |
405 | entry->mask_base = base; | 407 | entry->mask_base = base; |
406 | 408 | ||
407 | /* Configure MSI-X capability structure */ | ||
408 | ret = arch_setup_msi_irq(dev, entry); | ||
409 | if (ret) { | ||
410 | kfree(entry); | ||
411 | break; | ||
412 | } | ||
413 | entries[i].vector = entry->irq; | ||
414 | list_add(&entry->list, &dev->msi_list); | 409 | list_add(&entry->list, &dev->msi_list); |
415 | } | 410 | } |
416 | if (i != nvec) { | 411 | |
417 | int avail = i - 1; | 412 | ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); |
418 | i--; | 413 | if (ret) { |
419 | for (; i >= 0; i--) { | 414 | int avail = 0; |
420 | irq = (entries + i)->vector; | 415 | list_for_each_entry(entry, &dev->msi_list, list) { |
421 | msi_free_irq(dev, irq); | 416 | if (entry->irq != 0) { |
422 | (entries + i)->vector = 0; | 417 | avail++; |
418 | msi_free_irq(dev, entry->irq); | ||
419 | } | ||
423 | } | 420 | } |
421 | |||
424 | /* If we had some success report the number of irqs | 422 | /* If we had some success report the number of irqs |
425 | * we succeeded in setting up. | 423 | * we succeeded in setting up. |
426 | */ | 424 | */ |
427 | if (avail <= 0) | 425 | if (avail == 0) |
428 | avail = -EBUSY; | 426 | avail = ret; |
429 | return avail; | 427 | return avail; |
430 | } | 428 | } |
429 | |||
430 | i = 0; | ||
431 | list_for_each_entry(entry, &dev->msi_list, list) { | ||
432 | entries[i].vector = entry->irq; | ||
433 | set_irq_msi(entry->irq, entry); | ||
434 | i++; | ||
435 | } | ||
431 | /* Set MSI-X enabled bits */ | 436 | /* Set MSI-X enabled bits */ |
432 | pci_intx(dev, 0); /* disable intx */ | 437 | pci_intx(dev, 0); /* disable intx */ |
433 | msix_set_enable(dev, 1); | 438 | msix_set_enable(dev, 1); |
@@ -694,3 +699,23 @@ arch_msi_check_device(struct pci_dev* dev, int nvec, int type) | |||
694 | return 0; | 699 | return 0; |
695 | } | 700 | } |
696 | 701 | ||
702 | int __attribute__ ((weak)) | ||
703 | arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *entry) | ||
704 | { | ||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | int __attribute__ ((weak)) | ||
709 | arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | ||
710 | { | ||
711 | struct msi_desc *entry; | ||
712 | int ret; | ||
713 | |||
714 | list_for_each_entry(entry, &dev->msi_list, list) { | ||
715 | ret = arch_setup_msi_irq(dev, entry); | ||
716 | if (ret) | ||
717 | return ret; | ||
718 | } | ||
719 | |||
720 | return 0; | ||
721 | } | ||