Commit d1ac1d26 authored by Alexander Gordeev's avatar Alexander Gordeev Committed by Bjorn Helgaas

PCI/MSI: Add pci_msi_vec_count()

Device drivers can use this interface to obtain the maximum number of MSI
interrupts the device supports and use that number, e.g., in a subsequent
call to pci_enable_msi_block().
Signed-off-by: default avatarAlexander Gordeev <agordeev@redhat.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Reviewed-by: default avatarTejun Heo <tj@kernel.org>
parent 52179dc9
...@@ -169,6 +169,21 @@ on any interrupt for which it previously called request_irq(). ...@@ -169,6 +169,21 @@ on any interrupt for which it previously called request_irq().
Failure to do so results in a BUG_ON(), leaving the device with Failure to do so results in a BUG_ON(), leaving the device with
MSI enabled and thus leaking its vector. MSI enabled and thus leaking its vector.
4.2.5 pci_msi_vec_count
int pci_msi_vec_count(struct pci_dev *dev)
This function could be used to retrieve the number of MSI vectors the
device requested (via the Multiple Message Capable register). The MSI
specification only allows the returned value to be a power of two,
up to a maximum of 2^5 (32).
If this function returns a negative number, it indicates the device is
not capable of sending MSIs.
If this function returns a positive number, it indicates the maximum
number of MSI interrupt vectors that could be allocated.
4.3 Using MSI-X 4.3 Using MSI-X
The MSI-X capability is much more flexible than the MSI capability. The MSI-X capability is much more flexible than the MSI capability.
......
...@@ -842,6 +842,31 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type) ...@@ -842,6 +842,31 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type)
return 0; return 0;
} }
/**
* pci_msi_vec_count - Return the number of MSI vectors a device can send
* @dev: device to report about
*
* This function returns the number of MSI vectors a device requested via
* Multiple Message Capable register. It returns a negative errno if the
* device is not capable sending MSI interrupts. Otherwise, the call succeeds
* and returns a power of two, up to a maximum of 2^5 (32), according to the
* MSI specification.
**/
int pci_msi_vec_count(struct pci_dev *dev)
{
int ret;
u16 msgctl;
if (!dev->msi_cap)
return -EINVAL;
pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl);
ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1);
return ret;
}
EXPORT_SYMBOL(pci_msi_vec_count);
/** /**
* pci_enable_msi_block - configure device's MSI capability structure * pci_enable_msi_block - configure device's MSI capability structure
* @dev: device to configure * @dev: device to configure
...@@ -858,13 +883,13 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type) ...@@ -858,13 +883,13 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type)
int pci_enable_msi_block(struct pci_dev *dev, int nvec) int pci_enable_msi_block(struct pci_dev *dev, int nvec)
{ {
int status, maxvec; int status, maxvec;
u16 msgctl;
if (!dev->msi_cap || dev->current_state != PCI_D0) if (dev->current_state != PCI_D0)
return -EINVAL; return -EINVAL;
pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); maxvec = pci_msi_vec_count(dev);
maxvec = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); if (maxvec < 0)
return maxvec;
if (nvec > maxvec) if (nvec > maxvec)
return maxvec; return maxvec;
...@@ -889,13 +914,13 @@ EXPORT_SYMBOL(pci_enable_msi_block); ...@@ -889,13 +914,13 @@ EXPORT_SYMBOL(pci_enable_msi_block);
int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec) int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec)
{ {
int ret, nvec; int ret, nvec;
u16 msgctl;
if (!dev->msi_cap || dev->current_state != PCI_D0) if (dev->current_state != PCI_D0)
return -EINVAL; return -EINVAL;
pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); ret = pci_msi_vec_count(dev);
ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); if (ret < 0)
return ret;
if (maxvec) if (maxvec)
*maxvec = ret; *maxvec = ret;
......
...@@ -1154,6 +1154,11 @@ struct msix_entry { ...@@ -1154,6 +1154,11 @@ struct msix_entry {
#ifndef CONFIG_PCI_MSI #ifndef CONFIG_PCI_MSI
static inline int pci_msi_vec_count(struct pci_dev *dev)
{
return -ENOSYS;
}
static inline int pci_enable_msi_block(struct pci_dev *dev, int nvec) static inline int pci_enable_msi_block(struct pci_dev *dev, int nvec)
{ {
return -ENOSYS; return -ENOSYS;
...@@ -1195,6 +1200,7 @@ static inline int pci_msi_enabled(void) ...@@ -1195,6 +1200,7 @@ static inline int pci_msi_enabled(void)
return 0; return 0;
} }
#else #else
int pci_msi_vec_count(struct pci_dev *dev);
int pci_enable_msi_block(struct pci_dev *dev, int nvec); int pci_enable_msi_block(struct pci_dev *dev, int nvec);
int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec); int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec);
void pci_msi_shutdown(struct pci_dev *dev); void pci_msi_shutdown(struct pci_dev *dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment