aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/Makefile69
-rw-r--r--drivers/pci/access.c380
-rw-r--r--drivers/pci/ats.c10
-rw-r--r--drivers/pci/bus.c2
-rw-r--r--drivers/pci/host-bridge.c2
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c23
-rw-r--r--drivers/pci/hotplug/cpqphp_ctrl.c12
-rw-r--r--drivers/pci/hotplug/pciehp.h3
-rw-r--r--drivers/pci/iov.c50
-rw-r--r--drivers/pci/mmap.c2
-rw-r--r--drivers/pci/msi.c3
-rw-r--r--drivers/pci/pci-acpi.c3
-rw-r--r--drivers/pci/pci-driver.c96
-rw-r--r--drivers/pci/pci-label.c5
-rw-r--r--drivers/pci/pci-stub.c3
-rw-r--r--drivers/pci/pci-sysfs.c111
-rw-r--r--drivers/pci/pci.c377
-rw-r--r--drivers/pci/pci.h45
-rw-r--r--drivers/pci/pcie/Makefile19
-rw-r--r--drivers/pci/pcie/aer/aer_inject.c4
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c10
-rw-r--r--drivers/pci/pcie/aer/aerdrv.h4
-rw-r--r--drivers/pci/pcie/aer/aerdrv_acpi.c1
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c11
-rw-r--r--drivers/pci/pcie/aer/aerdrv_errprint.c3
-rw-r--r--drivers/pci/pcie/aer/ecrc.c8
-rw-r--r--drivers/pci/pcie/aspm.c23
-rw-r--r--drivers/pci/pcie/dpc.c (renamed from drivers/pci/pcie/pcie-dpc.c)3
-rw-r--r--drivers/pci/pcie/pme.c1
-rw-r--r--drivers/pci/pcie/portdrv.h87
-rw-r--r--drivers/pci/pcie/portdrv_acpi.c5
-rw-r--r--drivers/pci/pcie/portdrv_bus.c56
-rw-r--r--drivers/pci/pcie/portdrv_core.c84
-rw-r--r--drivers/pci/pcie/portdrv_pci.c61
-rw-r--r--drivers/pci/probe.c75
-rw-r--r--drivers/pci/proc.c4
-rw-r--r--drivers/pci/quirks.c171
-rw-r--r--drivers/pci/rom.c4
-rw-r--r--drivers/pci/search.c8
-rw-r--r--drivers/pci/setup-bus.c6
-rw-r--r--drivers/pci/setup-irq.c4
-rw-r--r--drivers/pci/setup-res.c10
-rw-r--r--drivers/pci/slot.c2
-rw-r--r--drivers/pci/syscall.c9
-rw-r--r--drivers/pci/vpd.c576
-rw-r--r--drivers/pci/xen-pcifront.c4
46 files changed, 1333 insertions, 1116 deletions
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 941970936840..952addc7bacf 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -1,61 +1,40 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the PCI bus specific drivers.
-#
-obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o remove.o pci.o \
- pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \
- irq.o vpd.o setup-bus.o vc.o mmap.o setup-irq.o
+obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o \
+ remove.o pci.o pci-driver.o search.o \
+ pci-sysfs.o rom.o setup-res.o irq.o vpd.o \
+ setup-bus.o vc.o mmap.o setup-irq.o
ifdef CONFIG_PCI
-obj-$(CONFIG_PROC_FS) += proc.o
-obj-$(CONFIG_SYSFS) += slot.o
-obj-$(CONFIG_OF) += of.o
+obj-$(CONFIG_PROC_FS) += proc.o
+obj-$(CONFIG_SYSFS) += slot.o
+obj-$(CONFIG_OF) += of.o
endif
-obj-$(CONFIG_PCI_QUIRKS) += quirks.o
-
-# Build PCI Express stuff if needed
-obj-$(CONFIG_PCIEPORTBUS) += pcie/
-
-# Build the PCI Hotplug drivers if we were asked to
-obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
-
-# Build the PCI MSI interrupt support
-obj-$(CONFIG_PCI_MSI) += msi.o
-
-obj-$(CONFIG_PCI_ATS) += ats.o
-obj-$(CONFIG_PCI_IOV) += iov.o
-
-#
-# ACPI Related PCI FW Functions
-# ACPI _DSM provided firmware instance and string name
-#
-obj-$(CONFIG_ACPI) += pci-acpi.o
-
-# SMBIOS provided firmware instance and labels
-obj-$(CONFIG_PCI_LABEL) += pci-label.o
-
-# Intel MID platform PM support
-obj-$(CONFIG_X86_INTEL_MID) += pci-mid.o
-
-obj-$(CONFIG_PCI_SYSCALL) += syscall.o
-
-obj-$(CONFIG_PCI_STUB) += pci-stub.o
-
-obj-$(CONFIG_PCI_ECAM) += ecam.o
-
+obj-$(CONFIG_PCI_QUIRKS) += quirks.o
+obj-$(CONFIG_PCIEPORTBUS) += pcie/
+obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
+obj-$(CONFIG_PCI_MSI) += msi.o
+obj-$(CONFIG_PCI_ATS) += ats.o
+obj-$(CONFIG_PCI_IOV) += iov.o
+obj-$(CONFIG_ACPI) += pci-acpi.o
+obj-$(CONFIG_PCI_LABEL) += pci-label.o
+obj-$(CONFIG_X86_INTEL_MID) += pci-mid.o
+obj-$(CONFIG_PCI_SYSCALL) += syscall.o
+obj-$(CONFIG_PCI_STUB) += pci-stub.o
+obj-$(CONFIG_PCI_ECAM) += ecam.o
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
-ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
-
-# PCI host controller drivers
-obj-y += host/
-obj-y += switch/
+obj-y += host/
+obj-y += switch/
+# Endpoint library must be initialized before its users
obj-$(CONFIG_PCI_ENDPOINT) += endpoint/
-# Endpoint library must be initialized before its users
obj-$(CONFIG_PCIE_CADENCE) += cadence/
# pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
obj-y += dwc/
+
+ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index 5e9a9822d9d4..a3ad2fe185b9 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -1,8 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
-#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/module.h>
-#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/wait.h>
@@ -17,9 +15,9 @@
DEFINE_RAW_SPINLOCK(pci_lock);
/*
- * Wrappers for all PCI configuration access functions. They just check
- * alignment, do locking and call the low-level functions pointed to
- * by pci_dev->ops.
+ * Wrappers for all PCI configuration access functions. They just check
+ * alignment, do locking and call the low-level functions pointed to
+ * by pci_dev->ops.
*/
#define PCI_byte_BAD 0
@@ -264,372 +262,6 @@ PCI_USER_WRITE_CONFIG(byte, u8)
PCI_USER_WRITE_CONFIG(word, u16)
PCI_USER_WRITE_CONFIG(dword, u32)
-/* VPD access through PCI 2.2+ VPD capability */
-
-/**
- * pci_read_vpd - Read one entry from Vital Product Data
- * @dev: pci device struct
- * @pos: offset in vpd space
- * @count: number of bytes to read
- * @buf: pointer to where to store result
- */
-ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf)
-{
- if (!dev->vpd || !dev->vpd->ops)
- return -ENODEV;
- return dev->vpd->ops->read(dev, pos, count, buf);
-}
-EXPORT_SYMBOL(pci_read_vpd);
-
-/**
- * pci_write_vpd - Write entry to Vital Product Data
- * @dev: pci device struct
- * @pos: offset in vpd space
- * @count: number of bytes to write
- * @buf: buffer containing write data
- */
-ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf)
-{
- if (!dev->vpd || !dev->vpd->ops)
- return -ENODEV;
- return dev->vpd->ops->write(dev, pos, count, buf);
-}
-EXPORT_SYMBOL(pci_write_vpd);
-
-/**
- * pci_set_vpd_size - Set size of Vital Product Data space
- * @dev: pci device struct
- * @len: size of vpd space
- */
-int pci_set_vpd_size(struct pci_dev *dev, size_t len)
-{
- if (!dev->vpd || !dev->vpd->ops)
- return -ENODEV;
- return dev->vpd->ops->set_size(dev, len);
-}
-EXPORT_SYMBOL(pci_set_vpd_size);
-
-#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1)
-
-/**
- * pci_vpd_size - determine actual size of Vital Product Data
- * @dev: pci device struct
- * @old_size: current assumed size, also maximum allowed size
- */
-static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size)
-{
- size_t off = 0;
- unsigned char header[1+2]; /* 1 byte tag, 2 bytes length */
-
- while (off < old_size &&
- pci_read_vpd(dev, off, 1, header) == 1) {
- unsigned char tag;
-
- if (header[0] & PCI_VPD_LRDT) {
- /* Large Resource Data Type Tag */
- tag = pci_vpd_lrdt_tag(header);
- /* Only read length from known tag items */
- if ((tag == PCI_VPD_LTIN_ID_STRING) ||
- (tag == PCI_VPD_LTIN_RO_DATA) ||
- (tag == PCI_VPD_LTIN_RW_DATA)) {
- if (pci_read_vpd(dev, off+1, 2,
- &header[1]) != 2) {
- pci_warn(dev, "invalid large VPD tag %02x size at offset %zu",
- tag, off + 1);
- return 0;
- }
- off += PCI_VPD_LRDT_TAG_SIZE +
- pci_vpd_lrdt_size(header);
- }
- } else {
- /* Short Resource Data Type Tag */
- off += PCI_VPD_SRDT_TAG_SIZE +
- pci_vpd_srdt_size(header);
- tag = pci_vpd_srdt_tag(header);
- }
-
- if (tag == PCI_VPD_STIN_END) /* End tag descriptor */
- return off;
-
- if ((tag != PCI_VPD_LTIN_ID_STRING) &&
- (tag != PCI_VPD_LTIN_RO_DATA) &&
- (tag != PCI_VPD_LTIN_RW_DATA)) {
- pci_warn(dev, "invalid %s VPD tag %02x at offset %zu",
- (header[0] & PCI_VPD_LRDT) ? "large" : "short",
- tag, off);
- return 0;
- }
- }
- return 0;
-}
-
-/*
- * Wait for last operation to complete.
- * This code has to spin since there is no other notification from the PCI
- * hardware. Since the VPD is often implemented by serial attachment to an
- * EEPROM, it may take many milliseconds to complete.
- *
- * Returns 0 on success, negative values indicate error.
- */
-static int pci_vpd_wait(struct pci_dev *dev)
-{
- struct pci_vpd *vpd = dev->vpd;
- unsigned long timeout = jiffies + msecs_to_jiffies(125);
- unsigned long max_sleep = 16;
- u16 status;
- int ret;
-
- if (!vpd->busy)
- return 0;
-
- while (time_before(jiffies, timeout)) {
- ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR,
- &status);
- if (ret < 0)
- return ret;
-
- if ((status & PCI_VPD_ADDR_F) == vpd->flag) {
- vpd->busy = 0;
- return 0;
- }
-
- if (fatal_signal_pending(current))
- return -EINTR;
-
- usleep_range(10, max_sleep);
- if (max_sleep < 1024)
- max_sleep *= 2;
- }
-
- pci_warn(dev, "VPD access failed. This is likely a firmware bug on this device. Contact the card vendor for a firmware update\n");
- return -ETIMEDOUT;
-}
-
-static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count,
- void *arg)
-{
- struct pci_vpd *vpd = dev->vpd;
- int ret;
- loff_t end = pos + count;
- u8 *buf = arg;
-
- if (pos < 0)
- return -EINVAL;
-
- if (!vpd->valid) {
- vpd->valid = 1;
- vpd->len = pci_vpd_size(dev, vpd->len);
- }
-
- if (vpd->len == 0)
- return -EIO;
-
- if (pos > vpd->len)
- return 0;
-
- if (end > vpd->len) {
- end = vpd->len;
- count = end - pos;
- }
-
- if (mutex_lock_killable(&vpd->lock))
- return -EINTR;
-
- ret = pci_vpd_wait(dev);
- if (ret < 0)
- goto out;
-
- while (pos < end) {
- u32 val;
- unsigned int i, skip;
-
- ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
- pos & ~3);
- if (ret < 0)
- break;
- vpd->busy = 1;
- vpd->flag = PCI_VPD_ADDR_F;
- ret = pci_vpd_wait(dev);
- if (ret < 0)
- break;
-
- ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, &val);
- if (ret < 0)
- break;
-
- skip = pos & 3;
- for (i = 0; i < sizeof(u32); i++) {
- if (i >= skip) {
- *buf++ = val;
- if (++pos == end)
- break;
- }
- val >>= 8;
- }
- }
-out:
- mutex_unlock(&vpd->lock);
- return ret ? ret : count;
-}
-
-static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count,
- const void *arg)
-{
- struct pci_vpd *vpd = dev->vpd;
- const u8 *buf = arg;
- loff_t end = pos + count;
- int ret = 0;
-
- if (pos < 0 || (pos & 3) || (count & 3))
- return -EINVAL;
-
- if (!vpd->valid) {
- vpd->valid = 1;
- vpd->len = pci_vpd_size(dev, vpd->len);
- }
-
- if (vpd->len == 0)
- return -EIO;
-
- if (end > vpd->len)
- return -EINVAL;
-
- if (mutex_lock_killable(&vpd->lock))
- return -EINTR;
-
- ret = pci_vpd_wait(dev);
- if (ret < 0)
- goto out;
-
- while (pos < end) {
- u32 val;
-
- val = *buf++;
- val |= *buf++ << 8;
- val |= *buf++ << 16;
- val |= *buf++ << 24;
-
- ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val);
- if (ret < 0)
- break;
- ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
- pos | PCI_VPD_ADDR_F);
- if (ret < 0)
- break;
-
- vpd->busy = 1;
- vpd->flag = 0;
- ret = pci_vpd_wait(dev);
- if (ret < 0)
- break;
-
- pos += sizeof(u32);
- }
-out:
- mutex_unlock(&vpd->lock);
- return ret ? ret : count;
-}
-
-static int pci_vpd_set_size(struct pci_dev *dev, size_t len)
-{
- struct pci_vpd *vpd = dev->vpd;
-
- if (len == 0 || len > PCI_VPD_MAX_SIZE)
- return -EIO;
-
- vpd->valid = 1;
- vpd->len = len;
-
- return 0;
-}
-
-static const struct pci_vpd_ops pci_vpd_ops = {
- .read = pci_vpd_read,
- .write = pci_vpd_write,
- .set_size = pci_vpd_set_size,
-};
-
-static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count,
- void *arg)
-{
- struct pci_dev *tdev = pci_get_slot(dev->bus,
- PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
- ssize_t ret;
-
- if (!tdev)
- return -ENODEV;
-
- ret = pci_read_vpd(tdev, pos, count, arg);
- pci_dev_put(tdev);
- return ret;
-}
-
-static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count,
- const void *arg)
-{
- struct pci_dev *tdev = pci_get_slot(dev->bus,
- PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
- ssize_t ret;
-
- if (!tdev)
- return -ENODEV;
-
- ret = pci_write_vpd(tdev, pos, count, arg);
- pci_dev_put(tdev);
- return ret;
-}
-
-static int pci_vpd_f0_set_size(struct pci_dev *dev, size_t len)
-{
- struct pci_dev *tdev = pci_get_slot(dev->bus,
- PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
- int ret;
-
- if (!tdev)
- return -ENODEV;
-
- ret = pci_set_vpd_size(tdev, len);
- pci_dev_put(tdev);
- return ret;
-}
-
-static const struct pci_vpd_ops pci_vpd_f0_ops = {
- .read = pci_vpd_f0_read,
- .write = pci_vpd_f0_write,
- .set_size = pci_vpd_f0_set_size,
-};
-
-int pci_vpd_init(struct pci_dev *dev)
-{
- struct pci_vpd *vpd;
- u8 cap;
-
- cap = pci_find_capability(dev, PCI_CAP_ID_VPD);
- if (!cap)
- return -ENODEV;
-
- vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC);
- if (!vpd)
- return -ENOMEM;
-
- vpd->len = PCI_VPD_MAX_SIZE;
- if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0)
- vpd->ops = &pci_vpd_f0_ops;
- else
- vpd->ops = &pci_vpd_ops;
- mutex_init(&vpd->lock);
- vpd->cap = cap;
- vpd->busy = 0;
- vpd->valid = 0;
- dev->vpd = vpd;
- return 0;
-}
-
-void pci_vpd_release(struct pci_dev *dev)
-{
- kfree(dev->vpd);
-}
-
/**
* pci_cfg_access_lock - Lock PCI config reads/writes
* @dev: pci device struct
@@ -686,8 +318,10 @@ void pci_cfg_access_unlock(struct pci_dev *dev)
raw_spin_lock_irqsave(&pci_lock, flags);
- /* This indicates a problem in the caller, but we don't need
- * to kill them, unlike a double-block above. */
+ /*
+ * This indicates a problem in the caller, but we don't need
+ * to kill them, unlike a double-block above.
+ */
WARN_ON(!dev->block_cfg_access);
dev->block_cfg_access = 0;
diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c
index 6ad80a1fd5a7..89305b569d3d 100644
--- a/drivers/pci/ats.c
+++ b/drivers/pci/ats.c
@@ -1,14 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/ats.c
- *
- * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
- * Copyright (C) 2011 Advanced Micro Devices,
- *
- * PCI Express I/O Virtualization (IOV) support.
+ * PCI Express I/O Virtualization (IOV) support
* Address Translation Service 1.0
* Page Request Interface added by Joerg Roedel <joerg.roedel@amd.com>
* PASID support added by Joerg Roedel <joerg.roedel@amd.com>
+ *
+ * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
+ * Copyright (C) 2011 Advanced Micro Devices,
*/
#include <linux/export.h>
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 737d1c52f002..bc2ded4c451f 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -1,7 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/bus.c
- *
* From setup-res.c, by:
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c
index ac8d81268296..e01d53f5b32f 100644
--- a/drivers/pci/host-bridge.c
+++ b/drivers/pci/host-bridge.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * host bridge related code
+ * Host bridge related code
*/
#include <linux/kernel.h>
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index e2198a2feeca..b45b375c0e6c 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -541,6 +541,7 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot)
{
unsigned long long sta = 0;
struct acpiphp_func *func;
+ u32 dvid;
list_for_each_entry(func, &slot->funcs, sibling) {
if (func->flags & FUNC_HAS_STA) {
@@ -551,19 +552,27 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot)
if (ACPI_SUCCESS(status) && sta)
break;
} else {
- u32 dvid;
-
- pci_bus_read_config_dword(slot->bus,
- PCI_DEVFN(slot->device,
- func->function),
- PCI_VENDOR_ID, &dvid);
- if (dvid != 0xffffffff) {
+ if (pci_bus_read_dev_vendor_id(slot->bus,
+ PCI_DEVFN(slot->device, func->function),
+ &dvid, 0)) {
sta = ACPI_STA_ALL;
break;
}
}
}
+ if (!sta) {
+ /*
+ * Check for the slot itself since it may be that the
+ * ACPI slot is a device below PCIe upstream port so in
+ * that case it may not even be reachable yet.
+ */
+ if (pci_bus_read_dev_vendor_id(slot->bus,
+ PCI_DEVFN(slot->device, 0), &dvid, 0)) {
+ sta = ACPI_STA_ALL;
+ }
+ }
+
return (unsigned int)sta;
}
diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c
index b1b6e45253b2..616df442520b 100644
--- a/drivers/pci/hotplug/cpqphp_ctrl.c
+++ b/drivers/pci/hotplug/cpqphp_ctrl.c
@@ -2812,18 +2812,16 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
dbg("CND: length = 0x%x\n", base);
io_node = get_io_resource(&(resources->io_head), base);
+ if (!io_node)
+ return -ENOMEM;
dbg("Got io_node start = %8.8x, length = %8.8x next (%p)\n",
io_node->base, io_node->length, io_node->next);
dbg("func (%p) io_head (%p)\n", func, func->io_head);
/* allocate the resource to the board */
- if (io_node) {
- base = io_node->base;
-
- io_node->next = func->io_head;
- func->io_head = io_node;
- } else
- return -ENOMEM;
+ base = io_node->base;
+ io_node->next = func->io_head;
+ func->io_head = io_node;
} else if ((temp_register & 0x0BL) == 0x08) {
/* Map prefetchable memory */
base = temp_register & 0xFFFFFFF0;
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index 636ed8f4b869..88e917c9120f 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -20,10 +20,11 @@
#include <linux/pci_hotplug.h>
#include <linux/delay.h>
#include <linux/sched/signal.h> /* signal_pending() */
-#include <linux/pcieport_if.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
+#include "../pcie/portdrv.h"
+
#define MY_NAME "pciehp"
extern bool pciehp_poll_mode;
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 677924ae0350..8adf4a64f291 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -1,12 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/iov.c
- *
- * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
- *
- * PCI Express I/O Virtualization (IOV) support.
+ * PCI Express I/O Virtualization (IOV) support
* Single Root IOV 1.0
* Address Translation Service 1.0
+ *
+ * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
*/
#include <linux/pci.h>
@@ -114,6 +112,29 @@ resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno)
return dev->sriov->barsz[resno - PCI_IOV_RESOURCES];
}
+static void pci_read_vf_config_common(struct pci_dev *virtfn)
+{
+ struct pci_dev *physfn = virtfn->physfn;
+
+ /*
+ * Some config registers are the same across all associated VFs.
+ * Read them once from VF0 so we can skip reading them from the
+ * other VFs.
+ *
+ * PCIe r4.0, sec 9.3.4.1, technically doesn't require all VFs to
+ * have the same Revision ID and Subsystem ID, but we assume they
+ * do.
+ */
+ pci_read_config_dword(virtfn, PCI_CLASS_REVISION,
+ &physfn->sriov->class);
+ pci_read_config_byte(virtfn, PCI_HEADER_TYPE,
+ &physfn->sriov->hdr_type);
+ pci_read_config_word(virtfn, PCI_SUBSYSTEM_VENDOR_ID,
+ &physfn->sriov->subsystem_vendor);
+ pci_read_config_word(virtfn, PCI_SUBSYSTEM_ID,
+ &physfn->sriov->subsystem_device);
+}
+
int pci_iov_add_virtfn(struct pci_dev *dev, int id)
{
int i;
@@ -136,13 +157,17 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id)
virtfn->devfn = pci_iov_virtfn_devfn(dev, id);
virtfn->vendor = dev->vendor;
virtfn->device = iov->vf_device;
+ virtfn->is_virtfn = 1;
+ virtfn->physfn = pci_dev_get(dev);
+
+ if (id == 0)
+ pci_read_vf_config_common(virtfn);
+
rc = pci_setup_device(virtfn);
if (rc)
- goto failed0;
+ goto failed1;
virtfn->dev.parent = dev->dev.parent;
- virtfn->physfn = pci_dev_get(dev);
- virtfn->is_virtfn = 1;
virtfn->multifunction = 0;
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
@@ -163,10 +188,10 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id)
sprintf(buf, "virtfn%u", id);
rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
if (rc)
- goto failed1;
+ goto failed2;
rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn");
if (rc)
- goto failed2;
+ goto failed3;
kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE);
@@ -174,11 +199,12 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id)
return 0;
-failed2:
+failed3:
sysfs_remove_link(&dev->dev.kobj, buf);
+failed2:
+ pci_stop_and_remove_bus_device(virtfn);
failed1:
pci_dev_put(dev);
- pci_stop_and_remove_bus_device(virtfn);
failed0:
virtfn_remove_bus(dev->bus, bus);
failed:
diff --git a/drivers/pci/mmap.c b/drivers/pci/mmap.c
index 814a3ce341fc..24505b08de40 100644
--- a/drivers/pci/mmap.c
+++ b/drivers/pci/mmap.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * mmap.c — generic PCI resource mmap helper
+ * Generic PCI resource mmap helper
*
* Copyright © 2017 Amazon.com, Inc. or its affiliates.
*
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 8b0729c94bb7..30250631efe7 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * File: msi.c
- * Purpose: PCI Message Signaled Interrupt (MSI)
+ * PCI Message Signaled Interrupt (MSI)
*
* Copyright (C) 2003-2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 78157688dcc9..1abdbf267c19 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * File: pci-acpi.c
- * Purpose: Provide PCI support in ACPI
+ * PCI support in ACPI
*
* Copyright (C) 2005 David Shaohua Li <shaohua.li@intel.com>
* Copyright (C) 2004 Tom Long Nguyen <tom.l.nguyen@intel.com>
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 3bed6beda051..28cf87a7ff4c 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -1,7 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/pci-driver.c
- *
* (C) Copyright 2002-2004, 2007 Greg Kroah-Hartman <greg@kroah.com>
* (C) Copyright 2007 Novell Inc.
*/
@@ -19,6 +17,7 @@
#include <linux/suspend.h>
#include <linux/kexec.h>
#include "pci.h"
+#include "pcie/portdrv.h"
struct pci_dynid {
struct list_head node;
@@ -714,6 +713,18 @@ static void pci_pm_complete(struct device *dev)
#endif /* !CONFIG_PM_SLEEP */
#ifdef CONFIG_SUSPEND
+static void pcie_pme_root_status_cleanup(struct pci_dev *pci_dev)
+{
+ /*
+ * Some BIOSes forget to clear Root PME Status bits after system
+ * wakeup, which breaks ACPI-based runtime wakeup on PCI Express.
+ * Clear those bits now just in case (shouldn't hurt).
+ */
+ if (pci_is_pcie(pci_dev) &&
+ (pci_pcie_type(pci_dev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(pci_dev) == PCI_EXP_TYPE_RC_EC))
+ pcie_clear_root_pme_status(pci_dev);
+}
static int pci_pm_suspend(struct device *dev)
{
@@ -873,6 +884,8 @@ static int pci_pm_resume_noirq(struct device *dev)
if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume_early(dev);
+ pcie_pme_root_status_cleanup(pci_dev);
+
if (drv && drv->pm && drv->pm->resume_noirq)
error = drv->pm->resume_noirq(dev);
@@ -1517,6 +1530,42 @@ static int pci_uevent(struct device *dev, struct kobj_uevent_env *env)
return 0;
}
+#if defined(CONFIG_PCIEAER) || defined(CONFIG_EEH)
+/**
+ * pci_uevent_ers - emit a uevent during recovery path of PCI device
+ * @pdev: PCI device undergoing error recovery
+ * @err_type: type of error event
+ */
+void pci_uevent_ers(struct pci_dev *pdev, enum pci_ers_result err_type)
+{
+ int idx = 0;
+ char *envp[3];
+
+ switch (err_type) {
+ case PCI_ERS_RESULT_NONE:
+ case PCI_ERS_RESULT_CAN_RECOVER:
+ envp[idx++] = "ERROR_EVENT=BEGIN_RECOVERY";
+ envp[idx++] = "DEVICE_ONLINE=0";
+ break;
+ case PCI_ERS_RESULT_RECOVERED:
+ envp[idx++] = "ERROR_EVENT=SUCCESSFUL_RECOVERY";
+ envp[idx++] = "DEVICE_ONLINE=1";
+ break;
+ case PCI_ERS_RESULT_DISCONNECT:
+ envp[idx++] = "ERROR_EVENT=FAILED_RECOVERY";
+ envp[idx++] = "DEVICE_ONLINE=0";
+ break;
+ default:
+ break;
+ }
+
+ if (idx > 0) {
+ envp[idx++] = NULL;
+ kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp);
+ }
+}
+#endif
+
static int pci_bus_num_vf(struct device *dev)
{
return pci_num_vf(to_pci_dev(dev));
@@ -1538,8 +1587,49 @@ struct bus_type pci_bus_type = {
};
EXPORT_SYMBOL(pci_bus_type);
+#ifdef CONFIG_PCIEPORTBUS
+static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct pcie_device *pciedev;
+ struct pcie_port_service_driver *driver;
+
+ if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type)
+ return 0;
+
+ pciedev = to_pcie_device(dev);
+ driver = to_service_driver(drv);
+
+ if (driver->service != pciedev->service)
+ return 0;
+
+ if (driver->port_type != PCIE_ANY_PORT &&
+ driver->port_type != pci_pcie_type(pciedev->port))
+ return 0;
+
+ return 1;
+}
+
+struct bus_type pcie_port_bus_type = {
+ .name = "pci_express",
+ .match = pcie_port_bus_match,
+};
+EXPORT_SYMBOL_GPL(pcie_port_bus_type);
+#endif
+
static int __init pci_driver_init(void)
{
- return bus_register(&pci_bus_type);
+ int ret;
+
+ ret = bus_register(&pci_bus_type);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_PCIEPORTBUS
+ ret = bus_register(&pcie_port_bus_type);
+ if (ret)
+ return ret;
+#endif
+
+ return 0;
}
postcore_initcall(pci_driver_init);
diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c
index a961a71d950f..a5910f942857 100644
--- a/drivers/pci/pci-label.c
+++ b/drivers/pci/pci-label.c
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Purpose: Export the firmware instance and label associated with
- * a pci device to sysfs
+ * Export the firmware instance and label associated with a PCI device to
+ * sysfs
+ *
* Copyright (C) 2010 Dell Inc.
* by Narendra K <Narendra_K@dell.com>,
* Jordan Hargrave <Jordan_Hargrave@dell.com>
diff --git a/drivers/pci/pci-stub.c b/drivers/pci/pci-stub.c
index 10d54f939048..66f8a59fadbd 100644
--- a/drivers/pci/pci-stub.c
+++ b/drivers/pci/pci-stub.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
-/* pci-stub - simple stub driver to reserve a pci device
+/*
+ * Simple stub driver to reserve a PCI device
*
* Copyright (C) 2008 Red Hat, Inc.
* Author:
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index eb6bee8724cc..366d93af051d 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -1,7 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/pci-sysfs.c
- *
* (C) Copyright 2002-2004 Greg Kroah-Hartman <greg@kroah.com>
* (C) Copyright 2002-2004 IBM Corp.
* (C) Copyright 2003 Matthew Wilcox
@@ -12,7 +10,6 @@
* File attributes for PCI devices
*
* Modeled after usb's driverfs.c
- *
*/
@@ -158,45 +155,18 @@ static DEVICE_ATTR_RO(resource);
static ssize_t max_link_speed_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- u32 linkcap;
- int err;
- const char *speed;
-
- err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &linkcap);
- if (err)
- return -EINVAL;
-
- switch (linkcap & PCI_EXP_LNKCAP_SLS) {
- case PCI_EXP_LNKCAP_SLS_8_0GB:
- speed = "8 GT/s";
- break;
- case PCI_EXP_LNKCAP_SLS_5_0GB:
- speed = "5 GT/s";
- break;
- case PCI_EXP_LNKCAP_SLS_2_5GB:
- speed = "2.5 GT/s";
- break;
- default:
- speed = "Unknown speed";
- }
+ struct pci_dev *pdev = to_pci_dev(dev);
- return sprintf(buf, "%s\n", speed);
+ return sprintf(buf, "%s\n", PCIE_SPEED2STR(pcie_get_speed_cap(pdev)));
}
static DEVICE_ATTR_RO(max_link_speed);
static ssize_t max_link_width_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- u32 linkcap;
- int err;
-
- err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &linkcap);
- if (err)
- return -EINVAL;
+ struct pci_dev *pdev = to_pci_dev(dev);
- return sprintf(buf, "%u\n", (linkcap & PCI_EXP_LNKCAP_MLW) >> 4);
+ return sprintf(buf, "%u\n", pcie_get_width_cap(pdev));
}
static DEVICE_ATTR_RO(max_link_width);
@@ -213,6 +183,9 @@ static ssize_t current_link_speed_show(struct device *dev,
return -EINVAL;
switch (linkstat & PCI_EXP_LNKSTA_CLS) {
+ case PCI_EXP_LNKSTA_CLS_16_0GB:
+ speed = "16 GT/s";
+ break;
case PCI_EXP_LNKSTA_CLS_8_0GB:
speed = "8 GT/s";
break;
@@ -982,38 +955,6 @@ static ssize_t pci_write_config(struct file *filp, struct kobject *kobj,
return count;
}
-static ssize_t read_vpd_attr(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
- loff_t off, size_t count)
-{
- struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
-
- if (bin_attr->size > 0) {
- if (off > bin_attr->size)
- count = 0;
- else if (count > bin_attr->size - off)
- count = bin_attr->size - off;
- }
-
- return pci_read_vpd(dev, off, count, buf);
-}
-
-static ssize_t write_vpd_attr(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
- loff_t off, size_t count)
-{
- struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
-
- if (bin_attr->size > 0) {
- if (off > bin_attr->size)
- count = 0;
- else if (count > bin_attr->size - off)
- count = bin_attr->size - off;
- }
-
- return pci_write_vpd(dev, off, count, buf);
-}
-
#ifdef HAVE_PCI_LEGACY
/**
* pci_read_legacy_io - read byte(s) from legacy I/O port space
@@ -1517,46 +1458,20 @@ static struct device_attribute reset_attr = __ATTR(reset, 0200, NULL, reset_stor
static int pci_create_capabilities_sysfs(struct pci_dev *dev)
{
int retval;
- struct bin_attribute *attr;
- /* If the device has VPD, try to expose it in sysfs. */
- if (dev->vpd) {
- attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
- if (!attr)
- return -ENOMEM;
-
- sysfs_bin_attr_init(attr);
- attr->size = 0;
- attr->attr.name = "vpd";
- attr->attr.mode = S_IRUSR | S_IWUSR;
- attr->read = read_vpd_attr;
- attr->write = write_vpd_attr;
- retval = sysfs_create_bin_file(&dev->dev.kobj, attr);
- if (retval) {
- kfree(attr);
- return retval;
- }
- dev->vpd->attr = attr;
- }
-
- /* Active State Power Management */
+ pcie_vpd_create_sysfs_dev_files(dev);
pcie_aspm_create_sysfs_dev_files(dev);
- if (!pci_probe_reset_function(dev)) {
+ if (dev->reset_fn) {
retval = device_create_file(&dev->dev, &reset_attr);
if (retval)
goto error;
- dev->reset_fn = 1;
}
return 0;
error:
pcie_aspm_remove_sysfs_dev_files(dev);
- if (dev->vpd && dev->vpd->attr) {
- sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr);
- kfree(dev->vpd->attr);
- }
-
+ pcie_vpd_remove_sysfs_dev_files(dev);
return retval;
}
@@ -1630,11 +1545,7 @@ err:
static void pci_remove_capabilities_sysfs(struct pci_dev *dev)
{
- if (dev->vpd && dev->vpd->attr) {
- sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr);
- kfree(dev->vpd->attr);
- }
-
+ pcie_vpd_remove_sysfs_dev_files(dev);
pcie_aspm_remove_sysfs_dev_files(dev);
if (dev->reset_fn) {
device_remove_file(&dev->dev, &reset_attr);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index f6a4dd10d9b0..43aeecc4b675 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * PCI Bus Services, see include/linux/pci.h for further explanation.
+ * PCI Bus Services, see include/linux/pci.h for further explanation.
*
- * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
- * David Mosberger-Tang
+ * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
+ * David Mosberger-Tang
*
- * Copyright 1997 -- 2000 Martin Mares <mj@ucw.cz>
+ * Copyright 1997 -- 2000 Martin Mares <mj@ucw.cz>
*/
#include <linux/acpi.h>
@@ -22,6 +22,7 @@
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/log2.h>
+#include <linux/logic_pio.h>
#include <linux/pci-aspm.h>
#include <linux/pm_wakeup.h>
#include <linux/interrupt.h>
@@ -126,6 +127,9 @@ static int __init pcie_port_pm_setup(char *str)
}
__setup("pcie_port_pm=", pcie_port_pm_setup);
+/* Time to wait after a reset for device to become responsive */
+#define PCIE_RESET_READY_POLL_MS 60000
+
/**
* pci_bus_max_busnr - returns maximum PCI bus number of given bus' children
* @bus: pointer to PCI bus structure to search
@@ -1684,6 +1688,15 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state);
/**
+ * pcie_clear_root_pme_status - Clear root port PME interrupt status.
+ * @dev: PCIe root port or event collector.
+ */
+void pcie_clear_root_pme_status(struct pci_dev *dev)
+{
+ pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME);
+}
+
+/**
* pci_check_pme_status - Check if given device has generated PME.
* @dev: Device to check.
*
@@ -3440,68 +3453,35 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
}
EXPORT_SYMBOL(pci_request_regions_exclusive);
-#ifdef PCI_IOBASE
-struct io_range {
- struct list_head list;
- phys_addr_t start;
- resource_size_t size;
-};
-
-static LIST_HEAD(io_range_list);
-static DEFINE_SPINLOCK(io_range_lock);
-#endif
-
/*
* Record the PCI IO range (expressed as CPU physical address + size).
* Return a negative value if an error has occured, zero otherwise
*/
-int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
+int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
+ resource_size_t size)
{
- int err = 0;
-
+ int ret = 0;
#ifdef PCI_IOBASE
- struct io_range *range;
- resource_size_t allocated_size = 0;
-
- /* check if the range hasn't been previously recorded */
- spin_lock(&io_range_lock);
- list_for_each_entry(range, &io_range_list, list) {
- if (addr >= range->start && addr + size <= range->start + size) {
- /* range already registered, bail out */
- goto end_register;
- }
- allocated_size += range->size;
- }
-
- /* range not registed yet, check for available space */
- if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
- /* if it's too big check if 64K space can be reserved */
- if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
- err = -E2BIG;
- goto end_register;
- }
+ struct logic_pio_hwaddr *range;
- size = SZ_64K;
- pr_warn("Requested IO range too big, new size set to 64K\n");
- }
+ if (!size || addr + size < addr)
+ return -EINVAL;
- /* add the range to the list */
range = kzalloc(sizeof(*range), GFP_ATOMIC);
- if (!range) {
- err = -ENOMEM;
- goto end_register;
- }
+ if (!range)
+ return -ENOMEM;
- range->start = addr;
+ range->fwnode = fwnode;
range->size = size;
+ range->hw_start = addr;
+ range->flags = LOGIC_PIO_CPU_MMIO;
- list_add_tail(&range->list, &io_range_list);
-
-end_register:
- spin_unlock(&io_range_lock);
+ ret = logic_pio_register_range(range);
+ if (ret)
+ kfree(range);
#endif
- return err;
+ return ret;
}
phys_addr_t pci_pio_to_address(unsigned long pio)
@@ -3509,21 +3489,10 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
#ifdef PCI_IOBASE
- struct io_range *range;
- resource_size_t allocated_size = 0;
-
- if (pio > IO_SPACE_LIMIT)
+ if (pio >= MMIO_UPPER_LIMIT)
return address;
- spin_lock(&io_range_lock);
- list_for_each_entry(range, &io_range_list, list) {
- if (pio >= allocated_size && pio < allocated_size + range->size) {
- address = range->start + pio - allocated_size;
- break;
- }
- allocated_size += range->size;
- }
- spin_unlock(&io_range_lock);
+ address = logic_pio_to_hwaddr(pio);
#endif
return address;
@@ -3532,21 +3501,7 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
unsigned long __weak pci_address_to_pio(phys_addr_t address)
{
#ifdef PCI_IOBASE
- struct io_range *res;
- resource_size_t offset = 0;
- unsigned long addr = -1;
-
- spin_lock(&io_range_lock);
- list_for_each_entry(res, &io_range_list, list) {
- if (address >= res->start && address < res->start + res->size) {
- addr = address - res->start + offset;
- break;
- }
- offset += res->size;
- }
- spin_unlock(&io_range_lock);
-
- return addr;
+ return logic_pio_trans_cpuaddr(address);
#else
if (address > IO_SPACE_LIMIT)
return (unsigned long)-1;
@@ -4017,20 +3972,13 @@ int pci_wait_for_pending_transaction(struct pci_dev *dev)
}
EXPORT_SYMBOL(pci_wait_for_pending_transaction);
-static void pci_flr_wait(struct pci_dev *dev)
+static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
{
- int delay = 1, timeout = 60000;
+ int delay = 1;
u32 id;
/*
- * Per PCIe r3.1, sec 6.6.2, a device must complete an FLR within
- * 100ms, but may silently discard requests while the FLR is in
- * progress. Wait 100ms before trying to access the device.
- */
- msleep(100);
-
- /*
- * After 100ms, the device should not silently discard config
+ * After reset, the device should not silently discard config
* requests, but it may still indicate that it needs more time by
* responding to them with CRS completions. The Root Port will
* generally synthesize ~0 data to complete the read (except when
@@ -4044,14 +3992,14 @@ static void pci_flr_wait(struct pci_dev *dev)
pci_read_config_dword(dev, PCI_COMMAND, &id);
while (id == ~0) {
if (delay > timeout) {
- pci_warn(dev, "not ready %dms after FLR; giving up\n",
- 100 + delay - 1);
- return;
+ pci_warn(dev, "not ready %dms after %s; giving up\n",
+ delay - 1, reset_type);
+ return -ENOTTY;
}
if (delay > 1000)
- pci_info(dev, "not ready %dms after FLR; waiting\n",
- 100 + delay - 1);
+ pci_info(dev, "not ready %dms after %s; waiting\n",
+ delay - 1, reset_type);
msleep(delay);
delay *= 2;
@@ -4059,7 +4007,10 @@ static void pci_flr_wait(struct pci_dev *dev)
}
if (delay > 1000)
- pci_info(dev, "ready %dms after FLR\n", 100 + delay - 1);
+ pci_info(dev, "ready %dms after %s\n", delay - 1,
+ reset_type);
+
+ return 0;
}
/**
@@ -4088,13 +4039,21 @@ static bool pcie_has_flr(struct pci_dev *dev)
* device supports FLR before calling this function, e.g. by using the
* pcie_has_flr() helper.
*/
-void pcie_flr(struct pci_dev *dev)
+int pcie_flr(struct pci_dev *dev)
{
if (!pci_wait_for_pending_transaction(dev))
pci_err(dev, "timed out waiting for pending transaction; performing function level reset anyway\n");
pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
- pci_flr_wait(dev);
+
+ /*
+ * Per PCIe r4.0, sec 6.6.2, a device must complete an FLR within
+ * 100ms, but may silently discard requests while the FLR is in
+ * progress. Wait 100ms before trying to access the device.
+ */
+ msleep(100);
+
+ return pci_dev_wait(dev, "FLR", PCIE_RESET_READY_POLL_MS);
}
EXPORT_SYMBOL_GPL(pcie_flr);
@@ -4127,8 +4086,16 @@ static int pci_af_flr(struct pci_dev *dev, int probe)
pci_err(dev, "timed out waiting for pending transaction; performing AF function level reset anyway\n");
pci_write_config_byte(dev, pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR);
- pci_flr_wait(dev);
- return 0;
+
+ /*
+ * Per Advanced Capabilities for Conventional PCI ECN, 13 April 2006,
+ * updated 27 July 2006; a device must complete an FLR within
+ * 100ms, but may silently discard requests while the FLR is in
+ * progress. Wait 100ms before trying to access the device.
+ */
+ msleep(100);
+
+ return pci_dev_wait(dev, "AF_FLR", PCIE_RESET_READY_POLL_MS);
}
/**
@@ -4173,7 +4140,7 @@ static int pci_pm_reset(struct pci_dev *dev, int probe)
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr);
pci_dev_d3_sleep(dev);
- return 0;
+ return pci_dev_wait(dev, "PM D3->D0", PCIE_RESET_READY_POLL_MS);
}
void pci_reset_secondary_bus(struct pci_dev *dev)
@@ -4183,6 +4150,7 @@ void pci_reset_secondary_bus(struct pci_dev *dev)
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &ctrl);
ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
+
/*
* PCI spec v3.0 7.6.4.2 requires minimum Trst of 1ms. Double
* this to 2ms to ensure that we meet the minimum requirement.
@@ -4214,9 +4182,11 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev)
* Use the bridge control register to assert reset on the secondary bus.
* Devices on the secondary bus are left in power-on state.
*/
-void pci_reset_bridge_secondary_bus(struct pci_dev *dev)
+int pci_reset_bridge_secondary_bus(struct pci_dev *dev)
{
pcibios_reset_secondary_bus(dev);
+
+ return pci_dev_wait(dev, "bus reset", PCIE_RESET_READY_POLL_MS);
}
EXPORT_SYMBOL_GPL(pci_reset_bridge_secondary_bus);
@@ -4379,8 +4349,9 @@ int __pci_reset_function_locked(struct pci_dev *dev)
if (rc != -ENOTTY)
return rc;
if (pcie_has_flr(dev)) {
- pcie_flr(dev);
- return 0;
+ rc = pcie_flr(dev);
+ if (rc != -ENOTTY)
+ return rc;
}
rc = pci_af_flr(dev, 0);
if (rc != -ENOTTY)
@@ -4450,9 +4421,8 @@ int pci_reset_function(struct pci_dev *dev)
{
int rc;
- rc = pci_probe_reset_function(dev);
- if (rc)
- return rc;
+ if (!dev->reset_fn)
+ return -ENOTTY;
pci_dev_lock(dev);
pci_dev_save_and_disable(dev);
@@ -4487,9 +4457,8 @@ int pci_reset_function_locked(struct pci_dev *dev)
{
int rc;
- rc = pci_probe_reset_function(dev);
- if (rc)
- return rc;
+ if (!dev->reset_fn)
+ return -ENOTTY;
pci_dev_save_and_disable(dev);
@@ -4511,18 +4480,17 @@ int pci_try_reset_function(struct pci_dev *dev)
{
int rc;
- rc = pci_probe_reset_function(dev);
- if (rc)
- return rc;
+ if (!dev->reset_fn)
+ return -ENOTTY;
if (!pci_dev_trylock(dev))
return -EAGAIN;
pci_dev_save_and_disable(dev);
rc = __pci_reset_function_locked(dev);
+ pci_dev_restore(dev);
pci_dev_unlock(dev);
- pci_dev_restore(dev);
return rc;
}
EXPORT_SYMBOL_GPL(pci_try_reset_function);
@@ -4730,7 +4698,9 @@ static void pci_slot_restore(struct pci_slot *slot)
list_for_each_entry(dev, &slot->bus->devices, bus_list) {
if (!dev->slot || dev->slot != slot)
continue;
+ pci_dev_lock(dev);
pci_dev_restore(dev);
+ pci_dev_unlock(dev);
if (dev->subordinate)
pci_bus_restore(dev->subordinate);
}
@@ -5147,6 +5117,180 @@ int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed,
EXPORT_SYMBOL(pcie_get_minimum_link);
/**
+ * pcie_bandwidth_available - determine minimum link settings of a PCIe
+ * device and its bandwidth limitation
+ * @dev: PCI device to query
+ * @limiting_dev: storage for device causing the bandwidth limitation
+ * @speed: storage for speed of limiting device
+ * @width: storage for width of limiting device
+ *
+ * Walk up the PCI device chain and find the point where the minimum
+ * bandwidth is available. Return the bandwidth available there and (if
+ * limiting_dev, speed, and width pointers are supplied) information about
+ * that point. The bandwidth returned is in Mb/s, i.e., megabits/second of
+ * raw bandwidth.
+ */
+u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev,
+ enum pci_bus_speed *speed,
+ enum pcie_link_width *width)
+{
+ u16 lnksta;
+ enum pci_bus_speed next_speed;
+ enum pcie_link_width next_width;
+ u32 bw, next_bw;
+
+ if (speed)
+ *speed = PCI_SPEED_UNKNOWN;
+ if (width)
+ *width = PCIE_LNK_WIDTH_UNKNOWN;
+
+ bw = 0;
+
+ while (dev) {
+ pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
+
+ next_speed = pcie_link_speed[lnksta & PCI_EXP_LNKSTA_CLS];
+ next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >>
+ PCI_EXP_LNKSTA_NLW_SHIFT;
+
+ next_bw = next_width * PCIE_SPEED2MBS_ENC(next_speed);
+
+ /* Check if current device limits the total bandwidth */
+ if (!bw || next_bw <= bw) {
+ bw = next_bw;
+
+ if (limiting_dev)
+ *limiting_dev = dev;
+ if (speed)
+ *speed = next_speed;
+ if (width)
+ *width = next_width;
+ }
+
+ dev = pci_upstream_bridge(dev);
+ }
+
+ return bw;
+}
+EXPORT_SYMBOL(pcie_bandwidth_available);
+
+/**
+ * pcie_get_speed_cap - query for the PCI device's link speed capability
+ * @dev: PCI device to query
+ *
+ * Query the PCI device speed capability. Return the maximum link speed
+ * supported by the device.
+ */
+enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev)
+{
+ u32 lnkcap2, lnkcap;
+
+ /*
+ * PCIe r4.0 sec 7.5.3.18 recommends using the Supported Link
+ * Speeds Vector in Link Capabilities 2 when supported, falling
+ * back to Max Link Speed in Link Capabilities otherwise.
+ */
+ pcie_capability_read_dword(dev, PCI_EXP_LNKCAP2, &lnkcap2);
+ if (lnkcap2) { /* PCIe r3.0-compliant */
+ if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_16_0GB)
+ return PCIE_SPEED_16_0GT;
+ else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB)
+ return PCIE_SPEED_8_0GT;
+ else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB)
+ return PCIE_SPEED_5_0GT;
+ else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB)
+ return PCIE_SPEED_2_5GT;
+ return PCI_SPEED_UNKNOWN;
+ }
+
+ pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap);
+ if (lnkcap) {
+ if (lnkcap & PCI_EXP_LNKCAP_SLS_16_0GB)
+ return PCIE_SPEED_16_0GT;
+ else if (lnkcap & PCI_EXP_LNKCAP_SLS_8_0GB)
+ return PCIE_SPEED_8_0GT;
+ else if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB)
+ return PCIE_SPEED_5_0GT;
+ else if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB)
+ return PCIE_SPEED_2_5GT;
+ }
+
+ return PCI_SPEED_UNKNOWN;
+}
+
+/**
+ * pcie_get_width_cap - query for the PCI device's link width capability
+ * @dev: PCI device to query
+ *
+ * Query the PCI device width capability. Return the maximum link width
+ * supported by the device.
+ */
+enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev)
+{
+ u32 lnkcap;
+
+ pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap);
+ if (lnkcap)
+ return (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4;
+
+ return PCIE_LNK_WIDTH_UNKNOWN;
+}
+
+/**
+ * pcie_bandwidth_capable - calculate a PCI device's link bandwidth capability
+ * @dev: PCI device
+ * @speed: storage for link speed
+ * @width: storage for link width
+ *
+ * Calculate a PCI device's link bandwidth by querying for its link speed
+ * and width, multiplying them, and applying encoding overhead. The result
+ * is in Mb/s, i.e., megabits/second of raw bandwidth.
+ */
+u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed,
+ enum pcie_link_width *width)
+{
+ *speed = pcie_get_speed_cap(dev);
+ *width = pcie_get_width_cap(dev);
+
+ if (*speed == PCI_SPEED_UNKNOWN || *width == PCIE_LNK_WIDTH_UNKNOWN)
+ return 0;
+
+ return *width * PCIE_SPEED2MBS_ENC(*speed);
+}
+
+/**
+ * pcie_print_link_status - Report the PCI device's link speed and width
+ * @dev: PCI device to query
+ *
+ * Report the available bandwidth at the device. If this is less than the
+ * device is capable of, report the device's maximum possible bandwidth and
+ * the upstream link that limits its performance to less than that.
+ */
+void pcie_print_link_status(struct pci_dev *dev)
+{
+ enum pcie_link_width width, width_cap;
+ enum pci_bus_speed speed, speed_cap;
+ struct pci_dev *limiting_dev = NULL;
+ u32 bw_avail, bw_cap;
+
+ bw_cap = pcie_bandwidth_capable(dev, &speed_cap, &width_cap);
+ bw_avail = pcie_bandwidth_available(dev, &limiting_dev, &speed, &width);
+
+ if (bw_avail >= bw_cap)
+ pci_info(dev, "%u.%03u Gb/s available bandwidth (%s x%d link)\n",
+ bw_cap / 1000, bw_cap % 1000,
+ PCIE_SPEED2STR(speed_cap), width_cap);
+ else
+ pci_info(dev, "%u.%03u Gb/s available bandwidth, limited by %s x%d link at %s (capable of %u.%03u Gb/s with %s x%d link)\n",
+ bw_avail / 1000, bw_avail % 1000,
+ PCIE_SPEED2STR(speed), width,
+ limiting_dev ? pci_name(limiting_dev) : "<unknown>",
+ bw_cap / 1000, bw_cap % 1000,
+ PCIE_SPEED2STR(speed_cap), width_cap);
+}
+EXPORT_SYMBOL(pcie_print_link_status);
+
+/**
* pci_select_bars - Make BAR mask from the type of resource
* @dev: the PCI device for which BAR mask is made
* @flags: resource type mask to be selected
@@ -5611,8 +5755,9 @@ static int of_pci_bus_find_domain_nr(struct device *parent)
use_dt_domains = 0;
domain = pci_get_new_domain_nr();
} else {
- dev_err(parent, "Node %pOF has inconsistent \"linux,pci-domain\" property in DT\n",
- parent->of_node);
+ if (parent)
+ pr_err("Node %pOF has ", parent->of_node);
+ pr_err("Inconsistent \"linux,pci-domain\" property in DT\n");
domain = -1;
}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index fcd81911b127..023f7cf25bff 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -71,6 +71,7 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state);
void pci_power_up(struct pci_dev *dev);
void pci_disable_enabled_device(struct pci_dev *dev);
int pci_finish_runtime_suspend(struct pci_dev *dev);
+void pcie_clear_root_pme_status(struct pci_dev *dev);
int __pci_pme_wakeup(struct pci_dev *dev, void *ign);
void pci_pme_restore(struct pci_dev *dev);
bool pci_dev_keep_suspended(struct pci_dev *dev);
@@ -104,25 +105,10 @@ static inline bool pci_power_manageable(struct pci_dev *pci_dev)
return !pci_has_subordinate(pci_dev) || pci_dev->bridge_d3;
}
-struct pci_vpd_ops {
- ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
- ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
- int (*set_size)(struct pci_dev *dev, size_t len);
-};
-
-struct pci_vpd {
- const struct pci_vpd_ops *ops;
- struct bin_attribute *attr; /* Descriptor for sysfs VPD entry */
- struct mutex lock;
- unsigned int len;
- u16 flag;
- u8 cap;
- u8 busy:1;
- u8 valid:1;
-};
-
int pci_vpd_init(struct pci_dev *dev);
void pci_vpd_release(struct pci_dev *dev);
+void pcie_vpd_create_sysfs_dev_files(struct pci_dev *dev);
+void pcie_vpd_remove_sysfs_dev_files(struct pci_dev *dev);
/* PCI /proc functions */
#ifdef CONFIG_PROC_FS
@@ -253,6 +239,27 @@ bool pci_bus_clip_resource(struct pci_dev *dev, int idx);
void pci_reassigndev_resource_alignment(struct pci_dev *dev);
void pci_disable_bridge_window(struct pci_dev *dev);
+/* PCIe link information */
+#define PCIE_SPEED2STR(speed) \
+ ((speed) == PCIE_SPEED_16_0GT ? "16 GT/s" : \
+ (speed) == PCIE_SPEED_8_0GT ? "8 GT/s" : \
+ (speed) == PCIE_SPEED_5_0GT ? "5 GT/s" : \
+ (speed) == PCIE_SPEED_2_5GT ? "2.5 GT/s" : \
+ "Unknown speed")
+
+/* PCIe speed to Mb/s reduced by encoding overhead */
+#define PCIE_SPEED2MBS_ENC(speed) \
+ ((speed) == PCIE_SPEED_16_0GT ? 16000*128/130 : \
+ (speed) == PCIE_SPEED_8_0GT ? 8000*128/130 : \
+ (speed) == PCIE_SPEED_5_0GT ? 5000*8/10 : \
+ (speed) == PCIE_SPEED_2_5GT ? 2500*8/10 : \
+ 0)
+
+enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev);
+enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev);
+u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed,
+ enum pcie_link_width *width);
+
/* Single Root I/O Virtualization */
struct pci_sriov {
int pos; /* Capability position */
@@ -271,6 +278,10 @@ struct pci_sriov {
u16 driver_max_VFs; /* Max num VFs driver supports */
struct pci_dev *dev; /* Lowest numbered PF */
struct pci_dev *self; /* This PF */
+ u32 class; /* VF device */
+ u8 hdr_type; /* VF header type */
+ u16 subsystem_vendor; /* VF subsystem vendor */
+ u16 subsystem_device; /* VF subsystem device */
resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */
bool drivers_autoprobe; /* Auto probing of VFs by driver */
};
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index 223e4c34c29a..800e1d404a45 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -1,20 +1,13 @@
# SPDX-License-Identifier: GPL-2.0
#
-# Makefile for PCI-Express PORT Driver
-#
-
-# Build PCI Express ASPM if needed
-obj-$(CONFIG_PCIEASPM) += aspm.o
+# Makefile for PCI Express features and port driver
-pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o
-pcieportdrv-$(CONFIG_ACPI) += portdrv_acpi.o
+pcieportdrv-y := portdrv_core.o portdrv_pci.o
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
-# Build PCI Express AER if needed
+obj-$(CONFIG_PCIEASPM) += aspm.o
obj-$(CONFIG_PCIEAER) += aer/
-
-obj-$(CONFIG_PCIE_PME) += pme.o
-
-obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o
-obj-$(CONFIG_PCIE_PTM) += ptm.o
+obj-$(CONFIG_PCIE_PME) += pme.o
+obj-$(CONFIG_PCIE_DPC) += dpc.o
+obj-$(CONFIG_PCIE_PTM) += ptm.o
diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c
index 25e1feb962c5..a49090935303 100644
--- a/drivers/pci/pcie/aer/aer_inject.c
+++ b/drivers/pci/pcie/aer/aer_inject.c
@@ -344,7 +344,7 @@ static int aer_inject(struct aer_error_inj *einj)
goto out_put;
}
- pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ pos_cap_err = dev->aer_cap;
if (!pos_cap_err) {
pci_err(dev, "aer_inject: Device doesn't support AER\n");
ret = -EPROTONOSUPPORT;
@@ -355,7 +355,7 @@ static int aer_inject(struct aer_error_inj *einj)
pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK,
&uncor_mask);
- rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR);
+ rp_pos_cap_err = rpdev->aer_cap;
if (!rp_pos_cap_err) {
pci_err(rpdev, "aer_inject: Root port doesn't support AER\n");
ret = -EPROTONOSUPPORT;
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
index da8331f5684d..779b3879b1b5 100644
--- a/drivers/pci/pcie/aer/aerdrv.c
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -1,15 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/pcie/aer/aerdrv.c
- *
- * This file implements the AER root port service driver. The driver will
- * register an irq handler. When root port triggers an AER interrupt, the irq
- * handler will collect root port status and schedule a work.
+ * Implement the AER root port service driver. The driver registers an IRQ
+ * handler. When a root port triggers an AER interrupt, the IRQ handler
+ * collects root port status and schedules work.
*
* Copyright (C) 2006 Intel Corp.
* Tom Long Nguyen (tom.l.nguyen@intel.com)
* Zhang Yanmin (yanmin.zhang@intel.com)
- *
*/
#include <linux/pci.h>
@@ -21,7 +18,6 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
-#include <linux/pcieport_if.h>
#include <linux/slab.h>
#include "aerdrv.h"
diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h
index 5449e5ce139d..08b4584f62fe 100644
--- a/drivers/pci/pcie/aer/aerdrv.h
+++ b/drivers/pci/pcie/aer/aerdrv.h
@@ -3,17 +3,17 @@
* Copyright (C) 2006 Intel Corp.
* Tom Long Nguyen (tom.l.nguyen@intel.com)
* Zhang Yanmin (yanmin.zhang@intel.com)
- *
*/
#ifndef _AERDRV_H_
#define _AERDRV_H_
#include <linux/workqueue.h>
-#include <linux/pcieport_if.h>
#include <linux/aer.h>
#include <linux/interrupt.h>
+#include "../portdrv.h"
+
#define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \
PCI_EXP_RTCTL_SENFEE| \
PCI_EXP_RTCTL_SEFEE)
diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c
index b2019440e882..08c87de13cb8 100644
--- a/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ b/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -5,7 +5,6 @@
* Copyright (C) 2006 Intel Corp.
* Tom Long Nguyen (tom.l.nguyen@intel.com)
* Zhang Yanmin (yanmin.zhang@intel.com)
- *
*/
#include <linux/module.h>
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index a4bfea52e7d4..0ea5acc40323 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -1,16 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/pcie/aer/aerdrv_core.c
- *
- * This file implements the core part of PCIe AER. When a PCIe
- * error is delivered, an error message will be collected and printed to
- * console, then, an error recovery procedure will be executed by following
- * the PCI error recovery rules.
+ * Implement the core part of PCIe AER. When a PCIe error is delivered, an
+ * error message will be collected and printed to console, then an error
+ * recovery procedure will be executed by following the PCI error recovery
+ * rules.
*
* Copyright (C) 2006 Intel Corp.
* Tom Long Nguyen (tom.l.nguyen@intel.com)
* Zhang Yanmin (yanmin.zhang@intel.com)
- *
*/
#include <linux/module.h>
diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c
index 6a352e638699..cfc89dd57831 100644
--- a/drivers/pci/pcie/aer/aerdrv_errprint.c
+++ b/drivers/pci/pcie/aer/aerdrv_errprint.c
@@ -1,13 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/pcie/aer/aerdrv_errprint.c
- *
* Format error messages and print them to console.
*
* Copyright (C) 2006 Intel Corp.
* Tom Long Nguyen (tom.l.nguyen@intel.com)
* Zhang Yanmin (yanmin.zhang@intel.com)
- *
*/
#include <linux/module.h>
diff --git a/drivers/pci/pcie/aer/ecrc.c b/drivers/pci/pcie/aer/ecrc.c
index 26d3cac9e635..039efb606e31 100644
--- a/drivers/pci/pcie/aer/ecrc.c
+++ b/drivers/pci/pcie/aer/ecrc.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Enables/disables PCIe ECRC checking.
+ * Enable/disable PCIe ECRC checking
*
- * (C) Copyright 2009 Hewlett-Packard Development Company, L.P.
+ * (C) Copyright 2009 Hewlett-Packard Development Company, L.P.
* Andrew Patterson <andrew.patterson@hp.com>
*/
@@ -40,7 +40,7 @@ static int enable_ecrc_checking(struct pci_dev *dev)
if (!pci_is_pcie(dev))
return -ENODEV;
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ pos = dev->aer_cap;
if (!pos)
return -ENODEV;
@@ -68,7 +68,7 @@ static int disable_ecrc_checking(struct pci_dev *dev)
if (!pci_is_pcie(dev))
return -ENODEV;
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ pos = dev->aer_cap;
if (!pos)
return -ENODEV;
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 57feef2ecfe7..f76eb7704f64 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * File: drivers/pci/pcie/aspm.c
- * Enabling PCIe link L0s/L1 state and Clock Power Management
+ * Enable PCIe link L0s/L1 state and Clock Power Management
*
* Copyright (C) 2007 Intel
* Copyright (C) Zhang Yanmin (yanmin.zhang@intel.com)
@@ -228,6 +227,24 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
if (!(reg16 & PCI_EXP_LNKSTA_SLC))
same_clock = 0;
+ /* Port might be already in common clock mode */
+ pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &reg16);
+ if (same_clock && (reg16 & PCI_EXP_LNKCTL_CCC)) {
+ bool consistent = true;
+
+ list_for_each_entry(child, &linkbus->devices, bus_list) {
+ pcie_capability_read_word(child, PCI_EXP_LNKCTL,
+ &reg16);
+ if (!(reg16 & PCI_EXP_LNKCTL_CCC)) {
+ consistent = false;
+ break;
+ }
+ }
+ if (consistent)
+ return;
+ pci_warn(parent, "ASPM: current common clock configuration is broken, reconfiguring\n");
+ }
+
/* Configure downstream component, all functions */
list_for_each_entry(child, &linkbus->devices, bus_list) {
pcie_capability_read_word(child, PCI_EXP_LNKCTL, &reg16);
@@ -322,7 +339,7 @@ static u32 calc_l1ss_pwron(struct pci_dev *pdev, u32 scale, u32 val)
static void encode_l12_threshold(u32 threshold_us, u32 *scale, u32 *value)
{
- u64 threshold_ns = threshold_us * 1000;
+ u32 threshold_ns = threshold_us * 1000;
/* See PCIe r3.1, sec 7.33.3 and sec 6.18 */
if (threshold_ns < 32) {
diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/dpc.c
index 38e40c6c576f..8c57d607e603 100644
--- a/drivers/pci/pcie/pcie-dpc.c
+++ b/drivers/pci/pcie/dpc.c
@@ -10,7 +10,8 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
-#include <linux/pcieport_if.h>
+
+#include "portdrv.h"
#include "../pci.h"
#include "aer/aerdrv.h"
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
index 5480f54f7612..3ed67676ea2a 100644
--- a/drivers/pci/pcie/pme.c
+++ b/drivers/pci/pcie/pme.c
@@ -14,7 +14,6 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/device.h>
-#include <linux/pcieport_if.h>
#include <linux/pm_runtime.h>
#include "../pci.h"
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index a854bc569117..d0c6783dbfe3 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * File: portdrv.h
* Purpose: PCI Express Port Bus Driver's Internal Data Structures
*
* Copyright (C) 2004 Intel
@@ -12,7 +11,66 @@
#include <linux/compiler.h>
-#define PCIE_PORT_DEVICE_MAXSERVICES 5
+extern bool pcie_ports_native;
+
+/* Service Type */
+#define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */
+#define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT)
+#define PCIE_PORT_SERVICE_AER_SHIFT 1 /* Advanced Error Reporting */
+#define PCIE_PORT_SERVICE_AER (1 << PCIE_PORT_SERVICE_AER_SHIFT)
+#define PCIE_PORT_SERVICE_HP_SHIFT 2 /* Native Hotplug */
+#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT)
+#define PCIE_PORT_SERVICE_DPC_SHIFT 3 /* Downstream Port Containment */
+#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT)
+
+#define PCIE_PORT_DEVICE_MAXSERVICES 4
+
+/* Port Type */
+#define PCIE_ANY_PORT (~0)
+
+struct pcie_device {
+ int irq; /* Service IRQ/MSI/MSI-X Vector */
+ struct pci_dev *port; /* Root/Upstream/Downstream Port */
+ u32 service; /* Port service this device represents */
+ void *priv_data; /* Service Private Data */
+ struct device device; /* Generic Device Interface */
+};
+#define to_pcie_device(d) container_of(d, struct pcie_device, device)
+
+static inline void set_service_data(struct pcie_device *dev, void *data)
+{
+ dev->priv_data = data;
+}
+
+static inline void *get_service_data(struct pcie_device *dev)
+{
+ return dev->priv_data;
+}
+
+struct pcie_port_service_driver {
+ const char *name;
+ int (*probe) (struct pcie_device *dev);
+ void (*remove) (struct pcie_device *dev);
+ int (*suspend) (struct pcie_device *dev);
+ int (*resume) (struct pcie_device *dev);
+
+ /* Device driver may resume normal operations */
+ void (*error_resume)(struct pci_dev *dev);
+
+ /* Link Reset Capability - AER service driver specific */
+ pci_ers_result_t (*reset_link) (struct pci_dev *dev);
+
+ int port_type; /* Type of the port this driver can handle */
+ u32 service; /* Port service this device represents */
+
+ struct device_driver driver;
+};
+#define to_service_driver(d) \
+ container_of(d, struct pcie_port_service_driver, driver)
+
+int pcie_port_service_register(struct pcie_port_service_driver *new);
+void pcie_port_service_unregister(struct pcie_port_service_driver *new);
+
/*
* The PCIe Capability Interrupt Message Number (PCIe r3.1, sec 7.8.2) must
* be one of the first 32 MSI-X entries. Per PCI r3.0, sec 6.8.3.1, MSI
@@ -34,20 +92,6 @@ void pcie_port_bus_unregister(void);
struct pci_dev;
-void pcie_clear_root_pme_status(struct pci_dev *dev);
-
-#ifdef CONFIG_HOTPLUG_PCI_PCIE
-extern bool pciehp_msi_disabled;
-
-static inline bool pciehp_no_msi(void)
-{
- return pciehp_msi_disabled;
-}
-
-#else /* !CONFIG_HOTPLUG_PCI_PCIE */
-static inline bool pciehp_no_msi(void) { return false; }
-#endif /* !CONFIG_HOTPLUG_PCI_PCIE */
-
#ifdef CONFIG_PCIE_PME
extern bool pcie_pme_msi_disabled;
@@ -68,15 +112,4 @@ static inline bool pcie_pme_no_msi(void) { return false; }
static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {}
#endif /* !CONFIG_PCIE_PME */
-#ifdef CONFIG_ACPI
-void pcie_port_acpi_setup(struct pci_dev *port, int *mask);
-
-static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask)
-{
- pcie_port_acpi_setup(port, mask);
-}
-#else /* !CONFIG_ACPI */
-static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask){}
-#endif /* !CONFIG_ACPI */
-
#endif /* _PORTDRV_H_ */
diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c
index 319c94976873..8ab5d434b9c6 100644
--- a/drivers/pci/pcie/portdrv_acpi.c
+++ b/drivers/pci/pcie/portdrv_acpi.c
@@ -10,7 +10,6 @@
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/pci-acpi.h>
-#include <linux/pcieport_if.h>
#include "aer/aerdrv.h"
#include "../pci.h"
@@ -48,11 +47,11 @@ void pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
flags = root->osc_control_set;
- *srv_mask = PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_DPC;
+ *srv_mask = 0;
if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
*srv_mask |= PCIE_PORT_SERVICE_HP;
if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
*srv_mask |= PCIE_PORT_SERVICE_PME;
if (flags & OSC_PCI_EXPRESS_AER_CONTROL)
- *srv_mask |= PCIE_PORT_SERVICE_AER;
+ *srv_mask |= PCIE_PORT_SERVICE_AER | PCIE_PORT_SERVICE_DPC;
}
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c
deleted file mode 100644
index f0fba552a0e2..000000000000
--- a/drivers/pci/pcie/portdrv_bus.c
+++ /dev/null
@@ -1,56 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * File: portdrv_bus.c
- * Purpose: PCI Express Port Bus Driver's Bus Overloading Functions
- *
- * Copyright (C) 2004 Intel
- * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
- */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/pm.h>
-
-#include <linux/pcieport_if.h>
-#include "portdrv.h"
-
-static int pcie_port_bus_match(struct device *dev, struct device_driver *drv);
-
-struct bus_type pcie_port_bus_type = {
- .name = "pci_express",
- .match = pcie_port_bus_match,
-};
-EXPORT_SYMBOL_GPL(pcie_port_bus_type);
-
-static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
-{
- struct pcie_device *pciedev;
- struct pcie_port_service_driver *driver;
-
- if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type)
- return 0;
-
- pciedev = to_pcie_device(dev);
- driver = to_service_driver(drv);
-
- if (driver->service != pciedev->service)
- return 0;
-
- if ((driver->port_type != PCIE_ANY_PORT) &&
- (driver->port_type != pci_pcie_type(pciedev->port)))
- return 0;
-
- return 1;
-}
-
-int pcie_port_bus_register(void)
-{
- return bus_register(&pcie_port_bus_type);
-}
-
-void pcie_port_bus_unregister(void)
-{
- bus_unregister(&pcie_port_bus_type);
-}
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index ef3bad4ad010..c9c0663db282 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * File: portdrv_core.c
* Purpose: PCI Express Port Bus Driver's Core Functions
*
* Copyright (C) 2004 Intel
@@ -15,23 +14,11 @@
#include <linux/pm_runtime.h>
#include <linux/string.h>
#include <linux/slab.h>
-#include <linux/pcieport_if.h>
#include <linux/aer.h>
#include "../pci.h"
#include "portdrv.h"
-bool pciehp_msi_disabled;
-
-static int __init pciehp_setup(char *str)
-{
- if (!strncmp(str, "nomsi", 5))
- pciehp_msi_disabled = true;
-
- return 1;
-}
-__setup("pcie_hp=", pciehp_setup);
-
/**
* release_pcie_device - free PCI Express port service device structure
* @dev: Port service device to release
@@ -52,7 +39,7 @@ static void release_pcie_device(struct device *dev)
static int pcie_message_numbers(struct pci_dev *dev, int mask,
u32 *pme, u32 *aer, u32 *dpc)
{
- u32 nvec = 0, pos, reg32;
+ u32 nvec = 0, pos;
u16 reg16;
/*
@@ -68,8 +55,11 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
nvec = *pme + 1;
}
+#ifdef CONFIG_PCIEAER
if (mask & PCIE_PORT_SERVICE_AER) {
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ u32 reg32;
+
+ pos = dev->aer_cap;
if (pos) {
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS,
&reg32);
@@ -77,6 +67,7 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
nvec = max(nvec, *aer + 1);
}
}
+#endif
if (mask & PCIE_PORT_SERVICE_DPC) {
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC);
@@ -169,16 +160,13 @@ static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
irqs[i] = -1;
/*
- * If we support PME or hotplug, but we can't use MSI/MSI-X for
- * them, we have to fall back to INTx or other interrupts, e.g., a
- * system shared interrupt.
+ * If we support PME but can't use MSI/MSI-X for it, we have to
+ * fall back to INTx or other interrupts, e.g., a system shared
+ * interrupt.
*/
if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi())
goto legacy_irq;
- if ((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi())
- goto legacy_irq;
-
/* Try to use MSI-X or MSI if supported */
if (pcie_port_enable_irq_vec(dev, irqs, mask) == 0)
return 0;
@@ -189,10 +177,8 @@ legacy_irq:
if (ret < 0)
return -ENODEV;
- for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
- if (i != PCIE_PORT_SERVICE_VC_SHIFT)
- irqs[i] = pci_irq_vector(dev, 0);
- }
+ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
+ irqs[i] = pci_irq_vector(dev, 0);
return 0;
}
@@ -209,23 +195,13 @@ legacy_irq:
*/
static int get_port_device_capability(struct pci_dev *dev)
{
+ struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
int services = 0;
- int cap_mask = 0;
-
- if (pcie_ports_disabled)
- return 0;
-
- cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
- | PCIE_PORT_SERVICE_VC;
- if (pci_aer_available())
- cap_mask |= PCIE_PORT_SERVICE_AER | PCIE_PORT_SERVICE_DPC;
- if (pcie_ports_auto)
- pcie_port_platform_notify(dev, &cap_mask);
-
- /* Hot-Plug Capable */
- if ((cap_mask & PCIE_PORT_SERVICE_HP) && dev->is_hotplug_bridge) {
+ if (dev->is_hotplug_bridge &&
+ (pcie_ports_native || host->native_hotplug)) {
services |= PCIE_PORT_SERVICE_HP;
+
/*
* Disable hot-plug interrupts in case they have been enabled
* by the BIOS and the hot-plug service driver is not loaded.
@@ -233,23 +209,29 @@ static int get_port_device_capability(struct pci_dev *dev)
pcie_capability_clear_word(dev, PCI_EXP_SLTCTL,
PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
}
- /* AER capable */
- if ((cap_mask & PCIE_PORT_SERVICE_AER)
- && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) {
+
+#ifdef CONFIG_PCIEAER
+ if (dev->aer_cap && pci_aer_available() &&
+ (pcie_ports_native || host->native_aer)) {
services |= PCIE_PORT_SERVICE_AER;
+
/*
* Disable AER on this port in case it's been enabled by the
* BIOS (the AER service driver will enable it when necessary).
*/
pci_disable_pcie_error_reporting(dev);
}
- /* VC support */
- if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
- services |= PCIE_PORT_SERVICE_VC;
- /* Root ports are capable of generating PME too */
- if ((cap_mask & PCIE_PORT_SERVICE_PME)
- && pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
+#endif
+
+ /*
+ * Root ports are capable of generating PME too. Root Complex
+ * Event Collectors can also generate PMEs, but we don't handle
+ * those yet.
+ */
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT &&
+ (pcie_ports_native || host->native_pme)) {
services |= PCIE_PORT_SERVICE_PME;
+
/*
* Disable PME interrupt on this port in case it's been enabled
* by the BIOS (the PME service driver will enable it when
@@ -257,7 +239,9 @@ static int get_port_device_capability(struct pci_dev *dev)
*/
pcie_pme_interrupt_enable(dev, false);
}
- if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC))
+
+ if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) &&
+ pci_aer_available() && services & PCIE_PORT_SERVICE_AER)
services |= PCIE_PORT_SERVICE_DPC;
return services;
@@ -335,7 +319,7 @@ int pcie_port_device_register(struct pci_dev *dev)
*/
status = pcie_init_service_irqs(dev, irqs, capabilities);
if (status) {
- capabilities &= PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_HP;
+ capabilities &= PCIE_PORT_SERVICE_HP;
if (!capabilities)
goto error_disable;
}
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index fb1c1bb87316..973f1b80a038 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -1,9 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * File: portdrv_pci.c
* Purpose: PCI Express Port Bus Driver
* Author: Tom Nguyen <tom.l.nguyen@intel.com>
- * Version: v1.0
*
* Copyright (C) 2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
@@ -15,10 +13,8 @@
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/init.h>
-#include <linux/pcieport_if.h>
#include <linux/aer.h>
#include <linux/dmi.h>
-#include <linux/pci-aspm.h>
#include "../pci.h"
#include "portdrv.h"
@@ -27,22 +23,18 @@
bool pcie_ports_disabled;
/*
- * If this switch is set, ACPI _OSC will be used to determine whether or not to
- * enable PCIe port native services.
+ * If the user specified "pcie_ports=native", use the PCIe services regardless
+ * of whether the platform has given us permission. On ACPI systems, this
+ * means we ignore _OSC.
*/
-bool pcie_ports_auto = true;
+bool pcie_ports_native;
static int __init pcie_port_setup(char *str)
{
- if (!strncmp(str, "compat", 6)) {
+ if (!strncmp(str, "compat", 6))
pcie_ports_disabled = true;
- } else if (!strncmp(str, "native", 6)) {
- pcie_ports_disabled = false;
- pcie_ports_auto = false;
- } else if (!strncmp(str, "auto", 4)) {
- pcie_ports_disabled = false;
- pcie_ports_auto = true;
- }
+ else if (!strncmp(str, "native", 6))
+ pcie_ports_native = true;
return 1;
}
@@ -50,15 +42,6 @@ __setup("pcie_ports=", pcie_port_setup);
/* global data */
-/**
- * pcie_clear_root_pme_status - Clear root port PME interrupt status.
- * @dev: PCIe root port or event collector.
- */
-void pcie_clear_root_pme_status(struct pci_dev *dev)
-{
- pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME);
-}
-
static int pcie_portdrv_restore_config(struct pci_dev *dev)
{
int retval;
@@ -71,20 +54,6 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev)
}
#ifdef CONFIG_PM
-static int pcie_port_resume_noirq(struct device *dev)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
-
- /*
- * Some BIOSes forget to clear Root PME Status bits after system wakeup
- * which breaks ACPI-based runtime wakeup on PCI Express, so clear those
- * bits now just in case (shouldn't hurt).
- */
- if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)
- pcie_clear_root_pme_status(pdev);
- return 0;
-}
-
static int pcie_port_runtime_suspend(struct device *dev)
{
return to_pci_dev(dev)->bridge_d3 ? 0 : -EBUSY;
@@ -112,7 +81,6 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
.thaw = pcie_port_device_resume,
.poweroff = pcie_port_device_suspend,
.restore = pcie_port_device_resume,
- .resume_noirq = pcie_port_resume_noirq,
.runtime_suspend = pcie_port_runtime_suspend,
.runtime_resume = pcie_port_runtime_resume,
.runtime_idle = pcie_port_runtime_idle,
@@ -283,22 +251,11 @@ static const struct dmi_system_id pcie_portdrv_dmi_table[] __initconst = {
static int __init pcie_portdrv_init(void)
{
- int retval;
-
if (pcie_ports_disabled)
- return pci_register_driver(&pcie_portdriver);
+ return -EACCES;
dmi_check_system(pcie_portdrv_dmi_table);
- retval = pcie_port_bus_register();
- if (retval) {
- printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval);
- goto out;
- }
- retval = pci_register_driver(&pcie_portdriver);
- if (retval)
- pcie_port_bus_unregister();
- out:
- return retval;
+ return pci_register_driver(&pcie_portdriver);
}
device_initcall(pcie_portdrv_init);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index ef5377438a1e..caa07109e5f5 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * probe.c - PCI detection and setup code
+ * PCI detection and setup code
*/
#include <linux/kernel.h>
@@ -329,6 +329,10 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
if (dev->non_compliant_bars)
return;
+ /* Per PCIe r4.0, sec 9.3.4.1.11, the VF BARs are all RO Zero */
+ if (dev->is_virtfn)
+ return;
+
for (pos = 0; pos < howmany; pos++) {
struct resource *res = &dev->resource[pos];
reg = PCI_BASE_ADDRESS_0 + (pos << 2);
@@ -540,6 +544,16 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv)
INIT_LIST_HEAD(&bridge->windows);
bridge->dev.release = pci_release_host_bridge_dev;
+ /*
+ * We assume we can manage these PCIe features. Some systems may
+ * reserve these for use by the platform itself, e.g., an ACPI BIOS
+ * may implement its own AER handling and use _OSC to prevent the
+ * OS from interfering.
+ */
+ bridge->native_aer = 1;
+ bridge->native_hotplug = 1;
+ bridge->native_pme = 1;
+
return bridge;
}
EXPORT_SYMBOL(pci_alloc_host_bridge);
@@ -592,7 +606,7 @@ const unsigned char pcie_link_speed[] = {
PCIE_SPEED_2_5GT, /* 1 */
PCIE_SPEED_5_0GT, /* 2 */
PCIE_SPEED_8_0GT, /* 3 */
- PCI_SPEED_UNKNOWN, /* 4 */
+ PCIE_SPEED_16_0GT, /* 4 */
PCI_SPEED_UNKNOWN, /* 5 */
PCI_SPEED_UNKNOWN, /* 6 */
PCI_SPEED_UNKNOWN, /* 7 */
@@ -1230,6 +1244,13 @@ static void pci_read_irq(struct pci_dev *dev)
{
unsigned char irq;
+ /* VFs are not allowed to use INTx, so skip the config reads */
+ if (dev->is_virtfn) {
+ dev->pin = 0;
+ dev->irq = 0;
+ return;
+ }
+
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq);
dev->pin = irq;
if (irq)
@@ -1389,6 +1410,43 @@ int pci_cfg_space_size(struct pci_dev *dev)
return PCI_CFG_SPACE_SIZE;
}
+static u32 pci_class(struct pci_dev *dev)
+{
+ u32 class;
+
+#ifdef CONFIG_PCI_IOV
+ if (dev->is_virtfn)
+ return dev->physfn->sriov->class;
+#endif
+ pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
+ return class;
+}
+
+static void pci_subsystem_ids(struct pci_dev *dev, u16 *vendor, u16 *device)
+{
+#ifdef CONFIG_PCI_IOV
+ if (dev->is_virtfn) {
+ *vendor = dev->physfn->sriov->subsystem_vendor;
+ *device = dev->physfn->sriov->subsystem_device;
+ return;
+ }
+#endif
+ pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, vendor);
+ pci_read_config_word(dev, PCI_SUBSYSTEM_ID, device);
+}
+
+static u8 pci_hdr_type(struct pci_dev *dev)
+{
+ u8 hdr_type;
+
+#ifdef CONFIG_PCI_IOV
+ if (dev->is_virtfn)
+ return dev->physfn->sriov->hdr_type;
+#endif
+ pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type);
+ return hdr_type;
+}
+
#define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED)
static void pci_msi_setup_pci_dev(struct pci_dev *dev)
@@ -1454,8 +1512,7 @@ int pci_setup_device(struct pci_dev *dev)
struct pci_bus_region region;
struct resource *res;
- if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type))
- return -EIO;
+ hdr_type = pci_hdr_type(dev);
dev->sysdata = dev->bus->sysdata;
dev->dev.parent = dev->bus->bridge;
@@ -1477,7 +1534,8 @@ int pci_setup_device(struct pci_dev *dev)
dev->bus->number, PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn));
- pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
+ class = pci_class(dev);
+
dev->revision = class & 0xff;
dev->class = class >> 8; /* upper 3 bytes */
@@ -1517,8 +1575,8 @@ int pci_setup_device(struct pci_dev *dev)
goto bad;
pci_read_irq(dev);
pci_read_bases(dev, 6, PCI_ROM_ADDRESS);
- pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
- pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);
+
+ pci_subsystem_ids(dev, &dev->subsystem_vendor, &dev->subsystem_device);
/*
* Do the ugly legacy mode stuff here rather than broken chip
@@ -2121,6 +2179,9 @@ static void pci_init_capabilities(struct pci_dev *dev)
/* Advanced Error Reporting */
pci_aer_init(dev);
+
+ if (pci_probe_reset_function(dev) == 0)
+ dev->reset_fn = 1;
}
/*
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 58a662e3c4a6..1ee8927a0635 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Procfs interface for the PCI bus.
+ * Procfs interface for the PCI bus
*
- * Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz>
+ * Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz>
*/
#include <linux/init.h>
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index fc734014206f..76eaf4ad7652 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -1,15 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * This file contains work-arounds for many known PCI hardware
- * bugs. Devices present only on certain architectures (host
- * bridges et cetera) should be handled in arch-specific code.
+ * This file contains work-arounds for many known PCI hardware bugs.
+ * Devices present only on certain architectures (host bridges et cetera)
+ * should be handled in arch-specific code.
*
- * Note: any quirks for hotpluggable devices must _NOT_ be declared __init.
+ * Note: any quirks for hotpluggable devices must _NOT_ be declared __init.
*
- * Copyright (c) 1999 Martin Mares <mj@ucw.cz>
+ * Copyright (c) 1999 Martin Mares <mj@ucw.cz>
*
- * Init/reset quirks for USB host controllers should be in the
- * USB quirks file, where their drivers can access reuse it.
+ * Init/reset quirks for USB host controllers should be in the USB quirks
+ * file, where their drivers can use them.
*/
#include <linux/types.h>
@@ -1967,31 +1967,6 @@ static void quirk_netmos(struct pci_dev *dev)
DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID,
PCI_CLASS_COMMUNICATION_SERIAL, 8, quirk_netmos);
-/*
- * Quirk non-zero PCI functions to route VPD access through function 0 for
- * devices that share VPD resources between functions. The functions are
- * expected to be identical devices.
- */
-static void quirk_f0_vpd_link(struct pci_dev *dev)
-{
- struct pci_dev *f0;
-
- if (!PCI_FUNC(dev->devfn))
- return;
-
- f0 = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
- if (!f0)
- return;
-
- if (f0->vpd && dev->class == f0->class &&
- dev->vendor == f0->vendor && dev->device == f0->device)
- dev->dev_flags |= PCI_DEV_FLAGS_VPD_REF_F0;
-
- pci_dev_put(f0);
-}
-DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
- PCI_CLASS_NETWORK_ETHERNET, 8, quirk_f0_vpd_link);
-
static void quirk_e100_interrupt(struct pci_dev *dev)
{
u16 command, pmcsr;
@@ -2182,83 +2157,6 @@ static void quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching);
-/*
- * If a device follows the VPD format spec, the PCI core will not read or
- * write past the VPD End Tag. But some vendors do not follow the VPD
- * format spec, so we can't tell how much data is safe to access. Devices
- * may behave unpredictably if we access too much. Blacklist these devices
- * so we don't touch VPD at all.
- */
-static void quirk_blacklist_vpd(struct pci_dev *dev)
-{
- if (dev->vpd) {
- dev->vpd->len = 0;
- pci_warn(dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n");
- }
-}
-
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID,
- quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_QLOGIC, 0x2261, quirk_blacklist_vpd);
-
-/*
- * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the
- * VPD end tag will hang the device. This problem was initially
- * observed when a vpd entry was created in sysfs
- * ('/sys/bus/pci/devices/<id>/vpd'). A read to this sysfs entry
- * will dump 32k of data. Reading a full 32k will cause an access
- * beyond the VPD end tag causing the device to hang. Once the device
- * is hung, the bnx2 driver will not be able to reset the device.
- * We believe that it is legal to read beyond the end tag and
- * therefore the solution is to limit the read/write length.
- */
-static void quirk_brcm_570x_limit_vpd(struct pci_dev *dev)
-{
- /*
- * Only disable the VPD capability for 5706, 5706S, 5708,
- * 5708S and 5709 rev. A
- */
- if ((dev->device == PCI_DEVICE_ID_NX2_5706) ||
- (dev->device == PCI_DEVICE_ID_NX2_5706S) ||
- (dev->device == PCI_DEVICE_ID_NX2_5708) ||
- (dev->device == PCI_DEVICE_ID_NX2_5708S) ||
- ((dev->device == PCI_DEVICE_ID_NX2_5709) &&
- (dev->revision & 0xf0) == 0x0)) {
- if (dev->vpd)
- dev->vpd->len = 0x80;
- }
-}
-
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
- PCI_DEVICE_ID_NX2_5706,
- quirk_brcm_570x_limit_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
- PCI_DEVICE_ID_NX2_5706S,
- quirk_brcm_570x_limit_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
- PCI_DEVICE_ID_NX2_5708,
- quirk_brcm_570x_limit_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
- PCI_DEVICE_ID_NX2_5708S,
- quirk_brcm_570x_limit_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
- PCI_DEVICE_ID_NX2_5709,
- quirk_brcm_570x_limit_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
- PCI_DEVICE_ID_NX2_5709S,
- quirk_brcm_570x_limit_vpd);
-
static void quirk_brcm_5719_limit_mrrs(struct pci_dev *dev)
{
u32 rev;
@@ -3104,16 +3002,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb);
static ktime_t fixup_debug_start(struct pci_dev *dev,
void (*fn)(struct pci_dev *dev))
{
- ktime_t calltime = 0;
+ if (initcall_debug)
+ pci_info(dev, "calling %pF @ %i\n", fn, task_pid_nr(current));
- pci_dbg(dev, "calling %pF\n", fn);
- if (initcall_debug) {
- pr_debug("calling %pF @ %i for %s\n",
- fn, task_pid_nr(current), dev_name(&dev->dev));
- calltime = ktime_get();
- }
-
- return calltime;
+ return ktime_get();
}
static void fixup_debug_report(struct pci_dev *dev, ktime_t calltime,
@@ -3122,13 +3014,11 @@ static void fixup_debug_report(struct pci_dev *dev, ktime_t calltime,
ktime_t delta, rettime;
unsigned long long duration;
- if (initcall_debug) {
- rettime = ktime_get();
- delta = ktime_sub(rettime, calltime);
- duration = (unsigned long long) ktime_to_ns(delta) >> 10;
- pr_debug("pci fixup %pF returned after %lld usecs for %s\n",
- fn, duration, dev_name(&dev->dev));
- }
+ rettime = ktime_get();
+ delta = ktime_sub(rettime, calltime);
+ duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+ if (initcall_debug || duration > 10000)
+ pci_info(dev, "%pF took %lld usecs\n", fn, duration);
}
/*
@@ -3417,25 +3307,6 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PORT_RIDGE,
quirk_thunderbolt_hotplug_msi);
-static void quirk_chelsio_extend_vpd(struct pci_dev *dev)
-{
- pci_set_vpd_size(dev, 8192);
-}
-
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x20, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x21, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x22, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x23, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x24, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x25, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x26, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x30, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x31, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x32, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x35, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x36, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x37, quirk_chelsio_extend_vpd);
-
#ifdef CONFIG_ACPI
/*
* Apple: Shutdown Cactus Ridge Thunderbolt controller.
@@ -3896,6 +3767,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9182,
/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c46 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0,
quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c127 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9220,
+ quirk_dma_func1_alias);
/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c49 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9230,
quirk_dma_func1_alias);
@@ -4514,6 +4388,15 @@ static const struct pci_dev_acs_enabled {
{ PCI_VENDOR_ID_CAVIUM, PCI_ANY_ID, pci_quirk_cavium_acs },
/* APM X-Gene */
{ PCI_VENDOR_ID_AMCC, 0xE004, pci_quirk_xgene_acs },
+ /* Ampere Computing */
+ { PCI_VENDOR_ID_AMPERE, 0xE005, pci_quirk_xgene_acs },
+ { PCI_VENDOR_ID_AMPERE, 0xE006, pci_quirk_xgene_acs },
+ { PCI_VENDOR_ID_AMPERE, 0xE007, pci_quirk_xgene_acs },
+ { PCI_VENDOR_ID_AMPERE, 0xE008, pci_quirk_xgene_acs },
+ { PCI_VENDOR_ID_AMPERE, 0xE009, pci_quirk_xgene_acs },
+ { PCI_VENDOR_ID_AMPERE, 0xE00A, pci_quirk_xgene_acs },
+ { PCI_VENDOR_ID_AMPERE, 0xE00B, pci_quirk_xgene_acs },
+ { PCI_VENDOR_ID_AMPERE, 0xE00C, pci_quirk_xgene_acs },
{ 0 }
};
diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c
index 374a33443be9..a7b5c37a85ec 100644
--- a/drivers/pci/rom.c
+++ b/drivers/pci/rom.c
@@ -1,11 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/rom.c
+ * PCI ROM access routines
*
* (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
* (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
- *
- * PCI ROM access routines
*/
#include <linux/kernel.h>
#include <linux/export.h>
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index bc1e023f1353..2b5f720862d3 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * PCI searching functions.
+ * PCI searching functions
*
- * Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter,
+ * Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter,
* David Mosberger-Tang
- * Copyright (C) 1997 -- 2000 Martin Mares <mj@ucw.cz>
- * Copyright (C) 2003 -- 2004 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 1997 -- 2000 Martin Mares <mj@ucw.cz>
+ * Copyright (C) 2003 -- 2004 Greg Kroah-Hartman <greg@kroah.com>
*/
#include <linux/pci.h>
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 3cce29a069e6..072784f55ea5 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -1,16 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/setup-bus.c
+ * Support routines for initializing a PCI subsystem
*
* Extruded from code written by
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
* David Miller (davem@redhat.com)
*
- * Support routines for initializing a PCI subsystem.
- */
-
-/*
* Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
* PCI-PCI bridges cleanup, sorted resource allocation.
* Feb 2002, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c
index 5ad4ee7d7b1e..7129494754dd 100644
--- a/drivers/pci/setup-irq.c
+++ b/drivers/pci/setup-irq.c
@@ -1,13 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/setup-irq.c
+ * Support routines for initializing a PCI subsystem
*
* Extruded from code written by
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
* David Miller (davem@redhat.com)
- *
- * Support routines for initializing a PCI subsystem.
*/
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 369d48d6c6f1..1ef01d79b52e 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -1,18 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/setup-res.c
+ * Support routines for initializing a PCI subsystem
*
* Extruded from code written by
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
* David Miller (davem@redhat.com)
*
- * Support routines for initializing a PCI subsystem.
- */
-
-/* fixed for multiple pci buses, 1999 Andrea Arcangeli <andrea@suse.de> */
-
-/*
+ * Fixed for multiple PCI buses, 1999 Andrea Arcangeli <andrea@suse.de>
+ *
* Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
* Resource sorting
*/
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
index d10f556dc03e..e634229ece89 100644
--- a/drivers/pci/slot.c
+++ b/drivers/pci/slot.c
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/slot.c
* Copyright (C) 2006 Matthew Wilcox <matthew@wil.cx>
* Copyright (C) 2006-2009 Hewlett-Packard Development Company, L.P.
* Alex Chiang <achiang@hp.com>
@@ -76,6 +75,7 @@ static const char *pci_bus_speed_strings[] = {
"2.5 GT/s PCIe", /* 0x14 */
"5.0 GT/s PCIe", /* 0x15 */
"8.0 GT/s PCIe", /* 0x16 */
+ "16.0 GT/s PCIe", /* 0x17 */
};
static ssize_t bus_speed_read(enum pci_bus_speed speed, char *buf)
diff --git a/drivers/pci/syscall.c b/drivers/pci/syscall.c
index e725f99b5479..d96626c614f5 100644
--- a/drivers/pci/syscall.c
+++ b/drivers/pci/syscall.c
@@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * pci_syscall.c
- *
- * For architectures where we want to allow direct access
- * to the PCI config stuff - it would probably be preferable
- * on PCs too, but there people just do it by hand with the
- * magic northbridge registers..
+ * For architectures where we want to allow direct access to the PCI config
+ * stuff - it would probably be preferable on PCs too, but there people
+ * just do it by hand with the magic northbridge registers.
*/
#include <linux/errno.h>
diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c
index 70fba57d6103..00b8b5d37056 100644
--- a/drivers/pci/vpd.c
+++ b/drivers/pci/vpd.c
@@ -1,13 +1,465 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * File: vpd.c
- * Purpose: Provide PCI VPD support
+ * PCI VPD support
*
* Copyright (C) 2010 Broadcom Corporation.
*/
#include <linux/pci.h>
+#include <linux/delay.h>
#include <linux/export.h>
+#include <linux/sched/signal.h>
+#include "pci.h"
+
+/* VPD access through PCI 2.2+ VPD capability */
+
+struct pci_vpd_ops {
+ ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
+ ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
+ int (*set_size)(struct pci_dev *dev, size_t len);
+};
+
+struct pci_vpd {
+ const struct pci_vpd_ops *ops;
+ struct bin_attribute *attr; /* Descriptor for sysfs VPD entry */
+ struct mutex lock;
+ unsigned int len;
+ u16 flag;
+ u8 cap;
+ unsigned int busy:1;
+ unsigned int valid:1;
+};
+
+/**
+ * pci_read_vpd - Read one entry from Vital Product Data
+ * @dev: pci device struct
+ * @pos: offset in vpd space
+ * @count: number of bytes to read
+ * @buf: pointer to where to store result
+ */
+ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf)
+{
+ if (!dev->vpd || !dev->vpd->ops)
+ return -ENODEV;
+ return dev->vpd->ops->read(dev, pos, count, buf);
+}
+EXPORT_SYMBOL(pci_read_vpd);
+
+/**
+ * pci_write_vpd - Write entry to Vital Product Data
+ * @dev: pci device struct
+ * @pos: offset in vpd space
+ * @count: number of bytes to write
+ * @buf: buffer containing write data
+ */
+ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf)
+{
+ if (!dev->vpd || !dev->vpd->ops)
+ return -ENODEV;
+ return dev->vpd->ops->write(dev, pos, count, buf);
+}
+EXPORT_SYMBOL(pci_write_vpd);
+
+/**
+ * pci_set_vpd_size - Set size of Vital Product Data space
+ * @dev: pci device struct
+ * @len: size of vpd space
+ */
+int pci_set_vpd_size(struct pci_dev *dev, size_t len)
+{
+ if (!dev->vpd || !dev->vpd->ops)
+ return -ENODEV;
+ return dev->vpd->ops->set_size(dev, len);
+}
+EXPORT_SYMBOL(pci_set_vpd_size);
+
+#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1)
+
+/**
+ * pci_vpd_size - determine actual size of Vital Product Data
+ * @dev: pci device struct
+ * @old_size: current assumed size, also maximum allowed size
+ */
+static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size)
+{
+ size_t off = 0;
+ unsigned char header[1+2]; /* 1 byte tag, 2 bytes length */
+
+ while (off < old_size &&
+ pci_read_vpd(dev, off, 1, header) == 1) {
+ unsigned char tag;
+
+ if (header[0] & PCI_VPD_LRDT) {
+ /* Large Resource Data Type Tag */
+ tag = pci_vpd_lrdt_tag(header);
+ /* Only read length from known tag items */
+ if ((tag == PCI_VPD_LTIN_ID_STRING) ||
+ (tag == PCI_VPD_LTIN_RO_DATA) ||
+ (tag == PCI_VPD_LTIN_RW_DATA)) {
+ if (pci_read_vpd(dev, off+1, 2,
+ &header[1]) != 2) {
+ pci_warn(dev, "invalid large VPD tag %02x size at offset %zu",
+ tag, off + 1);
+ return 0;
+ }
+ off += PCI_VPD_LRDT_TAG_SIZE +
+ pci_vpd_lrdt_size(header);
+ }
+ } else {
+ /* Short Resource Data Type Tag */
+ off += PCI_VPD_SRDT_TAG_SIZE +
+ pci_vpd_srdt_size(header);
+ tag = pci_vpd_srdt_tag(header);
+ }
+
+ if (tag == PCI_VPD_STIN_END) /* End tag descriptor */
+ return off;
+
+ if ((tag != PCI_VPD_LTIN_ID_STRING) &&
+ (tag != PCI_VPD_LTIN_RO_DATA) &&
+ (tag != PCI_VPD_LTIN_RW_DATA)) {
+ pci_warn(dev, "invalid %s VPD tag %02x at offset %zu",
+ (header[0] & PCI_VPD_LRDT) ? "large" : "short",
+ tag, off);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Wait for last operation to complete.
+ * This code has to spin since there is no other notification from the PCI
+ * hardware. Since the VPD is often implemented by serial attachment to an
+ * EEPROM, it may take many milliseconds to complete.
+ *
+ * Returns 0 on success, negative values indicate error.
+ */
+static int pci_vpd_wait(struct pci_dev *dev)
+{
+ struct pci_vpd *vpd = dev->vpd;
+ unsigned long timeout = jiffies + msecs_to_jiffies(125);
+ unsigned long max_sleep = 16;
+ u16 status;
+ int ret;
+
+ if (!vpd->busy)
+ return 0;
+
+ while (time_before(jiffies, timeout)) {
+ ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR,
+ &status);
+ if (ret < 0)
+ return ret;
+
+ if ((status & PCI_VPD_ADDR_F) == vpd->flag) {
+ vpd->busy = 0;
+ return 0;
+ }
+
+ if (fatal_signal_pending(current))
+ return -EINTR;
+
+ usleep_range(10, max_sleep);
+ if (max_sleep < 1024)
+ max_sleep *= 2;
+ }
+
+ pci_warn(dev, "VPD access failed. This is likely a firmware bug on this device. Contact the card vendor for a firmware update\n");
+ return -ETIMEDOUT;
+}
+
+static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count,
+ void *arg)
+{
+ struct pci_vpd *vpd = dev->vpd;
+ int ret;
+ loff_t end = pos + count;
+ u8 *buf = arg;
+
+ if (pos < 0)
+ return -EINVAL;
+
+ if (!vpd->valid) {
+ vpd->valid = 1;
+ vpd->len = pci_vpd_size(dev, vpd->len);
+ }
+
+ if (vpd->len == 0)
+ return -EIO;
+
+ if (pos > vpd->len)
+ return 0;
+
+ if (end > vpd->len) {
+ end = vpd->len;
+ count = end - pos;
+ }
+
+ if (mutex_lock_killable(&vpd->lock))
+ return -EINTR;
+
+ ret = pci_vpd_wait(dev);
+ if (ret < 0)
+ goto out;
+
+ while (pos < end) {
+ u32 val;
+ unsigned int i, skip;
+
+ ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
+ pos & ~3);
+ if (ret < 0)
+ break;
+ vpd->busy = 1;
+ vpd->flag = PCI_VPD_ADDR_F;
+ ret = pci_vpd_wait(dev);
+ if (ret < 0)
+ break;
+
+ ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, &val);
+ if (ret < 0)
+ break;
+
+ skip = pos & 3;
+ for (i = 0; i < sizeof(u32); i++) {
+ if (i >= skip) {
+ *buf++ = val;
+ if (++pos == end)
+ break;
+ }
+ val >>= 8;
+ }
+ }
+out:
+ mutex_unlock(&vpd->lock);
+ return ret ? ret : count;
+}
+
+static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count,
+ const void *arg)
+{
+ struct pci_vpd *vpd = dev->vpd;
+ const u8 *buf = arg;
+ loff_t end = pos + count;
+ int ret = 0;
+
+ if (pos < 0 || (pos & 3) || (count & 3))
+ return -EINVAL;
+
+ if (!vpd->valid) {
+ vpd->valid = 1;
+ vpd->len = pci_vpd_size(dev, vpd->len);
+ }
+
+ if (vpd->len == 0)
+ return -EIO;
+
+ if (end > vpd->len)
+ return -EINVAL;
+
+ if (mutex_lock_killable(&vpd->lock))
+ return -EINTR;
+
+ ret = pci_vpd_wait(dev);
+ if (ret < 0)
+ goto out;
+
+ while (pos < end) {
+ u32 val;
+
+ val = *buf++;
+ val |= *buf++ << 8;
+ val |= *buf++ << 16;
+ val |= *buf++ << 24;
+
+ ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val);
+ if (ret < 0)
+ break;
+ ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
+ pos | PCI_VPD_ADDR_F);
+ if (ret < 0)
+ break;
+
+ vpd->busy = 1;
+ vpd->flag = 0;
+ ret = pci_vpd_wait(dev);
+ if (ret < 0)
+ break;
+
+ pos += sizeof(u32);
+ }
+out:
+ mutex_unlock(&vpd->lock);
+ return ret ? ret : count;
+}
+
+static int pci_vpd_set_size(struct pci_dev *dev, size_t len)
+{
+ struct pci_vpd *vpd = dev->vpd;
+
+ if (len == 0 || len > PCI_VPD_MAX_SIZE)
+ return -EIO;
+
+ vpd->valid = 1;
+ vpd->len = len;
+
+ return 0;
+}
+
+static const struct pci_vpd_ops pci_vpd_ops = {
+ .read = pci_vpd_read,
+ .write = pci_vpd_write,
+ .set_size = pci_vpd_set_size,
+};
+
+static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count,
+ void *arg)
+{
+ struct pci_dev *tdev = pci_get_slot(dev->bus,
+ PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
+ ssize_t ret;
+
+ if (!tdev)
+ return -ENODEV;
+
+ ret = pci_read_vpd(tdev, pos, count, arg);
+ pci_dev_put(tdev);
+ return ret;
+}
+
+static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count,
+ const void *arg)
+{
+ struct pci_dev *tdev = pci_get_slot(dev->bus,
+ PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
+ ssize_t ret;
+
+ if (!tdev)
+ return -ENODEV;
+
+ ret = pci_write_vpd(tdev, pos, count, arg);
+ pci_dev_put(tdev);
+ return ret;
+}
+
+static int pci_vpd_f0_set_size(struct pci_dev *dev, size_t len)
+{
+ struct pci_dev *tdev = pci_get_slot(dev->bus,
+ PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
+ int ret;
+
+ if (!tdev)
+ return -ENODEV;
+
+ ret = pci_set_vpd_size(tdev, len);
+ pci_dev_put(tdev);
+ return ret;
+}
+
+static const struct pci_vpd_ops pci_vpd_f0_ops = {
+ .read = pci_vpd_f0_read,
+ .write = pci_vpd_f0_write,
+ .set_size = pci_vpd_f0_set_size,
+};
+
+int pci_vpd_init(struct pci_dev *dev)
+{
+ struct pci_vpd *vpd;
+ u8 cap;
+
+ cap = pci_find_capability(dev, PCI_CAP_ID_VPD);
+ if (!cap)
+ return -ENODEV;
+
+ vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC);
+ if (!vpd)
+ return -ENOMEM;
+
+ vpd->len = PCI_VPD_MAX_SIZE;
+ if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0)
+ vpd->ops = &pci_vpd_f0_ops;
+ else
+ vpd->ops = &pci_vpd_ops;
+ mutex_init(&vpd->lock);
+ vpd->cap = cap;
+ vpd->busy = 0;
+ vpd->valid = 0;
+ dev->vpd = vpd;
+ return 0;
+}
+
+void pci_vpd_release(struct pci_dev *dev)
+{
+ kfree(dev->vpd);
+}
+
+static ssize_t read_vpd_attr(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
+
+ if (bin_attr->size > 0) {
+ if (off > bin_attr->size)
+ count = 0;
+ else if (count > bin_attr->size - off)
+ count = bin_attr->size - off;
+ }
+
+ return pci_read_vpd(dev, off, count, buf);
+}
+
+static ssize_t write_vpd_attr(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
+
+ if (bin_attr->size > 0) {
+ if (off > bin_attr->size)
+ count = 0;
+ else if (count > bin_attr->size - off)
+ count = bin_attr->size - off;
+ }
+
+ return pci_write_vpd(dev, off, count, buf);
+}
+
+void pcie_vpd_create_sysfs_dev_files(struct pci_dev *dev)
+{
+ int retval;
+ struct bin_attribute *attr;
+
+ if (!dev->vpd)
+ return;
+
+ attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
+ if (!attr)
+ return;
+
+ sysfs_bin_attr_init(attr);
+ attr->size = 0;
+ attr->attr.name = "vpd";
+ attr->attr.mode = S_IRUSR | S_IWUSR;
+ attr->read = read_vpd_attr;
+ attr->write = write_vpd_attr;
+ retval = sysfs_create_bin_file(&dev->dev.kobj, attr);
+ if (retval) {
+ kfree(attr);
+ return;
+ }
+
+ dev->vpd->attr = attr;
+}
+
+void pcie_vpd_remove_sysfs_dev_files(struct pci_dev *dev)
+{
+ if (dev->vpd && dev->vpd->attr) {
+ sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr);
+ kfree(dev->vpd->attr);
+ }
+}
int pci_vpd_find_tag(const u8 *buf, unsigned int off, unsigned int len, u8 rdt)
{
@@ -61,3 +513,123 @@ int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off,
return -ENOENT;
}
EXPORT_SYMBOL_GPL(pci_vpd_find_info_keyword);
+
+#ifdef CONFIG_PCI_QUIRKS
+/*
+ * Quirk non-zero PCI functions to route VPD access through function 0 for
+ * devices that share VPD resources between functions. The functions are
+ * expected to be identical devices.
+ */
+static void quirk_f0_vpd_link(struct pci_dev *dev)
+{
+ struct pci_dev *f0;
+
+ if (!PCI_FUNC(dev->devfn))
+ return;
+
+ f0 = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
+ if (!f0)
+ return;
+
+ if (f0->vpd && dev->class == f0->class &&
+ dev->vendor == f0->vendor && dev->device == f0->device)
+ dev->dev_flags |= PCI_DEV_FLAGS_VPD_REF_F0;
+
+ pci_dev_put(f0);
+}
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+ PCI_CLASS_NETWORK_ETHERNET, 8, quirk_f0_vpd_link);
+
+/*
+ * If a device follows the VPD format spec, the PCI core will not read or
+ * write past the VPD End Tag. But some vendors do not follow the VPD
+ * format spec, so we can't tell how much data is safe to access. Devices
+ * may behave unpredictably if we access too much. Blacklist these devices
+ * so we don't touch VPD at all.
+ */
+static void quirk_blacklist_vpd(struct pci_dev *dev)
+{
+ if (dev->vpd) {
+ dev->vpd->len = 0;
+ pci_warn(dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n");
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID,
+ quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_QLOGIC, 0x2261, quirk_blacklist_vpd);
+
+/*
+ * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the
+ * VPD end tag will hang the device. This problem was initially
+ * observed when a vpd entry was created in sysfs
+ * ('/sys/bus/pci/devices/<id>/vpd'). A read to this sysfs entry
+ * will dump 32k of data. Reading a full 32k will cause an access
+ * beyond the VPD end tag causing the device to hang. Once the device
+ * is hung, the bnx2 driver will not be able to reset the device.
+ * We believe that it is legal to read beyond the end tag and
+ * therefore the solution is to limit the read/write length.
+ */
+static void quirk_brcm_570x_limit_vpd(struct pci_dev *dev)
+{
+ /*
+ * Only disable the VPD capability for 5706, 5706S, 5708,
+ * 5708S and 5709 rev. A
+ */
+ if ((dev->device == PCI_DEVICE_ID_NX2_5706) ||
+ (dev->device == PCI_DEVICE_ID_NX2_5706S) ||
+ (dev->device == PCI_DEVICE_ID_NX2_5708) ||
+ (dev->device == PCI_DEVICE_ID_NX2_5708S) ||
+ ((dev->device == PCI_DEVICE_ID_NX2_5709) &&
+ (dev->revision & 0xf0) == 0x0)) {
+ if (dev->vpd)
+ dev->vpd->len = 0x80;
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5706,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5706S,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5708,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5708S,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5709,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5709S,
+ quirk_brcm_570x_limit_vpd);
+
+static void quirk_chelsio_extend_vpd(struct pci_dev *dev)
+{
+ pci_set_vpd_size(dev, 8192);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x20, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x21, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x22, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x23, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x24, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x25, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x26, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x30, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x31, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x32, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x35, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x36, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x37, quirk_chelsio_extend_vpd);
+#endif
diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
index 8785014f656e..eba6e33147a2 100644
--- a/drivers/pci/xen-pcifront.c
+++ b/drivers/pci/xen-pcifront.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Xen PCI Frontend.
+ * Xen PCI Frontend
*
- * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
*/
#include <linux/module.h>
#include <linux/init.h>