Skip to content

Commit

Permalink
Merge tag 'pci-v3.14-fixes-1' of git://git.kernel.org/pub/scm/linux/k…
Browse files Browse the repository at this point in the history
…ernel/git/helgaas/pci

Pull PCI updates from Bjorn Helgaas:
 "The most interesting thing here is the change to enable INTx (by
  clearing PCI_COMMAND_INTX_DISABLE) if the BIOS left INTx disabled.
  Apparently the Baytrail BIOS does this, which means EHCI doesn't work.

  Also, fix an AHCI MSI regression and other issues with the recent MSI
  changes.  This also adds pci_enable_msi_exact() and
  pci_enable_msix_exact(), which aren't regression fixes, but will keep
  us from touching drivers twice (once to stop using the deprecated
  pci_enable_msi(), etc., and again to use the *_exact() variants).

  There's also a minor MVEBU fix.

  Summary:

  MSI:
    - Fix AHCI single-MSI fallback (Alexander Gordeev)
    - Fix populate_msi_sysfs() error paths (Greg Kroah-Hartman)
    - Fix htmldocs problem (Masanari Iida)
    - Add pci_enable_msi_exact() and pci_enable_msix_exact() (Alexander Gordeev)
    - Update documentation (Alexander Gordeev)

  Miscellaneous:
    - mvebu: expose device ID & revision via lspci (Andrew Lunn)
    - Enable INTx if the BIOS left them disabled (Bjorn Helgaas)"

* tag 'pci-v3.14-fixes-1' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci:
  ahci: Fix broken fallback to single MSI mode
  PCI: Enable INTx if BIOS left them disabled
  PCI/MSI: Add pci_enable_msi_exact() and pci_enable_msix_exact()
  PCI/MSI: Fix cut-and-paste errors in documentation
  PCI/MSI: Add pci_enable_msi() documentation back
  PCI/MSI: Fix pci_msix_vec_count() htmldocs failure
  PCI/MSI: Fix leak of msi_attrs
  PCI/MSI: Check kmalloc() return value, fix leak of name
  PCI: mvebu: Use Device ID and revision from underlying endpoint
  • Loading branch information
Linus Torvalds committed Feb 20, 2014
2 parents 54dfffd + fc40363 commit d158fc7
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 22 deletions.
119 changes: 109 additions & 10 deletions Documentation/PCI/MSI-HOWTO.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,19 @@ Most of the hard work is done for the driver in the PCI layer. It simply
has to request that the PCI layer set up the MSI capability for this
device.

4.2.1 pci_enable_msi_range
4.2.1 pci_enable_msi

int pci_enable_msi(struct pci_dev *dev)

A successful call allocates ONE interrupt to the device, regardless
of how many MSIs the device supports. The device is switched from
pin-based interrupt mode to MSI mode. The dev->irq number is changed
to a new number which represents the message signaled interrupt;
consequently, this function should be called before the driver calls
request_irq(), because an MSI is delivered via a vector that is
different from the vector of a pin-based interrupt.

4.2.2 pci_enable_msi_range

int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)

Expand Down Expand Up @@ -147,6 +159,11 @@ static int foo_driver_enable_msi(struct pci_dev *pdev, int nvec)
return pci_enable_msi_range(pdev, nvec, nvec);
}

Note, unlike pci_enable_msi_exact() function, which could be also used to
enable a particular number of MSI-X interrupts, pci_enable_msi_range()
returns either a negative errno or 'nvec' (not negative errno or 0 - as
pci_enable_msi_exact() does).

4.2.1.3 Single MSI mode

The most notorious example of the request type described above is
Expand All @@ -158,7 +175,27 @@ static int foo_driver_enable_single_msi(struct pci_dev *pdev)
return pci_enable_msi_range(pdev, 1, 1);
}

4.2.2 pci_disable_msi
Note, unlike pci_enable_msi() function, which could be also used to
enable the single MSI mode, pci_enable_msi_range() returns either a
negative errno or 1 (not negative errno or 0 - as pci_enable_msi()
does).

4.2.3 pci_enable_msi_exact

int pci_enable_msi_exact(struct pci_dev *dev, int nvec)

This variation on pci_enable_msi_range() call allows a device driver to
request exactly 'nvec' MSIs.

If this function returns a negative number, it indicates an error and
the driver should not attempt to request any more MSI interrupts for
this device.

By contrast with pci_enable_msi_range() function, pci_enable_msi_exact()
returns zero in case of success, which indicates MSI interrupts have been
successfully allocated.

4.2.4 pci_disable_msi

void pci_disable_msi(struct pci_dev *dev)

Expand All @@ -172,7 +209,7 @@ on any interrupt for which it previously called request_irq().
Failure to do so results in a BUG_ON(), leaving the device with
MSI enabled and thus leaking its vector.

4.2.3 pci_msi_vec_count
4.2.4 pci_msi_vec_count

int pci_msi_vec_count(struct pci_dev *dev)

Expand Down Expand Up @@ -257,8 +294,8 @@ possible, likely up to the limit returned by pci_msix_vec_count() function:

static int foo_driver_enable_msix(struct foo_adapter *adapter, int nvec)
{
return pci_enable_msi_range(adapter->pdev, adapter->msix_entries,
1, nvec);
return pci_enable_msix_range(adapter->pdev, adapter->msix_entries,
1, nvec);
}

Note the value of 'minvec' parameter is 1. As 'minvec' is inclusive,
Expand All @@ -269,8 +306,8 @@ In this case the function could look like this:

static int foo_driver_enable_msix(struct foo_adapter *adapter, int nvec)
{
return pci_enable_msi_range(adapter->pdev, adapter->msix_entries,
FOO_DRIVER_MINIMUM_NVEC, nvec);
return pci_enable_msix_range(adapter->pdev, adapter->msix_entries,
FOO_DRIVER_MINIMUM_NVEC, nvec);
}

4.3.1.2 Exact number of MSI-X interrupts
Expand All @@ -282,10 +319,15 @@ parameters:

static int foo_driver_enable_msix(struct foo_adapter *adapter, int nvec)
{
return pci_enable_msi_range(adapter->pdev, adapter->msix_entries,
nvec, nvec);
return pci_enable_msix_range(adapter->pdev, adapter->msix_entries,
nvec, nvec);
}

Note, unlike pci_enable_msix_exact() function, which could be also used to
enable a particular number of MSI-X interrupts, pci_enable_msix_range()
returns either a negative errno or 'nvec' (not negative errno or 0 - as
pci_enable_msix_exact() does).

4.3.1.3 Specific requirements to the number of MSI-X interrupts

As noted above, there could be devices that can not operate with just any
Expand Down Expand Up @@ -332,7 +374,64 @@ Note how pci_enable_msix_range() return value is analized for a fallback -
any error code other than -ENOSPC indicates a fatal error and should not
be retried.

4.3.2 pci_disable_msix
4.3.2 pci_enable_msix_exact

int pci_enable_msix_exact(struct pci_dev *dev,
struct msix_entry *entries, int nvec)

This variation on pci_enable_msix_range() call allows a device driver to
request exactly 'nvec' MSI-Xs.

If this function returns a negative number, it indicates an error and
the driver should not attempt to allocate any more MSI-X interrupts for
this device.

By contrast with pci_enable_msix_range() function, pci_enable_msix_exact()
returns zero in case of success, which indicates MSI-X interrupts have been
successfully allocated.

Another version of a routine that enables MSI-X mode for a device with
specific requirements described in chapter 4.3.1.3 might look like this:

/*
* Assume 'minvec' and 'maxvec' are non-zero
*/
static int foo_driver_enable_msix(struct foo_adapter *adapter,
int minvec, int maxvec)
{
int rc;

minvec = roundup_pow_of_two(minvec);
maxvec = rounddown_pow_of_two(maxvec);

if (minvec > maxvec)
return -ERANGE;

retry:
rc = pci_enable_msix_exact(adapter->pdev,
adapter->msix_entries, maxvec);

/*
* -ENOSPC is the only error code allowed to be analyzed
*/
if (rc == -ENOSPC) {
if (maxvec == 1)
return -ENOSPC;

maxvec /= 2;

if (minvec > maxvec)
return -ENOSPC;

goto retry;
} else if (rc < 0) {
return rc;
}

return maxvec;
}

4.3.3 pci_disable_msix

void pci_disable_msix(struct pci_dev *dev)

Expand Down
4 changes: 3 additions & 1 deletion drivers/ata/ahci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1184,8 +1184,10 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,

nvec = rc;
rc = pci_enable_msi_block(pdev, nvec);
if (rc)
if (rc < 0)
goto intx;
else if (rc > 0)
goto single_msi;

return nvec;

Expand Down
11 changes: 2 additions & 9 deletions drivers/pci/host/pci-mvebu.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,6 @@
#define PCIE_DEBUG_CTRL 0x1a60
#define PCIE_DEBUG_SOFT_RESET BIT(20)

/*
* This product ID is registered by Marvell, and used when the Marvell
* SoC is not the root complex, but an endpoint on the PCIe bus. It is
* therefore safe to re-use this PCI ID for our emulated PCI-to-PCI
* bridge.
*/
#define MARVELL_EMULATED_PCI_PCI_BRIDGE_ID 0x7846

/* PCI configuration space of a PCI-to-PCI bridge */
struct mvebu_sw_pci_bridge {
u16 vendor;
Expand Down Expand Up @@ -388,7 +380,8 @@ static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port)

bridge->class = PCI_CLASS_BRIDGE_PCI;
bridge->vendor = PCI_VENDOR_ID_MARVELL;
bridge->device = MARVELL_EMULATED_PCI_PCI_BRIDGE_ID;
bridge->device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16;
bridge->revision = mvebu_readl(port, PCIE_DEV_REV_OFF) & 0xff;
bridge->header_type = PCI_HEADER_TYPE_BRIDGE;
bridge->cache_line_size = 0x10;

Expand Down
10 changes: 8 additions & 2 deletions drivers/pci/msi.c
Original file line number Diff line number Diff line change
Expand Up @@ -545,9 +545,15 @@ static int populate_msi_sysfs(struct pci_dev *pdev)
return -ENOMEM;
list_for_each_entry(entry, &pdev->msi_list, list) {
char *name = kmalloc(20, GFP_KERNEL);
if (!name)
goto error_attrs;

msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL);
if (!msi_dev_attr)
if (!msi_dev_attr) {
kfree(name);
goto error_attrs;
}

sprintf(name, "%d", entry->irq);
sysfs_attr_init(&msi_dev_attr->attr);
msi_dev_attr->attr.name = name;
Expand Down Expand Up @@ -589,6 +595,7 @@ static int populate_msi_sysfs(struct pci_dev *pdev)
++count;
msi_attr = msi_attrs[count];
}
kfree(msi_attrs);
return ret;
}

Expand Down Expand Up @@ -959,7 +966,6 @@ EXPORT_SYMBOL(pci_disable_msi);
/**
* 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
* 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
Expand Down
10 changes: 10 additions & 0 deletions drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,8 @@ EXPORT_SYMBOL_GPL(pci_load_and_free_saved_state);
static int do_pci_enable_device(struct pci_dev *dev, int bars)
{
int err;
u16 cmd;
u8 pin;

err = pci_set_power_state(dev, PCI_D0);
if (err < 0 && err != -EIO)
Expand All @@ -1190,6 +1192,14 @@ static int do_pci_enable_device(struct pci_dev *dev, int bars)
return err;
pci_fixup_device(pci_fixup_enable, dev);

pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
if (pin) {
pci_read_config_word(dev, PCI_COMMAND, &cmd);
if (cmd & PCI_COMMAND_INTX_DISABLE)
pci_write_config_word(dev, PCI_COMMAND,
cmd & ~PCI_COMMAND_INTX_DISABLE);
}

return 0;
}

Expand Down
20 changes: 20 additions & 0 deletions include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -1169,8 +1169,23 @@ void msi_remove_pci_irq_vectors(struct pci_dev *dev);
void pci_restore_msi_state(struct pci_dev *dev);
int pci_msi_enabled(void);
int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec);
static inline int pci_enable_msi_exact(struct pci_dev *dev, int nvec)
{
int rc = pci_enable_msi_range(dev, nvec, nvec);
if (rc < 0)
return rc;
return 0;
}
int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
int minvec, int maxvec);
static inline int pci_enable_msix_exact(struct pci_dev *dev,
struct msix_entry *entries, int nvec)
{
int rc = pci_enable_msix_range(dev, entries, nvec, nvec);
if (rc < 0)
return rc;
return 0;
}
#else
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)
Expand All @@ -1189,9 +1204,14 @@ static inline int pci_msi_enabled(void) { return 0; }
static inline int pci_enable_msi_range(struct pci_dev *dev, int minvec,
int maxvec)
{ return -ENOSYS; }
static inline int pci_enable_msi_exact(struct pci_dev *dev, int nvec)
{ return -ENOSYS; }
static inline int pci_enable_msix_range(struct pci_dev *dev,
struct msix_entry *entries, int minvec, int maxvec)
{ return -ENOSYS; }
static inline int pci_enable_msix_exact(struct pci_dev *dev,
struct msix_entry *entries, int nvec)
{ return -ENOSYS; }
#endif

#ifdef CONFIG_PCIEPORTBUS
Expand Down

0 comments on commit d158fc7

Please sign in to comment.