diff options
author | Matthew Wilcox <willy@linux.intel.com> | 2009-03-17 08:54:10 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-03-20 13:48:14 -0400 |
commit | 1c8d7b0a562da06d3ebe83f01b1ed553205d1ae4 (patch) | |
tree | 79c84432f5aed5a08b3bef262a10d933daae6a9b | |
parent | f2440d9acbe866b917b16cc0f927366341ce9215 (diff) |
PCI MSI: Add support for multiple MSI
Add the new API pci_enable_msi_block() to allow drivers to
request multiple MSI and reimplement pci_enable_msi in terms of
pci_enable_msi_block. Ensure that the architecture back ends don't
have to know about multiple MSI.
Signed-off-by: Matthew Wilcox <willy@linux.intel.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r-- | Documentation/PCI/MSI-HOWTO.txt | 45 | ||||
-rw-r--r-- | arch/powerpc/kernel/msi.c | 4 | ||||
-rw-r--r-- | arch/x86/kernel/io_apic.c | 4 | ||||
-rw-r--r-- | drivers/pci/msi.c | 91 | ||||
-rw-r--r-- | drivers/pci/msi.h | 6 | ||||
-rw-r--r-- | include/linux/msi.h | 1 | ||||
-rw-r--r-- | include/linux/pci.h | 6 |
7 files changed, 116 insertions, 41 deletions
diff --git a/Documentation/PCI/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt index 1c02431f1d1a..9494f6dc38eb 100644 --- a/Documentation/PCI/MSI-HOWTO.txt +++ b/Documentation/PCI/MSI-HOWTO.txt | |||
@@ -94,15 +94,48 @@ This function should be called before the driver calls request_irq() | |||
94 | since enabling MSIs disables the pin-based IRQ and the driver will not | 94 | since enabling MSIs disables the pin-based IRQ and the driver will not |
95 | receive interrupts on the old interrupt. | 95 | receive interrupts on the old interrupt. |
96 | 96 | ||
97 | 4.2.2 pci_disable_msi | 97 | 4.2.2 pci_enable_msi_block |
98 | |||
99 | int pci_enable_msi_block(struct pci_dev *dev, int count) | ||
100 | |||
101 | This variation on the above call allows a device driver to request multiple | ||
102 | MSIs. The MSI specification only allows interrupts to be allocated in | ||
103 | powers of two, up to a maximum of 2^5 (32). | ||
104 | |||
105 | If this function returns 0, it has succeeded in allocating at least as many | ||
106 | interrupts as the driver requested (it may have allocated more in order | ||
107 | to satisfy the power-of-two requirement). In this case, the function | ||
108 | enables MSI on this device and updates dev->irq to be the lowest of | ||
109 | the new interrupts assigned to it. The other interrupts assigned to | ||
110 | the device are in the range dev->irq to dev->irq + count - 1. | ||
111 | |||
112 | If this function returns a negative number, it indicates an error and | ||
113 | the driver should not attempt to request any more MSI interrupts for | ||
114 | this device. If this function returns a positive number, it will be | ||
115 | less than 'count' and indicate the number of interrupts that could have | ||
116 | been allocated. In neither case will the irq value have been | ||
117 | updated, nor will the device have been switched into MSI mode. | ||
118 | |||
119 | The device driver must decide what action to take if | ||
120 | pci_enable_msi_block() returns a value less than the number asked for. | ||
121 | Some devices can make use of fewer interrupts than the maximum they | ||
122 | request; in this case the driver should call pci_enable_msi_block() | ||
123 | again. Note that it is not guaranteed to succeed, even when the | ||
124 | 'count' has been reduced to the value returned from a previous call to | ||
125 | pci_enable_msi_block(). This is because there are multiple constraints | ||
126 | on the number of vectors that can be allocated; pci_enable_msi_block() | ||
127 | will return as soon as it finds any constraint that doesn't allow the | ||
128 | call to succeed. | ||
129 | |||
130 | 4.2.3 pci_disable_msi | ||
98 | 131 | ||
99 | void pci_disable_msi(struct pci_dev *dev) | 132 | void pci_disable_msi(struct pci_dev *dev) |
100 | 133 | ||
101 | This function should be used to undo the effect of pci_enable_msi(). | 134 | This function should be used to undo the effect of pci_enable_msi() or |
102 | Calling it restores dev->irq to the pin-based interrupt number and frees | 135 | pci_enable_msi_block(). Calling it restores dev->irq to the pin-based |
103 | the previously allocated message signaled interrupt(s). The interrupt | 136 | interrupt number and frees the previously allocated message signaled |
104 | may subsequently be assigned to another device, so drivers should not | 137 | interrupt(s). The interrupt may subsequently be assigned to another |
105 | cache the value of dev->irq. | 138 | device, so drivers should not cache the value of dev->irq. |
106 | 139 | ||
107 | A device driver must always call free_irq() on the interrupt(s) | 140 | A device driver must always call free_irq() on the interrupt(s) |
108 | for which it has called request_irq() before calling this function. | 141 | for which it has called request_irq() before calling this function. |
diff --git a/arch/powerpc/kernel/msi.c b/arch/powerpc/kernel/msi.c index 3bb7d3dd28be..0c16e2a854e5 100644 --- a/arch/powerpc/kernel/msi.c +++ b/arch/powerpc/kernel/msi.c | |||
@@ -19,6 +19,10 @@ int arch_msi_check_device(struct pci_dev* dev, int nvec, int type) | |||
19 | return -ENOSYS; | 19 | return -ENOSYS; |
20 | } | 20 | } |
21 | 21 | ||
22 | /* PowerPC doesn't support multiple MSI yet */ | ||
23 | if (type == PCI_CAP_ID_MSI && nvec > 1) | ||
24 | return 1; | ||
25 | |||
22 | if (ppc_md.msi_check_device) { | 26 | if (ppc_md.msi_check_device) { |
23 | pr_debug("msi: Using platform check routine.\n"); | 27 | pr_debug("msi: Using platform check routine.\n"); |
24 | return ppc_md.msi_check_device(dev, nvec, type); | 28 | return ppc_md.msi_check_device(dev, nvec, type); |
diff --git a/arch/x86/kernel/io_apic.c b/arch/x86/kernel/io_apic.c index bc7ac4da90d7..a09549a6321b 100644 --- a/arch/x86/kernel/io_apic.c +++ b/arch/x86/kernel/io_apic.c | |||
@@ -3510,6 +3510,10 @@ int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | |||
3510 | int index = 0; | 3510 | int index = 0; |
3511 | #endif | 3511 | #endif |
3512 | 3512 | ||
3513 | /* x86 doesn't support multiple MSI yet */ | ||
3514 | if (type == PCI_CAP_ID_MSI && nvec > 1) | ||
3515 | return 1; | ||
3516 | |||
3513 | irq_want = nr_irqs_gsi; | 3517 | irq_want = nr_irqs_gsi; |
3514 | sub_handle = 0; | 3518 | sub_handle = 0; |
3515 | list_for_each_entry(msidesc, &dev->msi_list, list) { | 3519 | list_for_each_entry(msidesc, &dev->msi_list, list) { |
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index adcc78242571..6f2e6295e773 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c | |||
@@ -40,6 +40,13 @@ int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | |||
40 | struct msi_desc *entry; | 40 | struct msi_desc *entry; |
41 | int ret; | 41 | int ret; |
42 | 42 | ||
43 | /* | ||
44 | * If an architecture wants to support multiple MSI, it needs to | ||
45 | * override arch_setup_msi_irqs() | ||
46 | */ | ||
47 | if (type == PCI_CAP_ID_MSI && nvec > 1) | ||
48 | return 1; | ||
49 | |||
43 | list_for_each_entry(entry, &dev->msi_list, list) { | 50 | list_for_each_entry(entry, &dev->msi_list, list) { |
44 | ret = arch_setup_msi_irq(dev, entry); | 51 | ret = arch_setup_msi_irq(dev, entry); |
45 | if (ret < 0) | 52 | if (ret < 0) |
@@ -58,8 +65,12 @@ void arch_teardown_msi_irqs(struct pci_dev *dev) | |||
58 | struct msi_desc *entry; | 65 | struct msi_desc *entry; |
59 | 66 | ||
60 | list_for_each_entry(entry, &dev->msi_list, list) { | 67 | list_for_each_entry(entry, &dev->msi_list, list) { |
61 | if (entry->irq != 0) | 68 | int i, nvec; |
62 | arch_teardown_msi_irq(entry->irq); | 69 | if (entry->irq == 0) |
70 | continue; | ||
71 | nvec = 1 << entry->msi_attrib.multiple; | ||
72 | for (i = 0; i < nvec; i++) | ||
73 | arch_teardown_msi_irq(entry->irq + i); | ||
63 | } | 74 | } |
64 | } | 75 | } |
65 | #endif | 76 | #endif |
@@ -163,7 +174,8 @@ static void msi_set_mask_bit(unsigned irq, u32 flag) | |||
163 | msix_mask_irq(desc, flag); | 174 | msix_mask_irq(desc, flag); |
164 | readl(desc->mask_base); /* Flush write to device */ | 175 | readl(desc->mask_base); /* Flush write to device */ |
165 | } else { | 176 | } else { |
166 | msi_mask_irq(desc, 1, flag); | 177 | unsigned offset = irq - desc->dev->irq; |
178 | msi_mask_irq(desc, 1 << offset, flag << offset); | ||
167 | } | 179 | } |
168 | } | 180 | } |
169 | 181 | ||
@@ -229,6 +241,12 @@ void write_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg) | |||
229 | } else { | 241 | } else { |
230 | struct pci_dev *dev = entry->dev; | 242 | struct pci_dev *dev = entry->dev; |
231 | int pos = entry->msi_attrib.pos; | 243 | int pos = entry->msi_attrib.pos; |
244 | u16 msgctl; | ||
245 | |||
246 | pci_read_config_word(dev, msi_control_reg(pos), &msgctl); | ||
247 | msgctl &= ~PCI_MSI_FLAGS_QSIZE; | ||
248 | msgctl |= entry->msi_attrib.multiple << 4; | ||
249 | pci_write_config_word(dev, msi_control_reg(pos), msgctl); | ||
232 | 250 | ||
233 | pci_write_config_dword(dev, msi_lower_address_reg(pos), | 251 | pci_write_config_dword(dev, msi_lower_address_reg(pos), |
234 | msg->address_lo); | 252 | msg->address_lo); |
@@ -291,7 +309,7 @@ static void __pci_restore_msi_state(struct pci_dev *dev) | |||
291 | pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); | 309 | pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); |
292 | msi_mask_irq(entry, msi_capable_mask(control), entry->masked); | 310 | msi_mask_irq(entry, msi_capable_mask(control), entry->masked); |
293 | control &= ~PCI_MSI_FLAGS_QSIZE; | 311 | control &= ~PCI_MSI_FLAGS_QSIZE; |
294 | control |= PCI_MSI_FLAGS_ENABLE; | 312 | control |= (entry->msi_attrib.multiple << 4) | PCI_MSI_FLAGS_ENABLE; |
295 | pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); | 313 | pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); |
296 | } | 314 | } |
297 | 315 | ||
@@ -332,13 +350,15 @@ EXPORT_SYMBOL_GPL(pci_restore_msi_state); | |||
332 | /** | 350 | /** |
333 | * msi_capability_init - configure device's MSI capability structure | 351 | * msi_capability_init - configure device's MSI capability structure |
334 | * @dev: pointer to the pci_dev data structure of MSI device function | 352 | * @dev: pointer to the pci_dev data structure of MSI device function |
353 | * @nvec: number of interrupts to allocate | ||
335 | * | 354 | * |
336 | * Setup the MSI capability structure of device function with a single | 355 | * Setup the MSI capability structure of the device with the requested |
337 | * MSI irq, regardless of device function is capable of handling | 356 | * number of interrupts. A return value of zero indicates the successful |
338 | * multiple messages. A return of zero indicates the successful setup | 357 | * setup of an entry with the new MSI irq. A negative return value indicates |
339 | * of an entry zero with the new MSI irq or non-zero for otherwise. | 358 | * an error, and a positive return value indicates the number of interrupts |
340 | **/ | 359 | * which could have been allocated. |
341 | static int msi_capability_init(struct pci_dev *dev) | 360 | */ |
361 | static int msi_capability_init(struct pci_dev *dev, int nvec) | ||
342 | { | 362 | { |
343 | struct msi_desc *entry; | 363 | struct msi_desc *entry; |
344 | int pos, ret; | 364 | int pos, ret; |
@@ -371,7 +391,7 @@ static int msi_capability_init(struct pci_dev *dev) | |||
371 | list_add_tail(&entry->list, &dev->msi_list); | 391 | list_add_tail(&entry->list, &dev->msi_list); |
372 | 392 | ||
373 | /* Configure MSI capability structure */ | 393 | /* Configure MSI capability structure */ |
374 | ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI); | 394 | ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI); |
375 | if (ret) { | 395 | if (ret) { |
376 | msi_free_irqs(dev); | 396 | msi_free_irqs(dev); |
377 | return ret; | 397 | return ret; |
@@ -524,35 +544,48 @@ static int pci_msi_check_device(struct pci_dev* dev, int nvec, int type) | |||
524 | } | 544 | } |
525 | 545 | ||
526 | /** | 546 | /** |
527 | * pci_enable_msi - configure device's MSI capability structure | 547 | * pci_enable_msi_block - configure device's MSI capability structure |
528 | * @dev: pointer to the pci_dev data structure of MSI device function | 548 | * @dev: device to configure |
549 | * @nvec: number of interrupts to configure | ||
529 | * | 550 | * |
530 | * Setup the MSI capability structure of device function with | 551 | * Allocate IRQs for a device with the MSI capability. |
531 | * a single MSI irq upon its software driver call to request for | 552 | * This function returns a negative errno if an error occurs. If it |
532 | * MSI mode enabled on its hardware device function. A return of zero | 553 | * is unable to allocate the number of interrupts requested, it returns |
533 | * indicates the successful setup of an entry zero with the new MSI | 554 | * the number of interrupts it might be able to allocate. If it successfully |
534 | * irq or non-zero for otherwise. | 555 | * allocates at least the number of interrupts requested, it returns 0 and |
535 | **/ | 556 | * updates the @dev's irq member to the lowest new interrupt number; the |
536 | int pci_enable_msi(struct pci_dev* dev) | 557 | * other interrupt numbers allocated to this device are consecutive. |
558 | */ | ||
559 | int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) | ||
537 | { | 560 | { |
538 | int status; | 561 | int status, pos, maxvec; |
562 | u16 msgctl; | ||
563 | |||
564 | pos = pci_find_capability(dev, PCI_CAP_ID_MSI); | ||
565 | if (!pos) | ||
566 | return -EINVAL; | ||
567 | pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); | ||
568 | maxvec = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); | ||
569 | if (nvec > maxvec) | ||
570 | return maxvec; | ||
539 | 571 | ||
540 | status = pci_msi_check_device(dev, 1, PCI_CAP_ID_MSI); | 572 | status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI); |
541 | if (status) | 573 | if (status) |
542 | return status; | 574 | return status; |
543 | 575 | ||
544 | WARN_ON(!!dev->msi_enabled); | 576 | WARN_ON(!!dev->msi_enabled); |
545 | 577 | ||
546 | /* Check whether driver already requested for MSI-X irqs */ | 578 | /* Check whether driver already requested MSI-X irqs */ |
547 | if (dev->msix_enabled) { | 579 | if (dev->msix_enabled) { |
548 | dev_info(&dev->dev, "can't enable MSI " | 580 | dev_info(&dev->dev, "can't enable MSI " |
549 | "(MSI-X already enabled)\n"); | 581 | "(MSI-X already enabled)\n"); |
550 | return -EINVAL; | 582 | return -EINVAL; |
551 | } | 583 | } |
552 | status = msi_capability_init(dev); | 584 | |
585 | status = msi_capability_init(dev, nvec); | ||
553 | return status; | 586 | return status; |
554 | } | 587 | } |
555 | EXPORT_SYMBOL(pci_enable_msi); | 588 | EXPORT_SYMBOL(pci_enable_msi_block); |
556 | 589 | ||
557 | void pci_msi_shutdown(struct pci_dev *dev) | 590 | void pci_msi_shutdown(struct pci_dev *dev) |
558 | { | 591 | { |
@@ -599,8 +632,12 @@ static int msi_free_irqs(struct pci_dev* dev) | |||
599 | struct msi_desc *entry, *tmp; | 632 | struct msi_desc *entry, *tmp; |
600 | 633 | ||
601 | list_for_each_entry(entry, &dev->msi_list, list) { | 634 | list_for_each_entry(entry, &dev->msi_list, list) { |
602 | if (entry->irq) | 635 | int i, nvec; |
603 | BUG_ON(irq_has_action(entry->irq)); | 636 | if (!entry->irq) |
637 | continue; | ||
638 | nvec = 1 << entry->msi_attrib.multiple; | ||
639 | for (i = 0; i < nvec; i++) | ||
640 | BUG_ON(irq_has_action(entry->irq + i)); | ||
604 | } | 641 | } |
605 | 642 | ||
606 | arch_teardown_msi_irqs(dev); | 643 | arch_teardown_msi_irqs(dev); |
diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h index 3898f5237144..71f4df2ef654 100644 --- a/drivers/pci/msi.h +++ b/drivers/pci/msi.h | |||
@@ -20,14 +20,8 @@ | |||
20 | #define msi_mask_bits_reg(base, is64bit) \ | 20 | #define msi_mask_bits_reg(base, is64bit) \ |
21 | ( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4) | 21 | ( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4) |
22 | #define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE | 22 | #define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE |
23 | #define multi_msi_capable(control) \ | ||
24 | (1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1)) | ||
25 | #define multi_msi_enable(control, num) \ | ||
26 | control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE); | ||
27 | #define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT)) | 23 | #define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT)) |
28 | #define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT)) | 24 | #define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT)) |
29 | #define msi_enable(control, num) multi_msi_enable(control, num); \ | ||
30 | control |= PCI_MSI_FLAGS_ENABLE | ||
31 | 25 | ||
32 | #define msix_table_offset_reg(base) (base + 0x04) | 26 | #define msix_table_offset_reg(base) (base + 0x04) |
33 | #define msix_pba_offset_reg(base) (base + 0x08) | 27 | #define msix_pba_offset_reg(base) (base + 0x08) |
diff --git a/include/linux/msi.h b/include/linux/msi.h index 37c1bbe546e5..6991ab5b24d1 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h | |||
@@ -21,6 +21,7 @@ extern void write_msi_msg(unsigned int irq, struct msi_msg *msg); | |||
21 | struct msi_desc { | 21 | struct msi_desc { |
22 | struct { | 22 | struct { |
23 | __u8 is_msix : 1; | 23 | __u8 is_msix : 1; |
24 | __u8 multiple: 3; /* log2 number of messages */ | ||
24 | __u8 maskbit : 1; /* mask-pending bit supported ? */ | 25 | __u8 maskbit : 1; /* mask-pending bit supported ? */ |
25 | __u8 is_64 : 1; /* Address size: 0=32bit 1=64bit */ | 26 | __u8 is_64 : 1; /* Address size: 0=32bit 1=64bit */ |
26 | __u8 pos; /* Location of the msi capability */ | 27 | __u8 pos; /* Location of the msi capability */ |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 7baf2a5db12a..1f6c5ddaae36 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -789,7 +789,7 @@ struct msix_entry { | |||
789 | 789 | ||
790 | 790 | ||
791 | #ifndef CONFIG_PCI_MSI | 791 | #ifndef CONFIG_PCI_MSI |
792 | static inline int pci_enable_msi(struct pci_dev *dev) | 792 | static inline int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) |
793 | { | 793 | { |
794 | return -1; | 794 | return -1; |
795 | } | 795 | } |
@@ -824,7 +824,7 @@ static inline int pci_msi_enabled(void) | |||
824 | return 0; | 824 | return 0; |
825 | } | 825 | } |
826 | #else | 826 | #else |
827 | extern int pci_enable_msi(struct pci_dev *dev); | 827 | extern int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec); |
828 | extern void pci_msi_shutdown(struct pci_dev *dev); | 828 | extern void pci_msi_shutdown(struct pci_dev *dev); |
829 | extern void pci_disable_msi(struct pci_dev *dev); | 829 | extern void pci_disable_msi(struct pci_dev *dev); |
830 | extern int pci_msix_table_size(struct pci_dev *dev); | 830 | extern int pci_msix_table_size(struct pci_dev *dev); |
@@ -846,6 +846,8 @@ static inline int pcie_aspm_enabled(void) | |||
846 | extern int pcie_aspm_enabled(void); | 846 | extern int pcie_aspm_enabled(void); |
847 | #endif | 847 | #endif |
848 | 848 | ||
849 | #define pci_enable_msi(pdev) pci_enable_msi_block(pdev, 1) | ||
850 | |||
849 | #ifdef CONFIG_HT_IRQ | 851 | #ifdef CONFIG_HT_IRQ |
850 | /* The functions a driver should call */ | 852 | /* The functions a driver should call */ |
851 | int ht_create_irq(struct pci_dev *dev, int idx); | 853 | int ht_create_irq(struct pci_dev *dev, int idx); |