diff options
author | Alexander Gordeev <agordeev@redhat.com> | 2012-11-19 10:02:10 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2013-01-24 11:25:13 -0500 |
commit | 08261d87f7d1b6253ab3223756625a5c74532293 (patch) | |
tree | c0025a8e4593564bf356f1f185c21a137a96cb8a | |
parent | 51906e779f2b13b38f8153774c4c7163d412ffd9 (diff) |
PCI/MSI: Enable multiple MSIs with pci_enable_msi_block_auto()
The new function pci_enable_msi_block_auto() tries to allocate
maximum possible number of MSIs up to the number the device
supports. It generalizes a pattern when pci_enable_msi_block()
is contiguously called until it succeeds or fails.
Opposite to pci_enable_msi_block() which takes the number of
MSIs to allocate as a input parameter,
pci_enable_msi_block_auto() could be used by device drivers to
obtain the number of assigned MSIs and the number of MSIs the
device supports.
Signed-off-by: Alexander Gordeev <agordeev@redhat.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Suresh Siddha <suresh.b.siddha@intel.com>
Cc: Yinghai Lu <yinghai@kernel.org>
Cc: Matthew Wilcox <willy@linux.intel.com>
Cc: Jeff Garzik <jgarzik@pobox.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/c3de2419df94a0f95ca1a6f755afc421486455e6.1353324359.git.agordeev@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | Documentation/PCI/MSI-HOWTO.txt | 37 | ||||
-rw-r--r-- | drivers/pci/msi.c | 26 | ||||
-rw-r--r-- | include/linux/pci.h | 7 |
3 files changed, 65 insertions, 5 deletions
diff --git a/Documentation/PCI/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt index 53e6fca146d7..a09178086c30 100644 --- a/Documentation/PCI/MSI-HOWTO.txt +++ b/Documentation/PCI/MSI-HOWTO.txt | |||
@@ -127,15 +127,42 @@ on the number of vectors that can be allocated; pci_enable_msi_block() | |||
127 | returns as soon as it finds any constraint that doesn't allow the | 127 | returns as soon as it finds any constraint that doesn't allow the |
128 | call to succeed. | 128 | call to succeed. |
129 | 129 | ||
130 | 4.2.3 pci_disable_msi | 130 | 4.2.3 pci_enable_msi_block_auto |
131 | |||
132 | int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *count) | ||
133 | |||
134 | This variation on pci_enable_msi() call allows a device driver to request | ||
135 | the maximum possible number of MSIs. The MSI specification only allows | ||
136 | interrupts to be allocated in powers of two, up to a maximum of 2^5 (32). | ||
137 | |||
138 | If this function returns a positive number, it indicates that it has | ||
139 | succeeded and the returned value is the number of allocated interrupts. In | ||
140 | this case, the function enables MSI on this device and updates dev->irq to | ||
141 | be the lowest of the new interrupts assigned to it. The other interrupts | ||
142 | assigned to the device are in the range dev->irq to dev->irq + returned | ||
143 | value - 1. | ||
144 | |||
145 | If this function returns a negative number, it indicates an error and | ||
146 | the driver should not attempt to request any more MSI interrupts for | ||
147 | this device. | ||
148 | |||
149 | If the device driver needs to know the number of interrupts the device | ||
150 | supports it can pass the pointer count where that number is stored. The | ||
151 | device driver must decide what action to take if pci_enable_msi_block_auto() | ||
152 | succeeds, but returns a value less than the number of interrupts supported. | ||
153 | If the device driver does not need to know the number of interrupts | ||
154 | supported, it can set the pointer count to NULL. | ||
155 | |||
156 | 4.2.4 pci_disable_msi | ||
131 | 157 | ||
132 | void pci_disable_msi(struct pci_dev *dev) | 158 | void pci_disable_msi(struct pci_dev *dev) |
133 | 159 | ||
134 | This function should be used to undo the effect of pci_enable_msi() or | 160 | This function should be used to undo the effect of pci_enable_msi() or |
135 | pci_enable_msi_block(). Calling it restores dev->irq to the pin-based | 161 | pci_enable_msi_block() or pci_enable_msi_block_auto(). Calling it restores |
136 | interrupt number and frees the previously allocated message signaled | 162 | dev->irq to the pin-based interrupt number and frees the previously |
137 | interrupt(s). The interrupt may subsequently be assigned to another | 163 | allocated message signaled interrupt(s). The interrupt may subsequently be |
138 | device, so drivers should not cache the value of dev->irq. | 164 | assigned to another device, so drivers should not cache the value of |
165 | dev->irq. | ||
139 | 166 | ||
140 | Before calling this function, a device driver must always call free_irq() | 167 | Before calling this function, a device driver must always call free_irq() |
141 | on any interrupt for which it previously called request_irq(). | 168 | on any interrupt for which it previously called request_irq(). |
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 5099636a6e5f..00cc78c7aa04 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c | |||
@@ -845,6 +845,32 @@ int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) | |||
845 | } | 845 | } |
846 | EXPORT_SYMBOL(pci_enable_msi_block); | 846 | EXPORT_SYMBOL(pci_enable_msi_block); |
847 | 847 | ||
848 | int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec) | ||
849 | { | ||
850 | int ret, pos, nvec; | ||
851 | u16 msgctl; | ||
852 | |||
853 | pos = pci_find_capability(dev, PCI_CAP_ID_MSI); | ||
854 | if (!pos) | ||
855 | return -EINVAL; | ||
856 | |||
857 | pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); | ||
858 | ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); | ||
859 | |||
860 | if (maxvec) | ||
861 | *maxvec = ret; | ||
862 | |||
863 | do { | ||
864 | nvec = ret; | ||
865 | ret = pci_enable_msi_block(dev, nvec); | ||
866 | } while (ret > 0); | ||
867 | |||
868 | if (ret < 0) | ||
869 | return ret; | ||
870 | return nvec; | ||
871 | } | ||
872 | EXPORT_SYMBOL(pci_enable_msi_block_auto); | ||
873 | |||
848 | void pci_msi_shutdown(struct pci_dev *dev) | 874 | void pci_msi_shutdown(struct pci_dev *dev) |
849 | { | 875 | { |
850 | struct msi_desc *desc; | 876 | struct msi_desc *desc; |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 15472d691ee6..6fa4dd2a3b9e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -1101,6 +1101,12 @@ static inline int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) | |||
1101 | return -1; | 1101 | return -1; |
1102 | } | 1102 | } |
1103 | 1103 | ||
1104 | static inline int | ||
1105 | pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec) | ||
1106 | { | ||
1107 | return -1; | ||
1108 | } | ||
1109 | |||
1104 | static inline void pci_msi_shutdown(struct pci_dev *dev) | 1110 | static inline void pci_msi_shutdown(struct pci_dev *dev) |
1105 | { } | 1111 | { } |
1106 | static inline void pci_disable_msi(struct pci_dev *dev) | 1112 | static inline void pci_disable_msi(struct pci_dev *dev) |
@@ -1132,6 +1138,7 @@ static inline int pci_msi_enabled(void) | |||
1132 | } | 1138 | } |
1133 | #else | 1139 | #else |
1134 | extern int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec); | 1140 | extern int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec); |
1141 | extern int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec); | ||
1135 | extern void pci_msi_shutdown(struct pci_dev *dev); | 1142 | extern void pci_msi_shutdown(struct pci_dev *dev); |
1136 | extern void pci_disable_msi(struct pci_dev *dev); | 1143 | extern void pci_disable_msi(struct pci_dev *dev); |
1137 | extern int pci_msix_table_size(struct pci_dev *dev); | 1144 | extern int pci_msix_table_size(struct pci_dev *dev); |