Commit 04f982be authored by Bjorn Helgaas's avatar Bjorn Helgaas

Merge branch 'pci/msi' into next

* pci/msi:
  PCI/MSI: Add pci_enable_msi_range() and pci_enable_msix_range()
  PCI/MSI: Add pci_msix_vec_count()
  PCI/MSI: Remove pci_enable_msi_block_auto()
  PCI/MSI: Add pci_msi_vec_count()
parents ccb12654 302a2523
This diff is collapsed.
...@@ -1095,26 +1095,40 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host) ...@@ -1095,26 +1095,40 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host)
{} {}
#endif #endif
int ahci_init_interrupts(struct pci_dev *pdev, struct ahci_host_priv *hpriv) int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
struct ahci_host_priv *hpriv)
{ {
int rc; int rc, nvec;
unsigned int maxvec;
if (!(hpriv->flags & AHCI_HFLAG_NO_MSI)) { if (hpriv->flags & AHCI_HFLAG_NO_MSI)
rc = pci_enable_msi_block_auto(pdev, &maxvec); goto intx;
if (rc > 0) {
if ((rc == maxvec) || (rc == 1)) rc = pci_msi_vec_count(pdev);
return rc; if (rc < 0)
/* goto intx;
* Assume that advantage of multipe MSIs is negated,
* so fallback to single MSI mode to save resources /*
*/ * If number of MSIs is less than number of ports then Sharing Last
pci_disable_msi(pdev); * Message mode could be enforced. In this case assume that advantage
if (!pci_enable_msi(pdev)) * of multipe MSIs is negated and use single MSI mode instead.
return 1; */
} if (rc < n_ports)
} goto single_msi;
nvec = rc;
rc = pci_enable_msi_block(pdev, nvec);
if (rc)
goto intx;
return nvec;
single_msi:
rc = pci_enable_msi(pdev);
if (rc)
goto intx;
return 1;
intx:
pci_intx(pdev, 1); pci_intx(pdev, 1);
return 0; return 0;
} }
...@@ -1281,10 +1295,6 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -1281,10 +1295,6 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar]; hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar];
n_msis = ahci_init_interrupts(pdev, hpriv);
if (n_msis > 1)
hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
/* save initial config */ /* save initial config */
ahci_pci_save_initial_config(pdev, hpriv); ahci_pci_save_initial_config(pdev, hpriv);
...@@ -1339,6 +1349,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -1339,6 +1349,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
*/ */
n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
n_msis = ahci_init_interrupts(pdev, n_ports, hpriv);
if (n_msis > 1)
hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
if (!host) if (!host)
return -ENOMEM; return -ENOMEM;
......
...@@ -851,6 +851,31 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type) ...@@ -851,6 +851,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
...@@ -867,13 +892,13 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type) ...@@ -867,13 +892,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;
...@@ -895,31 +920,6 @@ int pci_enable_msi_block(struct pci_dev *dev, int nvec) ...@@ -895,31 +920,6 @@ int pci_enable_msi_block(struct pci_dev *dev, int nvec)
} }
EXPORT_SYMBOL(pci_enable_msi_block); EXPORT_SYMBOL(pci_enable_msi_block);
int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec)
{
int ret, nvec;
u16 msgctl;
if (!dev->msi_cap || dev->current_state != PCI_D0)
return -EINVAL;
pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl);
ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1);
if (maxvec)
*maxvec = ret;
do {
nvec = ret;
ret = pci_enable_msi_block(dev, nvec);
} while (ret > 0);
if (ret < 0)
return ret;
return nvec;
}
EXPORT_SYMBOL(pci_enable_msi_block_auto);
void pci_msi_shutdown(struct pci_dev *dev) void pci_msi_shutdown(struct pci_dev *dev)
{ {
struct msi_desc *desc; struct msi_desc *desc;
...@@ -957,19 +957,25 @@ void pci_disable_msi(struct pci_dev *dev) ...@@ -957,19 +957,25 @@ void pci_disable_msi(struct pci_dev *dev)
EXPORT_SYMBOL(pci_disable_msi); EXPORT_SYMBOL(pci_disable_msi);
/** /**
* pci_msix_table_size - return the number of device's MSI-X table entries * pci_msix_vec_count - return the number of device's MSI-X table entries
* @dev: pointer to the pci_dev data structure of MSI-X device function * @dev: pointer to the pci_dev data structure of MSI-X device function
*/
int pci_msix_table_size(struct pci_dev *dev) * This function returns the number of device's MSI-X table entries and
* therefore the number of MSI-X vectors device is capable of sending.
* It returns a negative errno if the device is not capable of sending MSI-X
* interrupts.
**/
int pci_msix_vec_count(struct pci_dev *dev)
{ {
u16 control; u16 control;
if (!dev->msix_cap) if (!dev->msix_cap)
return 0; return -EINVAL;
pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control); pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control);
return msix_table_size(control); return msix_table_size(control);
} }
EXPORT_SYMBOL(pci_msix_vec_count);
/** /**
* pci_enable_msix - configure device's MSI-X capability structure * pci_enable_msix - configure device's MSI-X capability structure
...@@ -998,7 +1004,9 @@ int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec) ...@@ -998,7 +1004,9 @@ int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec)
if (status) if (status)
return status; return status;
nr_entries = pci_msix_table_size(dev); nr_entries = pci_msix_vec_count(dev);
if (nr_entries < 0)
return nr_entries;
if (nvec > nr_entries) if (nvec > nr_entries)
return nr_entries; return nr_entries;
...@@ -1103,3 +1111,77 @@ void pci_msi_init_pci_dev(struct pci_dev *dev) ...@@ -1103,3 +1111,77 @@ void pci_msi_init_pci_dev(struct pci_dev *dev)
if (dev->msix_cap) if (dev->msix_cap)
msix_set_enable(dev, 0); msix_set_enable(dev, 0);
} }
/**
* pci_enable_msi_range - configure device's MSI capability structure
* @dev: device to configure
* @minvec: minimal number of interrupts to configure
* @maxvec: maximum number of interrupts to configure
*
* This function tries to allocate a maximum possible number of interrupts in a
* range between @minvec and @maxvec. It returns a negative errno if an error
* occurs. If it succeeds, it returns the actual number of interrupts allocated
* and updates the @dev's irq member to the lowest new interrupt number;
* the other interrupt numbers allocated to this device are consecutive.
**/
int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
{
int nvec = maxvec;
int rc;
if (maxvec < minvec)
return -ERANGE;
do {
rc = pci_enable_msi_block(dev, nvec);
if (rc < 0) {
return rc;
} else if (rc > 0) {
if (rc < minvec)
return -ENOSPC;
nvec = rc;
}
} while (rc);
return nvec;
}
EXPORT_SYMBOL(pci_enable_msi_range);
/**
* pci_enable_msix_range - configure device's MSI-X capability structure
* @dev: pointer to the pci_dev data structure of MSI-X device function
* @entries: pointer to an array of MSI-X entries
* @minvec: minimum number of MSI-X irqs requested
* @maxvec: maximum number of MSI-X irqs requested
*
* Setup the MSI-X capability structure of device function with a maximum
* possible number of interrupts in the range between @minvec and @maxvec
* upon its software driver call to request for MSI-X mode enabled on its
* hardware device function. It returns a negative errno if an error occurs.
* If it succeeds, it returns the actual number of interrupts allocated and
* indicates the successful configuration of MSI-X capability structure
* with new allocated MSI-X interrupts.
**/
int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
int minvec, int maxvec)
{
int nvec = maxvec;
int rc;
if (maxvec < minvec)
return -ERANGE;
do {
rc = pci_enable_msix(dev, entries, nvec);
if (rc < 0) {
return rc;
} else if (rc > 0) {
if (rc < minvec)
return -ENOSPC;
nvec = rc;
}
} while (rc);
return nvec;
}
EXPORT_SYMBOL(pci_enable_msix_range);
...@@ -79,9 +79,10 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) ...@@ -79,9 +79,10 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask)
u16 reg16; u16 reg16;
u32 reg32; u32 reg32;
nr_entries = pci_msix_table_size(dev); nr_entries = pci_msix_vec_count(dev);
if (!nr_entries) if (nr_entries < 0)
return -EINVAL; return nr_entries;
BUG_ON(!nr_entries);
if (nr_entries > PCIE_PORT_MAX_MSIX_ENTRIES) if (nr_entries > PCIE_PORT_MAX_MSIX_ENTRIES)
nr_entries = PCIE_PORT_MAX_MSIX_ENTRIES; nr_entries = PCIE_PORT_MAX_MSIX_ENTRIES;
......
...@@ -1166,13 +1166,12 @@ struct msix_entry { ...@@ -1166,13 +1166,12 @@ struct msix_entry {
#ifndef CONFIG_PCI_MSI #ifndef CONFIG_PCI_MSI
static inline int pci_enable_msi_block(struct pci_dev *dev, int nvec) static inline int pci_msi_vec_count(struct pci_dev *dev)
{ {
return -ENOSYS; return -ENOSYS;
} }
static inline int static inline int pci_enable_msi_block(struct pci_dev *dev, int nvec)
pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec)
{ {
return -ENOSYS; return -ENOSYS;
} }
...@@ -1182,9 +1181,9 @@ static inline void pci_msi_shutdown(struct pci_dev *dev) ...@@ -1182,9 +1181,9 @@ static inline void pci_msi_shutdown(struct pci_dev *dev)
static inline void pci_disable_msi(struct pci_dev *dev) static inline void pci_disable_msi(struct pci_dev *dev)
{ } { }
static inline int pci_msix_table_size(struct pci_dev *dev) static inline int pci_msix_vec_count(struct pci_dev *dev)
{ {
return 0; return -ENOSYS;
} }
static inline int pci_enable_msix(struct pci_dev *dev, static inline int pci_enable_msix(struct pci_dev *dev,
struct msix_entry *entries, int nvec) struct msix_entry *entries, int nvec)
...@@ -1206,18 +1205,32 @@ static inline int pci_msi_enabled(void) ...@@ -1206,18 +1205,32 @@ static inline int pci_msi_enabled(void)
{ {
return 0; return 0;
} }
static inline int pci_enable_msi_range(struct pci_dev *dev, int minvec,
int maxvec)
{
return -ENOSYS;
}
static inline int pci_enable_msix_range(struct pci_dev *dev,
struct msix_entry *entries, int minvec, int maxvec)
{
return -ENOSYS;
}
#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);
void pci_msi_shutdown(struct pci_dev *dev); void pci_msi_shutdown(struct pci_dev *dev);
void pci_disable_msi(struct pci_dev *dev); void pci_disable_msi(struct pci_dev *dev);
int pci_msix_table_size(struct pci_dev *dev); int pci_msix_vec_count(struct pci_dev *dev);
int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec); int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec);
void pci_msix_shutdown(struct pci_dev *dev); void pci_msix_shutdown(struct pci_dev *dev);
void pci_disable_msix(struct pci_dev *dev); void pci_disable_msix(struct pci_dev *dev);
void msi_remove_pci_irq_vectors(struct pci_dev *dev); void msi_remove_pci_irq_vectors(struct pci_dev *dev);
void pci_restore_msi_state(struct pci_dev *dev); void pci_restore_msi_state(struct pci_dev *dev);
int pci_msi_enabled(void); int pci_msi_enabled(void);
int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec);
int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
int minvec, int maxvec);
#endif #endif
#ifdef CONFIG_PCIEPORTBUS #ifdef CONFIG_PCIEPORTBUS
......
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