aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Wilcox <willy@linux.intel.com>2009-03-17 08:54:10 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-03-20 13:48:14 -0400
commit1c8d7b0a562da06d3ebe83f01b1ed553205d1ae4 (patch)
tree79c84432f5aed5a08b3bef262a10d933daae6a9b
parentf2440d9acbe866b917b16cc0f927366341ce9215 (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.txt45
-rw-r--r--arch/powerpc/kernel/msi.c4
-rw-r--r--arch/x86/kernel/io_apic.c4
-rw-r--r--drivers/pci/msi.c91
-rw-r--r--drivers/pci/msi.h6
-rw-r--r--include/linux/msi.h1
-rw-r--r--include/linux/pci.h6
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()
94since enabling MSIs disables the pin-based IRQ and the driver will not 94since enabling MSIs disables the pin-based IRQ and the driver will not
95receive interrupts on the old interrupt. 95receive interrupts on the old interrupt.
96 96
974.2.2 pci_disable_msi 974.2.2 pci_enable_msi_block
98
99int pci_enable_msi_block(struct pci_dev *dev, int count)
100
101This variation on the above call allows a device driver to request multiple
102MSIs. The MSI specification only allows interrupts to be allocated in
103powers of two, up to a maximum of 2^5 (32).
104
105If this function returns 0, it has succeeded in allocating at least as many
106interrupts as the driver requested (it may have allocated more in order
107to satisfy the power-of-two requirement). In this case, the function
108enables MSI on this device and updates dev->irq to be the lowest of
109the new interrupts assigned to it. The other interrupts assigned to
110the device are in the range dev->irq to dev->irq + count - 1.
111
112If this function returns a negative number, it indicates an error and
113the driver should not attempt to request any more MSI interrupts for
114this device. If this function returns a positive number, it will be
115less than 'count' and indicate the number of interrupts that could have
116been allocated. In neither case will the irq value have been
117updated, nor will the device have been switched into MSI mode.
118
119The device driver must decide what action to take if
120pci_enable_msi_block() returns a value less than the number asked for.
121Some devices can make use of fewer interrupts than the maximum they
122request; in this case the driver should call pci_enable_msi_block()
123again. 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
125pci_enable_msi_block(). This is because there are multiple constraints
126on the number of vectors that can be allocated; pci_enable_msi_block()
127will return as soon as it finds any constraint that doesn't allow the
128call to succeed.
129
1304.2.3 pci_disable_msi
98 131
99void pci_disable_msi(struct pci_dev *dev) 132void pci_disable_msi(struct pci_dev *dev)
100 133
101This function should be used to undo the effect of pci_enable_msi(). 134This function should be used to undo the effect of pci_enable_msi() or
102Calling it restores dev->irq to the pin-based interrupt number and frees 135pci_enable_msi_block(). Calling it restores dev->irq to the pin-based
103the previously allocated message signaled interrupt(s). The interrupt 136interrupt number and frees the previously allocated message signaled
104may subsequently be assigned to another device, so drivers should not 137interrupt(s). The interrupt may subsequently be assigned to another
105cache the value of dev->irq. 138device, so drivers should not cache the value of dev->irq.
106 139
107A device driver must always call free_irq() on the interrupt(s) 140A device driver must always call free_irq() on the interrupt(s)
108for which it has called request_irq() before calling this function. 141for 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.
341static int msi_capability_init(struct pci_dev *dev) 360 */
361static 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
536int pci_enable_msi(struct pci_dev* dev) 557 * other interrupt numbers allocated to this device are consecutive.
558 */
559int 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}
555EXPORT_SYMBOL(pci_enable_msi); 588EXPORT_SYMBOL(pci_enable_msi_block);
556 589
557void pci_msi_shutdown(struct pci_dev *dev) 590void 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);
21struct msi_desc { 21struct 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
792static inline int pci_enable_msi(struct pci_dev *dev) 792static 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
827extern int pci_enable_msi(struct pci_dev *dev); 827extern int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec);
828extern void pci_msi_shutdown(struct pci_dev *dev); 828extern void pci_msi_shutdown(struct pci_dev *dev);
829extern void pci_disable_msi(struct pci_dev *dev); 829extern void pci_disable_msi(struct pci_dev *dev);
830extern int pci_msix_table_size(struct pci_dev *dev); 830extern int pci_msix_table_size(struct pci_dev *dev);
@@ -846,6 +846,8 @@ static inline int pcie_aspm_enabled(void)
846extern int pcie_aspm_enabled(void); 846extern 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 */
851int ht_create_irq(struct pci_dev *dev, int idx); 853int ht_create_irq(struct pci_dev *dev, int idx);