aboutsummaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/Kconfig2
-rw-r--r--drivers/pci/access.c9
-rw-r--r--drivers/pci/bus.c2
-rw-r--r--drivers/pci/controller/pci-host-common.c3
-rw-r--r--drivers/pci/hotplug/ibmphp_res.c1
-rw-r--r--drivers/pci/hotplug/pciehp.h11
-rw-r--r--drivers/pci/hotplug/pciehp_core.c9
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c39
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c87
-rw-r--r--drivers/pci/iov.c171
-rw-r--r--drivers/pci/p2pdma.c374
-rw-r--r--drivers/pci/pci-acpi.c410
-rw-r--r--drivers/pci/pci-bridge-emul.c4
-rw-r--r--drivers/pci/pci-sysfs.c223
-rw-r--r--drivers/pci/pci.c87
-rw-r--r--drivers/pci/pci.h68
-rw-r--r--drivers/pci/pcie/aspm.c9
-rw-r--r--drivers/pci/pcie/err.c2
-rw-r--r--drivers/pci/probe.c326
-rw-r--r--drivers/pci/quirks.c15
-rw-r--r--drivers/pci/search.c1
-rw-r--r--drivers/pci/setup-bus.c4
-rw-r--r--drivers/pci/vc.c4
23 files changed, 1076 insertions, 785 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 2ab92409210a..beb3408a0272 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -52,7 +52,7 @@ config PCI_MSI
If you don't know what to do here, say Y.
config PCI_MSI_IRQ_DOMAIN
- def_bool ARC || ARM || ARM64 || X86
+ def_bool ARC || ARM || ARM64 || X86 || RISCV
depends on PCI_MSI
select GENERIC_MSI_IRQ_DOMAIN
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index 544922f097c0..2fccb5762c76 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -336,15 +336,6 @@ static inline int pcie_cap_version(const struct pci_dev *dev)
return pcie_caps_reg(dev) & PCI_EXP_FLAGS_VERS;
}
-static bool pcie_downstream_port(const struct pci_dev *dev)
-{
- int type = pci_pcie_type(dev);
-
- return type == PCI_EXP_TYPE_ROOT_PORT ||
- type == PCI_EXP_TYPE_DOWNSTREAM ||
- type == PCI_EXP_TYPE_PCIE_BRIDGE;
-}
-
bool pcie_cap_has_lnkctl(const struct pci_dev *dev)
{
int type = pci_pcie_type(dev);
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 495059d923f7..8e40b3e6da77 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -417,11 +417,9 @@ struct pci_bus *pci_bus_get(struct pci_bus *bus)
get_device(&bus->dev);
return bus;
}
-EXPORT_SYMBOL(pci_bus_get);
void pci_bus_put(struct pci_bus *bus)
{
if (bus)
put_device(&bus->dev);
}
-EXPORT_SYMBOL(pci_bus_put);
diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c
index c742881b5061..c8cb9c5188a4 100644
--- a/drivers/pci/controller/pci-host-common.c
+++ b/drivers/pci/controller/pci-host-common.c
@@ -43,9 +43,8 @@ static struct pci_config_window *gen_pci_init(struct device *dev,
goto err_out;
}
- err = devm_add_action(dev, gen_pci_unmap_cfg, cfg);
+ err = devm_add_action_or_reset(dev, gen_pci_unmap_cfg, cfg);
if (err) {
- gen_pci_unmap_cfg(cfg);
goto err_out;
}
return cfg;
diff --git a/drivers/pci/hotplug/ibmphp_res.c b/drivers/pci/hotplug/ibmphp_res.c
index 5e8caf7a4452..5c93aa14f0de 100644
--- a/drivers/pci/hotplug/ibmphp_res.c
+++ b/drivers/pci/hotplug/ibmphp_res.c
@@ -1941,6 +1941,7 @@ static int __init update_bridge_ranges(struct bus_node **bus)
break;
case PCI_HEADER_TYPE_BRIDGE:
function = 0x8;
+ /* fall through */
case PCI_HEADER_TYPE_MULTIBRIDGE:
/* We assume here that only 1 bus behind the bridge
TO DO: add functionality for several:
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index 8c51a04b8083..654c972b8ea0 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -110,9 +110,9 @@ struct controller {
*
* @OFF_STATE: slot is powered off, no subordinate devices are enumerated
* @BLINKINGON_STATE: slot will be powered on after the 5 second delay,
- * green led is blinking
+ * Power Indicator is blinking
* @BLINKINGOFF_STATE: slot will be powered off after the 5 second delay,
- * green led is blinking
+ * Power Indicator is blinking
* @POWERON_STATE: slot is currently powering on
* @POWEROFF_STATE: slot is currently powering off
* @ON_STATE: slot is powered on, subordinate devices have been enumerated
@@ -167,12 +167,11 @@ int pciehp_power_on_slot(struct controller *ctrl);
void pciehp_power_off_slot(struct controller *ctrl);
void pciehp_get_power_status(struct controller *ctrl, u8 *status);
-void pciehp_set_attention_status(struct controller *ctrl, u8 status);
+#define INDICATOR_NOOP -1 /* Leave indicator unchanged */
+void pciehp_set_indicators(struct controller *ctrl, int pwr, int attn);
+
void pciehp_get_latch_status(struct controller *ctrl, u8 *status);
int pciehp_query_power_fault(struct controller *ctrl);
-void pciehp_green_led_on(struct controller *ctrl);
-void pciehp_green_led_off(struct controller *ctrl);
-void pciehp_green_led_blink(struct controller *ctrl);
bool pciehp_card_present(struct controller *ctrl);
bool pciehp_card_present_or_link_active(struct controller *ctrl);
int pciehp_check_link_status(struct controller *ctrl);
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 6ad0d86762cb..b3122c151b80 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -95,15 +95,20 @@ static void cleanup_slot(struct controller *ctrl)
}
/*
- * set_attention_status - Turns the Amber LED for a slot on, off or blink
+ * set_attention_status - Turns the Attention Indicator on, off or blinking
*/
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
{
struct controller *ctrl = to_ctrl(hotplug_slot);
struct pci_dev *pdev = ctrl->pcie->port;
+ if (status)
+ status <<= PCI_EXP_SLTCTL_ATTN_IND_SHIFT;
+ else
+ status = PCI_EXP_SLTCTL_ATTN_IND_OFF;
+
pci_config_pm_runtime_get(pdev);
- pciehp_set_attention_status(ctrl, status);
+ pciehp_set_indicators(ctrl, INDICATOR_NOOP, status);
pci_config_pm_runtime_put(pdev);
return 0;
}
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index 631ced0ab28a..21af7b16d7a4 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -30,7 +30,10 @@
static void set_slot_off(struct controller *ctrl)
{
- /* turn off slot, turn on Amber LED, turn off Green LED if supported*/
+ /*
+ * Turn off slot, turn on attention indicator, turn off power
+ * indicator
+ */
if (POWER_CTRL(ctrl)) {
pciehp_power_off_slot(ctrl);
@@ -42,8 +45,8 @@ static void set_slot_off(struct controller *ctrl)
msleep(1000);
}
- pciehp_green_led_off(ctrl);
- pciehp_set_attention_status(ctrl, 1);
+ pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
+ PCI_EXP_SLTCTL_ATTN_IND_ON);
}
/**
@@ -65,7 +68,8 @@ static int board_added(struct controller *ctrl)
return retval;
}
- pciehp_green_led_blink(ctrl);
+ pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK,
+ INDICATOR_NOOP);
/* Check link training status */
retval = pciehp_check_link_status(ctrl);
@@ -90,8 +94,8 @@ static int board_added(struct controller *ctrl)
}
}
- pciehp_green_led_on(ctrl);
- pciehp_set_attention_status(ctrl, 0);
+ pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON,
+ PCI_EXP_SLTCTL_ATTN_IND_OFF);
return 0;
err_exit:
@@ -100,7 +104,7 @@ err_exit:
}
/**
- * remove_board - Turns off slot and LEDs
+ * remove_board - Turn off slot and Power Indicator
* @ctrl: PCIe hotplug controller where board is being removed
* @safe_removal: whether the board is safely removed (versus surprise removed)
*/
@@ -123,8 +127,8 @@ static void remove_board(struct controller *ctrl, bool safe_removal)
&ctrl->pending_events);
}
- /* turn off Green LED */
- pciehp_green_led_off(ctrl);
+ pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
+ INDICATOR_NOOP);
}
static int pciehp_enable_slot(struct controller *ctrl);
@@ -171,9 +175,9 @@ void pciehp_handle_button_press(struct controller *ctrl)
ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n",
slot_name(ctrl));
}
- /* blink green LED and turn off amber */
- pciehp_green_led_blink(ctrl);
- pciehp_set_attention_status(ctrl, 0);
+ /* blink power indicator and turn off attention */
+ pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK,
+ PCI_EXP_SLTCTL_ATTN_IND_OFF);
schedule_delayed_work(&ctrl->button_work, 5 * HZ);
break;
case BLINKINGOFF_STATE:
@@ -187,12 +191,13 @@ void pciehp_handle_button_press(struct controller *ctrl)
cancel_delayed_work(&ctrl->button_work);
if (ctrl->state == BLINKINGOFF_STATE) {
ctrl->state = ON_STATE;
- pciehp_green_led_on(ctrl);
+ pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON,
+ PCI_EXP_SLTCTL_ATTN_IND_OFF);
} else {
ctrl->state = OFF_STATE;
- pciehp_green_led_off(ctrl);
+ pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
+ PCI_EXP_SLTCTL_ATTN_IND_OFF);
}
- pciehp_set_attention_status(ctrl, 0);
ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
slot_name(ctrl));
break;
@@ -310,7 +315,9 @@ static int pciehp_enable_slot(struct controller *ctrl)
pm_runtime_get_sync(&ctrl->pcie->port->dev);
ret = __pciehp_enable_slot(ctrl);
if (ret && ATTN_BUTTN(ctrl))
- pciehp_green_led_off(ctrl); /* may be blinking */
+ /* may be blinking */
+ pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
+ INDICATOR_NOOP);
pm_runtime_put(&ctrl->pcie->port->dev);
mutex_lock(&ctrl->state_lock);
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index bd990e3371e3..1a522c1c4177 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -418,65 +418,40 @@ int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot,
return 0;
}
-void pciehp_set_attention_status(struct controller *ctrl, u8 value)
+/**
+ * pciehp_set_indicators() - set attention indicator, power indicator, or both
+ * @ctrl: PCIe hotplug controller
+ * @pwr: one of:
+ * PCI_EXP_SLTCTL_PWR_IND_ON
+ * PCI_EXP_SLTCTL_PWR_IND_BLINK
+ * PCI_EXP_SLTCTL_PWR_IND_OFF
+ * @attn: one of:
+ * PCI_EXP_SLTCTL_ATTN_IND_ON
+ * PCI_EXP_SLTCTL_ATTN_IND_BLINK
+ * PCI_EXP_SLTCTL_ATTN_IND_OFF
+ *
+ * Either @pwr or @attn can also be INDICATOR_NOOP to leave that indicator
+ * unchanged.
+ */
+void pciehp_set_indicators(struct controller *ctrl, int pwr, int attn)
{
- u16 slot_cmd;
+ u16 cmd = 0, mask = 0;
- if (!ATTN_LED(ctrl))
- return;
-
- switch (value) {
- case 0: /* turn off */
- slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_OFF;
- break;
- case 1: /* turn on */
- slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_ON;
- break;
- case 2: /* turn blink */
- slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_BLINK;
- break;
- default:
- return;
+ if (PWR_LED(ctrl) && pwr != INDICATOR_NOOP) {
+ cmd |= (pwr & PCI_EXP_SLTCTL_PIC);
+ mask |= PCI_EXP_SLTCTL_PIC;
}
- pcie_write_cmd_nowait(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC);
- ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
- pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
-}
-void pciehp_green_led_on(struct controller *ctrl)
-{
- if (!PWR_LED(ctrl))
- return;
-
- pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON,
- PCI_EXP_SLTCTL_PIC);
- ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
- pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
- PCI_EXP_SLTCTL_PWR_IND_ON);
-}
-
-void pciehp_green_led_off(struct controller *ctrl)
-{
- if (!PWR_LED(ctrl))
- return;
-
- pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
- PCI_EXP_SLTCTL_PIC);
- ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
- pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
- PCI_EXP_SLTCTL_PWR_IND_OFF);
-}
-
-void pciehp_green_led_blink(struct controller *ctrl)
-{
- if (!PWR_LED(ctrl))
- return;
+ if (ATTN_LED(ctrl) && attn != INDICATOR_NOOP) {
+ cmd |= (attn & PCI_EXP_SLTCTL_AIC);
+ mask |= PCI_EXP_SLTCTL_AIC;
+ }
- pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK,
- PCI_EXP_SLTCTL_PIC);
- ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
- pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
- PCI_EXP_SLTCTL_PWR_IND_BLINK);
+ if (cmd) {
+ pcie_write_cmd_nowait(ctrl, cmd, mask);
+ ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+ pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd);
+ }
}
int pciehp_power_on_slot(struct controller *ctrl)
@@ -638,8 +613,8 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
ctrl->power_fault_detected = 1;
ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl));
- pciehp_set_attention_status(ctrl, 1);
- pciehp_green_led_off(ctrl);
+ pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
+ PCI_EXP_SLTCTL_ATTN_IND_ON);
}
/*
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 525fd3f272b3..b3f972e8cfed 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -240,6 +240,173 @@ void pci_iov_remove_virtfn(struct pci_dev *dev, int id)
pci_dev_put(dev);
}
+static ssize_t sriov_totalvfs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ return sprintf(buf, "%u\n", pci_sriov_get_totalvfs(pdev));
+}
+
+static ssize_t sriov_numvfs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ return sprintf(buf, "%u\n", pdev->sriov->num_VFs);
+}
+
+/*
+ * num_vfs > 0; number of VFs to enable
+ * num_vfs = 0; disable all VFs
+ *
+ * Note: SRIOV spec does not allow partial VF
+ * disable, so it's all or none.
+ */
+static ssize_t sriov_numvfs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ int ret;
+ u16 num_vfs;
+
+ ret = kstrtou16(buf, 0, &num_vfs);
+ if (ret < 0)
+ return ret;
+
+ if (num_vfs > pci_sriov_get_totalvfs(pdev))
+ return -ERANGE;
+
+ device_lock(&pdev->dev);
+
+ if (num_vfs == pdev->sriov->num_VFs)
+ goto exit;
+
+ /* is PF driver loaded w/callback */
+ if (!pdev->driver || !pdev->driver->sriov_configure) {
+ pci_info(pdev, "Driver does not support SRIOV configuration via sysfs\n");
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ if (num_vfs == 0) {
+ /* disable VFs */
+ ret = pdev->driver->sriov_configure(pdev, 0);
+ goto exit;
+ }
+
+ /* enable VFs */
+ if (pdev->sriov->num_VFs) {
+ pci_warn(pdev, "%d VFs already enabled. Disable before enabling %d VFs\n",
+ pdev->sriov->num_VFs, num_vfs);
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ ret = pdev->driver->sriov_configure(pdev, num_vfs);
+ if (ret < 0)
+ goto exit;
+
+ if (ret != num_vfs)
+ pci_warn(pdev, "%d VFs requested; only %d enabled\n",
+ num_vfs, ret);
+
+exit:
+ device_unlock(&pdev->dev);
+
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t sriov_offset_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ return sprintf(buf, "%u\n", pdev->sriov->offset);
+}
+
+static ssize_t sriov_stride_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ return sprintf(buf, "%u\n", pdev->sriov->stride);
+}
+
+static ssize_t sriov_vf_device_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ return sprintf(buf, "%x\n", pdev->sriov->vf_device);
+}
+
+static ssize_t sriov_drivers_autoprobe_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ return sprintf(buf, "%u\n", pdev->sriov->drivers_autoprobe);
+}
+
+static ssize_t sriov_drivers_autoprobe_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ bool drivers_autoprobe;
+
+ if (kstrtobool(buf, &drivers_autoprobe) < 0)
+ return -EINVAL;
+
+ pdev->sriov->drivers_autoprobe = drivers_autoprobe;
+
+ return count;
+}
+
+static DEVICE_ATTR_RO(sriov_totalvfs);
+static DEVICE_ATTR_RW(sriov_numvfs);
+static DEVICE_ATTR_RO(sriov_offset);
+static DEVICE_ATTR_RO(sriov_stride);
+static DEVICE_ATTR_RO(sriov_vf_device);
+static DEVICE_ATTR_RW(sriov_drivers_autoprobe);
+
+static struct attribute *sriov_dev_attrs[] = {
+ &dev_attr_sriov_totalvfs.attr,
+ &dev_attr_sriov_numvfs.attr,
+ &dev_attr_sriov_offset.attr,
+ &dev_attr_sriov_stride.attr,
+ &dev_attr_sriov_vf_device.attr,
+ &dev_attr_sriov_drivers_autoprobe.attr,
+ NULL,
+};
+
+static umode_t sriov_attrs_are_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+
+ if (!dev_is_pf(dev))
+ return 0;
+
+ return a->mode;
+}
+
+const struct attribute_group sriov_dev_attr_group = {
+ .attrs = sriov_dev_attrs,
+ .is_visible = sriov_attrs_are_visible,
+};
+
int __weak pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs)
{
return 0;
@@ -557,8 +724,8 @@ static void sriov_restore_state(struct pci_dev *dev)
ctrl |= iov->ctrl & PCI_SRIOV_CTRL_ARI;
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, ctrl);
- for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++)
- pci_update_resource(dev, i);
+ for (i = 0; i < PCI_SRIOV_NUM_BARS; i++)
+ pci_update_resource(dev, i + PCI_IOV_RESOURCES);
pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
pci_iov_set_numvfs(dev, iov->num_VFs);
diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c
index 234476226529..0608aae72ccc 100644
--- a/drivers/pci/p2pdma.c
+++ b/drivers/pci/p2pdma.c
@@ -18,13 +18,32 @@
#include <linux/percpu-refcount.h>
#include <linux/random.h>
#include <linux/seq_buf.h>
-#include <linux/iommu.h>
+#include <linux/xarray.h>
+
+enum pci_p2pdma_map_type {
+ PCI_P2PDMA_MAP_UNKNOWN = 0,
+ PCI_P2PDMA_MAP_NOT_SUPPORTED,
+ PCI_P2PDMA_MAP_BUS_ADDR,
+ PCI_P2PDMA_MAP_THRU_HOST_BRIDGE,
+};
struct pci_p2pdma {
struct gen_pool *pool;
bool p2pmem_published;
+ struct xarray map_types;
};
+struct pci_p2pdma_pagemap {
+ struct dev_pagemap pgmap;
+ struct pci_dev *provider;
+ u64 bus_offset;
+};
+
+static struct pci_p2pdma_pagemap *to_p2p_pgmap(struct dev_pagemap *pgmap)
+{
+ return container_of(pgmap, struct pci_p2pdma_pagemap, pgmap);
+}
+
static ssize_t size_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -87,6 +106,7 @@ static void pci_p2pdma_release(void *data)
gen_pool_destroy(p2pdma->pool);
sysfs_remove_group(&pdev->dev.kobj, &p2pmem_group);
+ xa_destroy(&p2pdma->map_types);
}
static int pci_p2pdma_setup(struct pci_dev *pdev)
@@ -98,6 +118,8 @@ static int pci_p2pdma_setup(struct pci_dev *pdev)
if (!p2p)
return -ENOMEM;
+ xa_init(&p2p->map_types);
+
p2p->pool = gen_pool_create(PAGE_SHIFT, dev_to_node(&pdev->dev));
if (!p2p->pool)
goto out;
@@ -135,6 +157,7 @@ out:
int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
u64 offset)
{
+ struct pci_p2pdma_pagemap *p2p_pgmap;
struct dev_pagemap *pgmap;
void *addr;
int error;
@@ -157,14 +180,18 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
return error;
}
- pgmap = devm_kzalloc(&pdev->dev, sizeof(*pgmap), GFP_KERNEL);
- if (!pgmap)
+ p2p_pgmap = devm_kzalloc(&pdev->dev, sizeof(*p2p_pgmap), GFP_KERNEL);
+ if (!p2p_pgmap)
return -ENOMEM;
+
+ pgmap = &p2p_pgmap->pgmap;
pgmap->res.start = pci_resource_start(pdev, bar) + offset;
pgmap->res.end = pgmap->res.start + size - 1;
pgmap->res.flags = pci_resource_flags(pdev, bar);
pgmap->type = MEMORY_DEVICE_PCI_P2PDMA;
- pgmap->pci_p2pdma_bus_offset = pci_bus_address(pdev, bar) -
+
+ p2p_pgmap->provider = pdev;
+ p2p_pgmap->bus_offset = pci_bus_address(pdev, bar) -
pci_resource_start(pdev, bar);
addr = devm_memremap_pages(&pdev->dev, pgmap);
@@ -246,19 +273,32 @@ static void seq_buf_print_bus_devfn(struct seq_buf *buf, struct pci_dev *pdev)
seq_buf_printf(buf, "%s;", pci_name(pdev));
}
-/*
- * If we can't find a common upstream bridge take a look at the root
- * complex and compare it to a whitelist of known good hardware.
- */
-static bool root_complex_whitelist(struct pci_dev *dev)
+static const struct pci_p2pdma_whitelist_entry {
+ unsigned short vendor;
+ unsigned short device;
+ enum {
+ REQ_SAME_HOST_BRIDGE = 1 << 0,
+ } flags;
+} pci_p2pdma_whitelist[] = {
+ /* AMD ZEN */
+ {PCI_VENDOR_ID_AMD, 0x1450, 0},
+
+ /* Intel Xeon E5/Core i7 */
+ {PCI_VENDOR_ID_INTEL, 0x3c00, REQ_SAME_HOST_BRIDGE},
+ {PCI_VENDOR_ID_INTEL, 0x3c01, REQ_SAME_HOST_BRIDGE},
+ /* Intel Xeon E7 v3/Xeon E5 v3/Core i7 */
+ {PCI_VENDOR_ID_INTEL, 0x2f00, REQ_SAME_HOST_BRIDGE},
+ {PCI_VENDOR_ID_INTEL, 0x2f01, REQ_SAME_HOST_BRIDGE},
+ {}
+};
+
+static bool __host_bridge_whitelist(struct pci_host_bridge *host,
+ bool same_host_bridge)
{
- struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
struct pci_dev *root = pci_get_slot(host->bus, PCI_DEVFN(0, 0));
+ const struct pci_p2pdma_whitelist_entry *entry;
unsigned short vendor, device;
- if (iommu_present(dev->dev.bus))
- return false;
-
if (!root)
return false;
@@ -266,65 +306,49 @@ static bool root_complex_whitelist(struct pci_dev *dev)
device = root->device;
pci_dev_put(root);
- /* AMD ZEN host bridges can do peer to peer */
- if (vendor == PCI_VENDOR_ID_AMD && device == 0x1450)
+ for (entry = pci_p2pdma_whitelist; entry->vendor; entry++) {
+ if (vendor != entry->vendor || device != entry->device)
+ continue;
+ if (entry->flags & REQ_SAME_HOST_BRIDGE && !same_host_bridge)
+ return false;
+
return true;
+ }
return false;
}
/*
- * Find the distance through the nearest common upstream bridge between
- * two PCI devices.
- *
- * If the two devices are the same device then 0 will be returned.
- *
- * If there are two virtual functions of the same device behind the same
- * bridge port then 2 will be returned (one step down to the PCIe switch,
- * then one step back to the same device).
- *
- * In the case where two devices are connected to the same PCIe switch, the
- * value 4 will be returned. This corresponds to the following PCI tree:
- *
- * -+ Root Port
- * \+ Switch Upstream Port
- * +-+ Switch Downstream Port
- * + \- Device A
- * \-+ Switch Downstream Port
- * \- Device B
- *
- * The distance is 4 because we traverse from Device A through the downstream
- * port of the switch, to the common upstream port, back up to the second
- * downstream port and then to Device B.
- *
- * Any two devices that don't have a common upstream bridge will return -1.
- * In this way devices on separate PCIe root ports will be rejected, which
- * is what we want for peer-to-peer seeing each PCIe root port defines a
- * separate hierarchy domain and there's no way to determine whether the root
- * complex supports forwarding between them.
- *
- * In the case where two devices are connected to different PCIe switches,
- * this function will still return a positive distance as long as both
- * switches eventually have a common upstream bridge. Note this covers
- * the case of using multiple PCIe switches to achieve a desired level of
- * fan-out from a root port. The exact distance will be a function of the
- * number of switches between Device A and Device B.
- *
- * If a bridge which has any ACS redirection bits set is in the path
- * then this functions will return -2. This is so we reject any
- * cases where the TLPs are forwarded up into the root complex.
- * In this case, a list of all infringing bridge addresses will be
- * populated in acs_list (assuming it's non-null) for printk purposes.
+ * If we can't find a common upstream bridge take a look at the root
+ * complex and compare it to a whitelist of known good hardware.
*/
-static int upstream_bridge_distance(struct pci_dev *provider,
- struct pci_dev *client,
- struct seq_buf *acs_list)
+static bool host_bridge_whitelist(struct pci_dev *a, struct pci_dev *b)
+{
+ struct pci_host_bridge *host_a = pci_find_host_bridge(a->bus);
+ struct pci_host_bridge *host_b = pci_find_host_bridge(b->bus);
+
+ if (host_a == host_b)
+ return __host_bridge_whitelist(host_a, true);
+
+ if (__host_bridge_whitelist(host_a, false) &&
+ __host_bridge_whitelist(host_b, false))
+ return true;
+
+ return false;
+}
+
+static enum pci_p2pdma_map_type
+__upstream_bridge_distance(struct pci_dev *provider, struct pci_dev *client,
+ int *dist, bool *acs_redirects, struct seq_buf *acs_list)
{
struct pci_dev *a = provider, *b = client, *bb;
int dist_a = 0;
int dist_b = 0;
int acs_cnt = 0;
+ if (acs_redirects)
+ *acs_redirects = false;
+
/*
* Note, we don't need to take references to devices returned by
* pci_upstream_bridge() seeing we hold a reference to a child
@@ -353,15 +377,10 @@ static int upstream_bridge_distance(struct pci_dev *provider,
dist_a++;
}
- /*
- * Allow the connection if both devices are on a whitelisted root
- * complex, but add an arbitrary large value to the distance.
- */
- if (root_complex_whitelist(provider) &&
- root_complex_whitelist(client))
- return 0x1000 + dist_a + dist_b;
+ if (dist)
+ *dist = dist_a + dist_b;
- return -1;
+ return PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
check_b_path_acs:
bb = b;
@@ -378,33 +397,110 @@ check_b_path_acs:
bb = pci_upstream_bridge(bb);
}
- if (acs_cnt)
- return -2;
+ if (dist)
+ *dist = dist_a + dist_b;
+
+ if (acs_cnt) {
+ if (acs_redirects)
+ *acs_redirects = true;
+
+ return PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
+ }
+
+ return PCI_P2PDMA_MAP_BUS_ADDR;
+}
+
+static unsigned long map_types_idx(struct pci_dev *client)
+{
+ return (pci_domain_nr(client->bus) << 16) |
+ (client->bus->number << 8) | client->devfn;
+}
+
+/*
+ * Find the distance through the nearest common upstream bridge between
+ * two PCI devices.
+ *
+ * If the two devices are the same device then 0 will be returned.
+ *
+ * If there are two virtual functions of the same device behind the same
+ * bridge port then 2 will be returned (one step down to the PCIe switch,
+ * then one step back to the same device).
+ *
+ * In the case where two devices are connected to the same PCIe switch, the
+ * value 4 will be returned. This corresponds to the following PCI tree:
+ *
+ * -+ Root Port
+ * \+ Switch Upstream Port
+ * +-+ Switch Downstream Port
+ * + \- Device A
+ * \-+ Switch Downstream Port
+ * \- Device B
+ *
+ * The distance is 4 because we traverse from Device A through the downstream
+ * port of the switch, to the common upstream port, back up to the second
+ * downstream port and then to Device B.
+ *
+ * Any two devices that cannot communicate using p2pdma will return
+ * PCI_P2PDMA_MAP_NOT_SUPPORTED.
+ *
+ * Any two devices that have a data path that goes through the host bridge
+ * will consult a whitelist. If the host bridges are on the whitelist,
+ * this function will return PCI_P2PDMA_MAP_THRU_HOST_BRIDGE.
+ *
+ * If either bridge is not on the whitelist this function returns
+ * PCI_P2PDMA_MAP_NOT_SUPPORTED.
+ *
+ * If a bridge which has any ACS redirection bits set is in the path,
+ * acs_redirects will be set to true. In this case, a list of all infringing
+ * bridge addresses will be populated in acs_list (assuming it's non-null)
+ * for printk purposes.
+ */
+static enum pci_p2pdma_map_type
+upstream_bridge_distance(struct pci_dev *provider, struct pci_dev *client,
+ int *dist, bool *acs_redirects, struct seq_buf *acs_list)
+{
+ enum pci_p2pdma_map_type map_type;
+
+ map_type = __upstream_bridge_distance(provider, client, dist,
+ acs_redirects, acs_list);
+
+ if (map_type == PCI_P2PDMA_MAP_THRU_HOST_BRIDGE) {
+ if (!host_bridge_whitelist(provider, client))
+ map_type = PCI_P2PDMA_MAP_NOT_SUPPORTED;
+ }
+
+ if (provider->p2pdma)
+ xa_store(&provider->p2pdma->map_types, map_types_idx(client),
+ xa_mk_value(map_type), GFP_KERNEL);
- return dist_a + dist_b;
+ return map_type;
}
-static int upstream_bridge_distance_warn(struct pci_dev *provider,
- struct pci_dev *client)
+static enum pci_p2pdma_map_type
+upstream_bridge_distance_warn(struct pci_dev *provider, struct pci_dev *client,
+ int *dist)
{
struct seq_buf acs_list;
+ bool acs_redirects;
int ret;
seq_buf_init(&acs_list, kmalloc(PAGE_SIZE, GFP_KERNEL), PAGE_SIZE);
if (!acs_list.buffer)
return -ENOMEM;
- ret = upstream_bridge_distance(provider, client, &acs_list);
- if (ret == -2) {
- pci_warn(client, "cannot be used for peer-to-peer DMA as ACS redirect is set between the client and provider (%s)\n",
+ ret = upstream_bridge_distance(provider, client, dist, &acs_redirects,
+ &acs_list);
+ if (acs_redirects) {
+ pci_warn(client, "ACS redirect is set between the client and provider (%s)\n",
pci_name(provider));
/* Drop final semicolon */
acs_list.buffer[acs_list.len-1] = 0;
pci_warn(client, "to disable ACS redirect for this path, add the kernel parameter: pci=disable_acs_redir=%s\n",
acs_list.buffer);
+ }
- } else if (ret < 0) {
- pci_warn(client, "cannot be used for peer-to-peer DMA as the client and provider (%s) do not share an upstream bridge\n",
+ if (ret == PCI_P2PDMA_MAP_NOT_SUPPORTED) {
+ pci_warn(client, "cannot be used for peer-to-peer DMA as the client and provider (%s) do not share an upstream bridge or whitelisted host bridge\n",
pci_name(provider));
}
@@ -421,22 +517,22 @@ static int upstream_bridge_distance_warn(struct pci_dev *provider,
* @num_clients: number of clients in the array
* @verbose: if true, print warnings for devices when we return -1
*
- * Returns -1 if any of the clients are not compatible (behind the same
- * root port as the provider), otherwise returns a positive number where
- * a lower number is the preferable choice. (If there's one client
- * that's the same as the provider it will return 0, which is best choice).
+ * Returns -1 if any of the clients are not compatible, otherwise returns a
+ * positive number where a lower number is the preferable choice. (If there's
+ * one client that's the same as the provider it will return 0, which is best
+ * choice).
*
- * For now, "compatible" means the provider and the clients are all behind
- * the same PCI root port. This cuts out cases that may work but is safest
- * for the user. Future work can expand this to white-list root complexes that
- * can safely forward between each ports.
+ * "compatible" means the provider and the clients are either all behind
+ * the same PCI root port or the host bridges connected to each of the devices
+ * are listed in the 'pci_p2pdma_whitelist'.
*/
int pci_p2pdma_distance_many(struct pci_dev *provider, struct device **clients,
int num_clients, bool verbose)
{
bool not_supported = false;
struct pci_dev *pci_client;
- int distance = 0;
+ int total_dist = 0;
+ int distance;
int i, ret;
if (num_clients == 0)
@@ -461,26 +557,26 @@ int pci_p2pdma_distance_many(struct pci_dev *provider, struct device **clients,
if (verbose)
ret = upstream_bridge_distance_warn(provider,
- pci_client);
+ pci_client, &distance);
else
ret = upstream_bridge_distance(provider, pci_client,
- NULL);
+ &distance, NULL, NULL);
pci_dev_put(pci_client);
- if (ret < 0)
+ if (ret == PCI_P2PDMA_MAP_NOT_SUPPORTED)
not_supported = true;
if (not_supported && !verbose)
break;
- distance += ret;
+ total_dist += distance;
}
if (not_supported)
return -1;
- return distance;
+ return total_dist;
}
EXPORT_SYMBOL_GPL(pci_p2pdma_distance_many);
@@ -706,21 +802,19 @@ void pci_p2pmem_publish(struct pci_dev *pdev, bool publish)
}
EXPORT_SYMBOL_GPL(pci_p2pmem_publish);
-/**
- * pci_p2pdma_map_sg - map a PCI peer-to-peer scatterlist for DMA
- * @dev: device doing the DMA request
- * @sg: scatter list to map
- * @nents: elements in the scatterlist
- * @dir: DMA direction
- *
- * Scatterlists mapped with this function should not be unmapped in any way.
- *
- * Returns the number of SG entries mapped or 0 on error.
- */
-int pci_p2pdma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
- enum dma_data_direction dir)
+static enum pci_p2pdma_map_type pci_p2pdma_map_type(struct pci_dev *provider,
+ struct pci_dev *client)
+{
+ if (!provider->p2pdma)
+ return PCI_P2PDMA_MAP_NOT_SUPPORTED;
+
+ return xa_to_value(xa_load(&provider->p2pdma->map_types,
+ map_types_idx(client)));
+}
+
+static int __pci_p2pdma_map_sg(struct pci_p2pdma_pagemap *p2p_pgmap,
+ struct device *dev, struct scatterlist *sg, int nents)
{
- struct dev_pagemap *pgmap;
struct scatterlist *s;
phys_addr_t paddr;
int i;
@@ -736,16 +830,80 @@ int pci_p2pdma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
return 0;
for_each_sg(sg, s, nents, i) {
- pgmap = sg_page(s)->pgmap;
paddr = sg_phys(s);
- s->dma_address = paddr - pgmap->pci_p2pdma_bus_offset;
+ s->dma_address = paddr - p2p_pgmap->bus_offset;
sg_dma_len(s) = s->length;
}
return nents;
}
-EXPORT_SYMBOL_GPL(pci_p2pdma_map_sg);
+
+/**
+ * pci_p2pdma_map_sg - map a PCI peer-to-peer scatterlist for DMA
+ * @dev: device doing the DMA request
+ * @sg: scatter list to map
+ * @nents: elements in the scatterlist
+ * @dir: DMA direction
+ * @attrs: DMA attributes passed to dma_map_sg() (if called)
+ *
+ * Scatterlists mapped with this function should be unmapped using
+ * pci_p2pdma_unmap_sg_attrs().
+ *
+ * Returns the number of SG entries mapped or 0 on error.
+ */
+int pci_p2pdma_map_sg_attrs(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction dir, unsigned long attrs)
+{
+ struct pci_p2pdma_pagemap *p2p_pgmap =
+ to_p2p_pgmap(sg_page(sg)->pgmap);
+ struct pci_dev *client;
+
+ if (WARN_ON_ONCE(!dev_is_pci(dev)))
+ return 0;
+
+ client = to_pci_dev(dev);
+
+ switch (pci_p2pdma_map_type(p2p_pgmap->provider, client)) {
+ case PCI_P2PDMA_MAP_THRU_HOST_BRIDGE:
+ return dma_map_sg_attrs(dev, sg, nents, dir, attrs);
+ case PCI_P2PDMA_MAP_BUS_ADDR:
+ return __pci_p2pdma_map_sg(p2p_pgmap, dev, sg, nents);
+ default:
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(pci_p2pdma_map_sg_attrs);
+
+/**
+ * pci_p2pdma_unmap_sg - unmap a PCI peer-to-peer scatterlist that was
+ * mapped with pci_p2pdma_map_sg()
+ * @dev: device doing the DMA request
+ * @sg: scatter list to map
+ * @nents: number of elements returned by pci_p2pdma_map_sg()
+ * @dir: DMA direction
+ * @attrs: DMA attributes passed to dma_unmap_sg() (if called)
+ */
+void pci_p2pdma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction dir, unsigned long attrs)
+{
+ struct pci_p2pdma_pagemap *p2p_pgmap =
+ to_p2p_pgmap(sg_page(sg)->pgmap);
+ enum pci_p2pdma_map_type map_type;
+ struct pci_dev *client;
+
+ if (WARN_ON_ONCE(!dev_is_pci(dev)))
+ return;
+
+ client = to_pci_dev(dev);
+
+ map_type = pci_p2pdma_map_type(p2p_pgmap->provider, client);
+
+ if (map_type == PCI_P2PDMA_MAP_THRU_HOST_BRIDGE)
+ dma_unmap_sg_attrs(dev, sg, nents, dir, attrs);
+}
+EXPORT_SYMBOL_GPL(pci_p2pdma_unmap_sg_attrs);
/**
* pci_p2pdma_enable_store - parse a configfs/sysfs attribute store
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 45049f558860..0c02d500158f 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -14,7 +14,6 @@
#include <linux/msi.h>
#include <linux/pci_hotplug.h>
#include <linux/module.h>
-#include <linux/pci-aspm.h>
#include <linux/pci-acpi.h>
#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
@@ -118,8 +117,58 @@ phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
return (phys_addr_t)mcfg_addr;
}
+/* _HPX PCI Setting Record (Type 0); same as _HPP */
+struct hpx_type0 {
+ u32 revision; /* Not present in _HPP */
+ u8 cache_line_size; /* Not applicable to PCIe */
+ u8 latency_timer; /* Not applicable to PCIe */
+ u8 enable_serr;
+ u8 enable_perr;
+};
+
+static struct hpx_type0 pci_default_type0 = {
+ .revision = 1,
+ .cache_line_size = 8,
+ .latency_timer = 0x40,
+ .enable_serr = 0,
+ .enable_perr = 0,
+};
+
+static void program_hpx_type0(struct pci_dev *dev, struct hpx_type0 *hpx)
+{
+ u16 pci_cmd, pci_bctl;
+
+ if (!hpx)
+ hpx = &pci_default_type0;
+
+ if (hpx->revision > 1) {
+ pci_warn(dev, "PCI settings rev %d not supported; using defaults\n",
+ hpx->revision);
+ hpx = &pci_default_type0;
+ }
+
+ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpx->cache_line_size);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpx->latency_timer);
+ pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
+ if (hpx->enable_serr)
+ pci_cmd |= PCI_COMMAND_SERR;
+ if (hpx->enable_perr)
+ pci_cmd |= PCI_COMMAND_PARITY;
+ pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
+
+ /* Program bridge control value */
+ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+ pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
+ hpx->latency_timer);
+ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
+ if (hpx->enable_perr)
+ pci_bctl |= PCI_BRIDGE_CTL_PARITY;
+ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
+ }
+}
+
static acpi_status decode_type0_hpx_record(union acpi_object *record,
- struct hpp_type0 *hpx0)
+ struct hpx_type0 *hpx0)
{
int i;
union acpi_object *fields = record->package.elements;
@@ -146,8 +195,30 @@ static acpi_status decode_type0_hpx_record(union acpi_object *record,
return AE_OK;
}
+/* _HPX PCI-X Setting Record (Type 1) */
+struct hpx_type1 {
+ u32 revision;
+ u8 max_mem_read;
+ u8 avg_max_split;
+ u16 tot_max_split;
+};
+
+static void program_hpx_type1(struct pci_dev *dev, struct hpx_type1 *hpx)
+{
+ int pos;
+
+ if (!hpx)
+ return;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
+ if (!pos)
+ return;
+
+ pci_warn(dev, "PCI-X settings not supported\n");
+}
+
static acpi_status decode_type1_hpx_record(union acpi_object *record,
- struct hpp_type1 *hpx1)
+ struct hpx_type1 *hpx1)
{
int i;
union acpi_object *fields = record->package.elements;
@@ -173,8 +244,130 @@ static acpi_status decode_type1_hpx_record(union acpi_object *record,
return AE_OK;
}
+static bool pcie_root_rcb_set(struct pci_dev *dev)
+{
+ struct pci_dev *rp = pcie_find_root_port(dev);
+ u16 lnkctl;
+
+ if (!rp)
+ return false;
+
+ pcie_capability_read_word(rp, PCI_EXP_LNKCTL, &lnkctl);
+ if (lnkctl & PCI_EXP_LNKCTL_RCB)
+ return true;
+
+ return false;
+}
+
+/* _HPX PCI Express Setting Record (Type 2) */
+struct hpx_type2 {
+ u32 revision;
+ u32 unc_err_mask_and;
+ u32 unc_err_mask_or;
+ u32 unc_err_sever_and;
+ u32 unc_err_sever_or;
+ u32 cor_err_mask_and;
+ u32 cor_err_mask_or;
+ u32 adv_err_cap_and;
+ u32 adv_err_cap_or;
+ u16 pci_exp_devctl_and;
+ u16 pci_exp_devctl_or;
+ u16 pci_exp_lnkctl_and;
+ u16 pci_exp_lnkctl_or;
+ u32 sec_unc_err_sever_and;
+ u32 sec_unc_err_sever_or;
+ u32 sec_unc_err_mask_and;
+ u32 sec_unc_err_mask_or;
+};
+
+static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx)
+{
+ int pos;
+ u32 reg32;
+
+ if (!hpx)
+ return;
+
+ if (!pci_is_pcie(dev))
+ return;
+
+ if (hpx->revision > 1) {
+ pci_warn(dev, "PCIe settings rev %d not supported\n",
+ hpx->revision);
+ return;
+ }
+
+ /*
+ * Don't allow _HPX to change MPS or MRRS settings. We manage
+ * those to make sure they're consistent with the rest of the
+ * platform.
+ */
+ hpx->pci_exp_devctl_and |= PCI_EXP_DEVCTL_PAYLOAD |
+ PCI_EXP_DEVCTL_READRQ;
+ hpx->pci_exp_devctl_or &= ~(PCI_EXP_DEVCTL_PAYLOAD |
+ PCI_EXP_DEVCTL_READRQ);
+
+ /* Initialize Device Control Register */
+ pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
+ ~hpx->pci_exp_devctl_and, hpx->pci_exp_devctl_or);
+
+ /* Initialize Link Control Register */
+ if (pcie_cap_has_lnkctl(dev)) {
+
+ /*
+ * If the Root Port supports Read Completion Boundary of
+ * 128, set RCB to 128. Otherwise, clear it.
+ */
+ hpx->pci_exp_lnkctl_and |= PCI_EXP_LNKCTL_RCB;
+ hpx->pci_exp_lnkctl_or &= ~PCI_EXP_LNKCTL_RCB;
+ if (pcie_root_rcb_set(dev))
+ hpx->pci_exp_lnkctl_or |= PCI_EXP_LNKCTL_RCB;
+
+ pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
+ ~hpx->pci_exp_lnkctl_and, hpx->pci_exp_lnkctl_or);
+ }
+
+ /* Find Advanced Error Reporting Enhanced Capability */
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ if (!pos)
+ return;
+
+ /* Initialize Uncorrectable Error Mask Register */
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &reg32);
+ reg32 = (reg32 & hpx->unc_err_mask_and) | hpx->unc_err_mask_or;
+ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32);
+
+ /* Initialize Uncorrectable Error Severity Register */
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &reg32);
+ reg32 = (reg32 & hpx->unc_err_sever_and) | hpx->unc_err_sever_or;
+ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32);
+
+ /* Initialize Correctable Error Mask Register */
+ pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg32);
+ reg32 = (reg32 & hpx->cor_err_mask_and) | hpx->cor_err_mask_or;
+ pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32);
+
+ /* Initialize Advanced Error Capabilities and Control Register */
+ pci_read_config_dword(dev, pos + PCI_ERR_CAP, &reg32);
+ reg32 = (reg32 & hpx->adv_err_cap_and) | hpx->adv_err_cap_or;
+
+ /* Don't enable ECRC generation or checking if unsupported */
+ if (!(reg32 & PCI_ERR_CAP_ECRC_GENC))
+ reg32 &= ~PCI_ERR_CAP_ECRC_GENE;
+ if (!(reg32 & PCI_ERR_CAP_ECRC_CHKC))
+ reg32 &= ~PCI_ERR_CAP_ECRC_CHKE;
+ pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
+
+ /*
+ * FIXME: The following two registers are not supported yet.
+ *
+ * o Secondary Uncorrectable Error Severity Register
+ * o Secondary Uncorrectable Error Mask Register
+ */
+}
+
static acpi_status decode_type2_hpx_record(union acpi_object *record,
- struct hpp_type2 *hpx2)
+ struct hpx_type2 *hpx2)
{
int i;
union acpi_object *fields = record->package.elements;
@@ -213,6 +406,164 @@ static acpi_status decode_type2_hpx_record(union acpi_object *record,
return AE_OK;
}
+/* _HPX PCI Express Setting Record (Type 3) */
+struct hpx_type3 {
+ u16 device_type;
+ u16 function_type;
+ u16 config_space_location;
+ u16 pci_exp_cap_id;
+ u16 pci_exp_cap_ver;
+ u16 pci_exp_vendor_id;
+ u16 dvsec_id;
+ u16 dvsec_rev;
+ u16 match_offset;
+ u32 match_mask_and;
+ u32 match_value;
+ u16 reg_offset;
+ u32 reg_mask_and;
+ u32 reg_mask_or;
+};
+
+enum hpx_type3_dev_type {
+ HPX_TYPE_ENDPOINT = BIT(0),
+ HPX_TYPE_LEG_END = BIT(1),
+ HPX_TYPE_RC_END = BIT(2),
+ HPX_TYPE_RC_EC = BIT(3),
+ HPX_TYPE_ROOT_PORT = BIT(4),
+ HPX_TYPE_UPSTREAM = BIT(5),
+ HPX_TYPE_DOWNSTREAM = BIT(6),
+ HPX_TYPE_PCI_BRIDGE = BIT(7),
+ HPX_TYPE_PCIE_BRIDGE = BIT(8),
+};
+
+static u16 hpx3_device_type(struct pci_dev *dev)
+{
+ u16 pcie_type = pci_pcie_type(dev);
+ const int pcie_to_hpx3_type[] = {
+ [PCI_EXP_TYPE_ENDPOINT] = HPX_TYPE_ENDPOINT,
+ [PCI_EXP_TYPE_LEG_END] = HPX_TYPE_LEG_END,
+ [PCI_EXP_TYPE_RC_END] = HPX_TYPE_RC_END,
+ [PCI_EXP_TYPE_RC_EC] = HPX_TYPE_RC_EC,
+ [PCI_EXP_TYPE_ROOT_PORT] = HPX_TYPE_ROOT_PORT,
+ [PCI_EXP_TYPE_UPSTREAM] = HPX_TYPE_UPSTREAM,
+ [PCI_EXP_TYPE_DOWNSTREAM] = HPX_TYPE_DOWNSTREAM,
+ [PCI_EXP_TYPE_PCI_BRIDGE] = HPX_TYPE_PCI_BRIDGE,
+ [PCI_EXP_TYPE_PCIE_BRIDGE] = HPX_TYPE_PCIE_BRIDGE,
+ };
+
+ if (pcie_type >= ARRAY_SIZE(pcie_to_hpx3_type))
+ return 0;
+
+ return pcie_to_hpx3_type[pcie_type];
+}
+
+enum hpx_type3_fn_type {
+ HPX_FN_NORMAL = BIT(0),
+ HPX_FN_SRIOV_PHYS = BIT(1),
+ HPX_FN_SRIOV_VIRT = BIT(2),
+};
+
+static u8 hpx3_function_type(struct pci_dev *dev)
+{
+ if (dev->is_virtfn)
+ return HPX_FN_SRIOV_VIRT;
+ else if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV) > 0)
+ return HPX_FN_SRIOV_PHYS;
+ else
+ return HPX_FN_NORMAL;
+}
+
+static bool hpx3_cap_ver_matches(u8 pcie_cap_id, u8 hpx3_cap_id)
+{
+ u8 cap_ver = hpx3_cap_id & 0xf;
+
+ if ((hpx3_cap_id & BIT(4)) && cap_ver >= pcie_cap_id)
+ return true;
+ else if (cap_ver == pcie_cap_id)
+ return true;
+
+ return false;
+}
+
+enum hpx_type3_cfg_loc {
+ HPX_CFG_PCICFG = 0,
+ HPX_CFG_PCIE_CAP = 1,
+ HPX_CFG_PCIE_CAP_EXT = 2,
+ HPX_CFG_VEND_CAP = 3,
+ HPX_CFG_DVSEC = 4,
+ HPX_CFG_MAX,
+};
+
+static void program_hpx_type3_register(struct pci_dev *dev,
+ const struct hpx_type3 *reg)
+{
+ u32 match_reg, write_reg, header, orig_value;
+ u16 pos;
+
+ if (!(hpx3_device_type(dev) & reg->device_type))
+ return;
+
+ if (!(hpx3_function_type(dev) & reg->function_type))
+ return;
+
+ switch (reg->config_space_location) {
+ case HPX_CFG_PCICFG:
+ pos = 0;
+ break;
+ case HPX_CFG_PCIE_CAP:
+ pos = pci_find_capability(dev, reg->pci_exp_cap_id);
+ if (pos == 0)
+ return;
+
+ break;
+ case HPX_CFG_PCIE_CAP_EXT:
+ pos = pci_find_ext_capability(dev, reg->pci_exp_cap_id);
+ if (pos == 0)
+ return;
+
+ pci_read_config_dword(dev, pos, &header);
+ if (!hpx3_cap_ver_matches(PCI_EXT_CAP_VER(header),
+ reg->pci_exp_cap_ver))
+ return;
+
+ break;
+ case HPX_CFG_VEND_CAP: /* Fall through */
+ case HPX_CFG_DVSEC: /* Fall through */
+ default:
+ pci_warn(dev, "Encountered _HPX type 3 with unsupported config space location");
+ return;
+ }
+
+ pci_read_config_dword(dev, pos + reg->match_offset, &match_reg);
+
+ if ((match_reg & reg->match_mask_and) != reg->match_value)
+ return;
+
+ pci_read_config_dword(dev, pos + reg->reg_offset, &write_reg);
+ orig_value = write_reg;
+ write_reg &= reg->reg_mask_and;
+ write_reg |= reg->reg_mask_or;
+
+ if (orig_value == write_reg)
+ return;
+
+ pci_write_config_dword(dev, pos + reg->reg_offset, write_reg);
+
+ pci_dbg(dev, "Applied _HPX3 at [0x%x]: 0x%08x -> 0x%08x",
+ pos, orig_value, write_reg);
+}
+
+static void program_hpx_type3(struct pci_dev *dev, struct hpx_type3 *hpx)
+{
+ if (!hpx)
+ return;
+
+ if (!pci_is_pcie(dev))
+ return;
+
+ program_hpx_type3_register(dev, hpx);
+}
+
static void parse_hpx3_register(struct hpx_type3 *hpx3_reg,
union acpi_object *reg_fields)
{
@@ -233,8 +584,7 @@ static void parse_hpx3_register(struct hpx_type3 *hpx3_reg,
}
static acpi_status program_type3_hpx_record(struct pci_dev *dev,
- union acpi_object *record,
- const struct hotplug_program_ops *hp_ops)
+ union acpi_object *record)
{
union acpi_object *fields = record->package.elements;
u32 desc_count, expected_length, revision;
@@ -258,7 +608,7 @@ static acpi_status program_type3_hpx_record(struct pci_dev *dev,
for (i = 0; i < desc_count; i++) {
reg_fields = fields + 3 + i * 14;
parse_hpx3_register(&hpx3, reg_fields);
- hp_ops->program_type3(dev, &hpx3);
+ program_hpx_type3(dev, &hpx3);
}
break;
@@ -271,15 +621,14 @@ static acpi_status program_type3_hpx_record(struct pci_dev *dev,
return AE_OK;
}
-static acpi_status acpi_run_hpx(struct pci_dev *dev, acpi_handle handle,
- const struct hotplug_program_ops *hp_ops)
+static acpi_status acpi_run_hpx(struct pci_dev *dev, acpi_handle handle)
{
acpi_status status;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *package, *record, *fields;
- struct hpp_type0 hpx0;
- struct hpp_type1 hpx1;
- struct hpp_type2 hpx2;
+ struct hpx_type0 hpx0;
+ struct hpx_type1 hpx1;
+ struct hpx_type2 hpx2;
u32 type;
int i;
@@ -314,24 +663,24 @@ static acpi_status acpi_run_hpx(struct pci_dev *dev, acpi_handle handle,
status = decode_type0_hpx_record(record, &hpx0);
if (ACPI_FAILURE(status))
goto exit;
- hp_ops->program_type0(dev, &hpx0);
+ program_hpx_type0(dev, &hpx0);
break;
case 1:
memset(&hpx1, 0, sizeof(hpx1));
status = decode_type1_hpx_record(record, &hpx1);
if (ACPI_FAILURE(status))
goto exit;
- hp_ops->program_type1(dev, &hpx1);
+ program_hpx_type1(dev, &hpx1);
break;
case 2:
memset(&hpx2, 0, sizeof(hpx2));
status = decode_type2_hpx_record(record, &hpx2);
if (ACPI_FAILURE(status))
goto exit;
- hp_ops->program_type2(dev, &hpx2);
+ program_hpx_type2(dev, &hpx2);
break;
case 3:
- status = program_type3_hpx_record(dev, record, hp_ops);
+ status = program_type3_hpx_record(dev, record);
if (ACPI_FAILURE(status))
goto exit;
break;
@@ -347,16 +696,15 @@ static acpi_status acpi_run_hpx(struct pci_dev *dev, acpi_handle handle,
return status;
}
-static acpi_status acpi_run_hpp(struct pci_dev *dev, acpi_handle handle,
- const struct hotplug_program_ops *hp_ops)
+static acpi_status acpi_run_hpp(struct pci_dev *dev, acpi_handle handle)
{
acpi_status status;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *package, *fields;
- struct hpp_type0 hpp0;
+ struct hpx_type0 hpx0;
int i;
- memset(&hpp0, 0, sizeof(hpp0));
+ memset(&hpx0, 0, sizeof(hpx0));
status = acpi_evaluate_object(handle, "_HPP", NULL, &buffer);
if (ACPI_FAILURE(status))
@@ -377,26 +725,24 @@ static acpi_status acpi_run_hpp(struct pci_dev *dev, acpi_handle handle,
}
}
- hpp0.revision = 1;
- hpp0.cache_line_size = fields[0].integer.value;
- hpp0.latency_timer = fields[1].integer.value;
- hpp0.enable_serr = fields[2].integer.value;
- hpp0.enable_perr = fields[3].integer.value;
+ hpx0.revision = 1;
+ hpx0.cache_line_size = fields[0].integer.value;
+ hpx0.latency_timer = fields[1].integer.value;
+ hpx0.enable_serr = fields[2].integer.value;
+ hpx0.enable_perr = fields[3].integer.value;
- hp_ops->program_type0(dev, &hpp0);
+ program_hpx_type0(dev, &hpx0);
exit:
kfree(buffer.pointer);
return status;
}
-/* pci_get_hp_params
+/* pci_acpi_program_hp_params
*
* @dev - the pci_dev for which we want parameters
- * @hpp - allocated by the caller
*/
-int pci_acpi_program_hp_params(struct pci_dev *dev,
- const struct hotplug_program_ops *hp_ops)
+int pci_acpi_program_hp_params(struct pci_dev *dev)
{
acpi_status status;
acpi_handle handle, phandle;
@@ -419,10 +765,10 @@ int pci_acpi_program_hp_params(struct pci_dev *dev,
* this pci dev.
*/
while (handle) {
- status = acpi_run_hpx(dev, handle, hp_ops);
+ status = acpi_run_hpx(dev, handle);
if (ACPI_SUCCESS(status))
return 0;
- status = acpi_run_hpp(dev, handle, hp_ops);
+ status = acpi_run_hpp(dev, handle);
if (ACPI_SUCCESS(status))
return 0;
if (acpi_is_root_bridge(handle))
diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c
index 06083b86d4f4..5fd90105510d 100644
--- a/drivers/pci/pci-bridge-emul.c
+++ b/drivers/pci/pci-bridge-emul.c
@@ -38,7 +38,7 @@ struct pci_bridge_reg_behavior {
u32 rsvd;
};
-const static struct pci_bridge_reg_behavior pci_regs_behavior[] = {
+static const struct pci_bridge_reg_behavior pci_regs_behavior[] = {
[PCI_VENDOR_ID / 4] = { .ro = ~0 },
[PCI_COMMAND / 4] = {
.rw = (PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
@@ -173,7 +173,7 @@ const static struct pci_bridge_reg_behavior pci_regs_behavior[] = {
},
};
-const static struct pci_bridge_reg_behavior pcie_cap_regs_behavior[] = {
+static const struct pci_bridge_reg_behavior pcie_cap_regs_behavior[] = {
[PCI_CAP_LIST_ID / 4] = {
/*
* Capability ID, Next Capability Pointer and
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 965c72104150..868e35109284 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -464,9 +464,7 @@ static ssize_t dev_rescan_store(struct device *dev,
}
return count;
}
-static struct device_attribute dev_rescan_attr = __ATTR(rescan,
- (S_IWUSR|S_IWGRP),
- NULL, dev_rescan_store);
+static DEVICE_ATTR_WO(dev_rescan);
static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
@@ -480,13 +478,12 @@ static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
pci_stop_and_remove_bus_device_locked(to_pci_dev(dev));
return count;
}
-static struct device_attribute dev_remove_attr = __ATTR_IGNORE_LOCKDEP(remove,
- (S_IWUSR|S_IWGRP),
- NULL, remove_store);
+static DEVICE_ATTR_IGNORE_LOCKDEP(remove, 0220, NULL,
+ remove_store);
-static ssize_t dev_bus_rescan_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t bus_rescan_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
unsigned long val;
struct pci_bus *bus = to_pci_bus(dev);
@@ -504,7 +501,7 @@ static ssize_t dev_bus_rescan_store(struct device *dev,
}
return count;
}
-static DEVICE_ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_bus_rescan_store);
+static DEVICE_ATTR_WO(bus_rescan);
#if defined(CONFIG_PM) && defined(CONFIG_ACPI)
static ssize_t d3cold_allowed_store(struct device *dev,
@@ -551,154 +548,6 @@ static ssize_t devspec_show(struct device *dev,
static DEVICE_ATTR_RO(devspec);
#endif
-#ifdef CONFIG_PCI_IOV
-static ssize_t sriov_totalvfs_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
-
- return sprintf(buf, "%u\n", pci_sriov_get_totalvfs(pdev));
-}
-
-
-static ssize_t sriov_numvfs_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
-
- return sprintf(buf, "%u\n", pdev->sriov->num_VFs);
-}
-
-/*
- * num_vfs > 0; number of VFs to enable
- * num_vfs = 0; disable all VFs
- *
- * Note: SRIOV spec doesn't allow partial VF
- * disable, so it's all or none.
- */
-static ssize_t sriov_numvfs_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- int ret;
- u16 num_vfs;
-
- ret = kstrtou16(buf, 0, &num_vfs);
- if (ret < 0)
- return ret;
-
- if (num_vfs > pci_sriov_get_totalvfs(pdev))
- return -ERANGE;
-
- device_lock(&pdev->dev);
-
- if (num_vfs == pdev->sriov->num_VFs)
- goto exit;
-
- /* is PF driver loaded w/callback */
- if (!pdev->driver || !pdev->driver->sriov_configure) {
- pci_info(pdev, "Driver doesn't support SRIOV configuration via sysfs\n");
- ret = -ENOENT;
- goto exit;
- }
-
- if (num_vfs == 0) {
- /* disable VFs */
- ret = pdev->driver->sriov_configure(pdev, 0);
- goto exit;
- }
-
- /* enable VFs */
- if (pdev->sriov->num_VFs) {
- pci_warn(pdev, "%d VFs already enabled. Disable before enabling %d VFs\n",
- pdev->sriov->num_VFs, num_vfs);
- ret = -EBUSY;
- goto exit;
- }
-
- ret = pdev->driver->sriov_configure(pdev, num_vfs);
- if (ret < 0)
- goto exit;
-
- if (ret != num_vfs)
- pci_warn(pdev, "%d VFs requested; only %d enabled\n",
- num_vfs, ret);
-
-exit:
- device_unlock(&pdev->dev);
-
- if (ret < 0)
- return ret;
-
- return count;
-}
-
-static ssize_t sriov_offset_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
-
- return sprintf(buf, "%u\n", pdev->sriov->offset);
-}
-
-static ssize_t sriov_stride_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
-
- return sprintf(buf, "%u\n", pdev->sriov->stride);
-}
-
-static ssize_t sriov_vf_device_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
-
- return sprintf(buf, "%x\n", pdev->sriov->vf_device);
-}
-
-static ssize_t sriov_drivers_autoprobe_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
-
- return sprintf(buf, "%u\n", pdev->sriov->drivers_autoprobe);
-}
-
-static ssize_t sriov_drivers_autoprobe_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- bool drivers_autoprobe;
-
- if (kstrtobool(buf, &drivers_autoprobe) < 0)
- return -EINVAL;
-
- pdev->sriov->drivers_autoprobe = drivers_autoprobe;
-
- return count;
-}
-
-static struct device_attribute sriov_totalvfs_attr = __ATTR_RO(sriov_totalvfs);
-static struct device_attribute sriov_numvfs_attr =
- __ATTR(sriov_numvfs, (S_IRUGO|S_IWUSR|S_IWGRP),
- sriov_numvfs_show, sriov_numvfs_store);
-static struct device_attribute sriov_offset_attr = __ATTR_RO(sriov_offset);
-static struct device_attribute sriov_stride_attr = __ATTR_RO(sriov_stride);
-static struct device_attribute sriov_vf_device_attr = __ATTR_RO(sriov_vf_device);
-static struct device_attribute sriov_drivers_autoprobe_attr =
- __ATTR(sriov_drivers_autoprobe, (S_IRUGO|S_IWUSR|S_IWGRP),
- sriov_drivers_autoprobe_show, sriov_drivers_autoprobe_store);
-#endif /* CONFIG_PCI_IOV */
-
static ssize_t driver_override_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -792,7 +641,7 @@ static struct attribute *pcie_dev_attrs[] = {
};
static struct attribute *pcibus_attrs[] = {
- &dev_attr_rescan.attr,
+ &dev_attr_bus_rescan.attr,
&dev_attr_cpuaffinity.attr,
&dev_attr_cpulistaffinity.attr,
NULL,
@@ -820,7 +669,7 @@ static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr,
!!(pdev->resource[PCI_ROM_RESOURCE].flags &
IORESOURCE_ROM_SHADOW));
}
-static struct device_attribute vga_attr = __ATTR_RO(boot_vga);
+static DEVICE_ATTR_RO(boot_vga);
static ssize_t pci_read_config(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf,
@@ -1085,7 +934,7 @@ void pci_create_legacy_files(struct pci_bus *b)
sysfs_bin_attr_init(b->legacy_io);
b->legacy_io->attr.name = "legacy_io";
b->legacy_io->size = 0xffff;
- b->legacy_io->attr.mode = S_IRUSR | S_IWUSR;
+ b->legacy_io->attr.mode = 0600;
b->legacy_io->read = pci_read_legacy_io;
b->legacy_io->write = pci_write_legacy_io;
b->legacy_io->mmap = pci_mmap_legacy_io;
@@ -1099,7 +948,7 @@ void pci_create_legacy_files(struct pci_bus *b)
sysfs_bin_attr_init(b->legacy_mem);
b->legacy_mem->attr.name = "legacy_mem";
b->legacy_mem->size = 1024*1024;
- b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR;
+ b->legacy_mem->attr.mode = 0600;
b->legacy_mem->mmap = pci_mmap_legacy_mem;
pci_adjust_legacy_attr(b, pci_mmap_mem);
error = device_create_bin_file(&b->dev, b->legacy_mem);
@@ -1306,7 +1155,7 @@ static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
}
}
res_attr->attr.name = res_attr_name;
- res_attr->attr.mode = S_IRUSR | S_IWUSR;
+ res_attr->attr.mode = 0600;
res_attr->size = pci_resource_len(pdev, num);
res_attr->private = (void *)(unsigned long)num;
retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
@@ -1419,7 +1268,7 @@ static ssize_t pci_read_rom(struct file *filp, struct kobject *kobj,
static const struct bin_attribute pci_config_attr = {
.attr = {
.name = "config",
- .mode = S_IRUGO | S_IWUSR,
+ .mode = 0644,
},
.size = PCI_CFG_SPACE_SIZE,
.read = pci_read_config,
@@ -1429,7 +1278,7 @@ static const struct bin_attribute pci_config_attr = {
static const struct bin_attribute pcie_config_attr = {
.attr = {
.name = "config",
- .mode = S_IRUGO | S_IWUSR,
+ .mode = 0644,
},
.size = PCI_CFG_SPACE_EXP_SIZE,
.read = pci_read_config,
@@ -1458,7 +1307,7 @@ static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct device_attribute reset_attr = __ATTR(reset, 0200, NULL, reset_store);
+static DEVICE_ATTR(reset, 0200, NULL, reset_store);
static int pci_create_capabilities_sysfs(struct pci_dev *dev)
{
@@ -1468,7 +1317,7 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev)
pcie_aspm_create_sysfs_dev_files(dev);
if (dev->reset_fn) {
- retval = device_create_file(&dev->dev, &reset_attr);
+ retval = device_create_file(&dev->dev, &dev_attr_reset);
if (retval)
goto error;
}
@@ -1511,7 +1360,7 @@ int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev)
sysfs_bin_attr_init(attr);
attr->size = rom_size;
attr->attr.name = "rom";
- attr->attr.mode = S_IRUSR | S_IWUSR;
+ attr->attr.mode = 0600;
attr->read = pci_read_rom;
attr->write = pci_write_rom;
retval = sysfs_create_bin_file(&pdev->dev.kobj, attr);
@@ -1553,7 +1402,7 @@ static void pci_remove_capabilities_sysfs(struct pci_dev *dev)
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);
+ device_remove_file(&dev->dev, &dev_attr_reset);
dev->reset_fn = 0;
}
}
@@ -1606,7 +1455,7 @@ static int __init pci_sysfs_init(void)
late_initcall(pci_sysfs_init);
static struct attribute *pci_dev_dev_attrs[] = {
- &vga_attr.attr,
+ &dev_attr_boot_vga.attr,
NULL,
};
@@ -1616,7 +1465,7 @@ static umode_t pci_dev_attrs_are_visible(struct kobject *kobj,
struct device *dev = kobj_to_dev(kobj);
struct pci_dev *pdev = to_pci_dev(dev);
- if (a == &vga_attr.attr)
+ if (a == &dev_attr_boot_vga.attr)
if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
return 0;
@@ -1624,8 +1473,8 @@ static umode_t pci_dev_attrs_are_visible(struct kobject *kobj,
}
static struct attribute *pci_dev_hp_attrs[] = {
- &dev_remove_attr.attr,
- &dev_rescan_attr.attr,
+ &dev_attr_remove.attr,
+ &dev_attr_dev_rescan.attr,
NULL,
};
@@ -1697,34 +1546,6 @@ static const struct attribute_group pci_dev_hp_attr_group = {
.is_visible = pci_dev_hp_attrs_are_visible,
};
-#ifdef CONFIG_PCI_IOV
-static struct attribute *sriov_dev_attrs[] = {
- &sriov_totalvfs_attr.attr,
- &sriov_numvfs_attr.attr,
- &sriov_offset_attr.attr,
- &sriov_stride_attr.attr,
- &sriov_vf_device_attr.attr,
- &sriov_drivers_autoprobe_attr.attr,
- NULL,
-};
-
-static umode_t sriov_attrs_are_visible(struct kobject *kobj,
- struct attribute *a, int n)
-{
- struct device *dev = kobj_to_dev(kobj);
-
- if (!dev_is_pf(dev))
- return 0;
-
- return a->mode;
-}
-
-static const struct attribute_group sriov_dev_attr_group = {
- .attrs = sriov_dev_attrs,
- .is_visible = sriov_attrs_are_visible,
-};
-#endif /* CONFIG_PCI_IOV */
-
static const struct attribute_group pci_dev_attr_group = {
.attrs = pci_dev_dev_attrs,
.is_visible = pci_dev_attrs_are_visible,
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 29ed5ec1ac27..80fe2d24fa37 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -890,8 +890,8 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
- if (dev->current_state != state && printk_ratelimit())
- pci_info(dev, "Refused to change power state, currently in D%d\n",
+ if (dev->current_state != state)
+ pci_info_ratelimited(dev, "Refused to change power state, currently in D%d\n",
dev->current_state);
/*
@@ -1438,7 +1438,7 @@ static void pci_restore_rebar_state(struct pci_dev *pdev)
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
res = pdev->resource + bar_idx;
- size = order_base_2((resource_size(res) >> 20) | 1) - 1;
+ size = ilog2(resource_size(res)) - 20;
ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
@@ -3576,7 +3576,7 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask)
}
/* Ensure upstream ports don't block AtomicOps on egress */
- if (!bridge->has_secondary_link) {
+ if (pci_pcie_type(bridge) == PCI_EXP_TYPE_UPSTREAM) {
pcie_capability_read_dword(bridge, PCI_EXP_DEVCTL2,
&ctl2);
if (ctl2 & PCI_EXP_DEVCTL2_ATOMIC_EGRESS_BLOCK)
@@ -5932,8 +5932,19 @@ resource_size_t __weak pcibios_default_alignment(void)
return 0;
}
-#define RESOURCE_ALIGNMENT_PARAM_SIZE COMMAND_LINE_SIZE
-static char resource_alignment_param[RESOURCE_ALIGNMENT_PARAM_SIZE] = {0};
+/*
+ * Arches that don't want to expose struct resource to userland as-is in
+ * sysfs and /proc can implement their own pci_resource_to_user().
+ */
+void __weak pci_resource_to_user(const struct pci_dev *dev, int bar,
+ const struct resource *rsrc,
+ resource_size_t *start, resource_size_t *end)
+{
+ *start = rsrc->start;
+ *end = rsrc->end;
+}
+
+static char *resource_alignment_param;
static DEFINE_SPINLOCK(resource_alignment_lock);
/**
@@ -5954,7 +5965,7 @@ static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev,
spin_lock(&resource_alignment_lock);
p = resource_alignment_param;
- if (!*p && !align)
+ if (!p || !*p)
goto out;
if (pci_has_flag(PCI_PROBE_ONLY)) {
align = 0;
@@ -6118,35 +6129,41 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev)
}
}
-static ssize_t pci_set_resource_alignment_param(const char *buf, size_t count)
+static ssize_t resource_alignment_show(struct bus_type *bus, char *buf)
{
- if (count > RESOURCE_ALIGNMENT_PARAM_SIZE - 1)
- count = RESOURCE_ALIGNMENT_PARAM_SIZE - 1;
- spin_lock(&resource_alignment_lock);
- strncpy(resource_alignment_param, buf, count);
- resource_alignment_param[count] = '\0';
- spin_unlock(&resource_alignment_lock);
- return count;
-}
+ size_t count = 0;
-static ssize_t pci_get_resource_alignment_param(char *buf, size_t size)
-{
- size_t count;
spin_lock(&resource_alignment_lock);
- count = snprintf(buf, size, "%s", resource_alignment_param);
+ if (resource_alignment_param)
+ count = snprintf(buf, PAGE_SIZE, "%s", resource_alignment_param);
spin_unlock(&resource_alignment_lock);
- return count;
-}
-static ssize_t resource_alignment_show(struct bus_type *bus, char *buf)
-{
- return pci_get_resource_alignment_param(buf, PAGE_SIZE);
+ /*
+ * When set by the command line, resource_alignment_param will not
+ * have a trailing line feed, which is ugly. So conditionally add
+ * it here.
+ */
+ if (count >= 2 && buf[count - 2] != '\n' && count < PAGE_SIZE - 1) {
+ buf[count - 1] = '\n';
+ buf[count++] = 0;
+ }
+
+ return count;
}
static ssize_t resource_alignment_store(struct bus_type *bus,
const char *buf, size_t count)
{
- return pci_set_resource_alignment_param(buf, count);
+ char *param = kstrndup(buf, count, GFP_KERNEL);
+
+ if (!param)
+ return -ENOMEM;
+
+ spin_lock(&resource_alignment_lock);
+ kfree(resource_alignment_param);
+ resource_alignment_param = param;
+ spin_unlock(&resource_alignment_lock);
+ return count;
}
static BUS_ATTR_RW(resource_alignment);
@@ -6275,8 +6292,7 @@ static int __init pci_setup(char *str)
} else if (!strncmp(str, "cbmemsize=", 10)) {
pci_cardbus_mem_size = memparse(str + 10, &str);
} else if (!strncmp(str, "resource_alignment=", 19)) {
- pci_set_resource_alignment_param(str + 19,
- strlen(str + 19));
+ resource_alignment_param = str + 19;
} else if (!strncmp(str, "ecrc=", 5)) {
pcie_ecrc_get_policy(str + 5);
} else if (!strncmp(str, "hpiosize=", 9)) {
@@ -6311,15 +6327,18 @@ static int __init pci_setup(char *str)
early_param("pci", pci_setup);
/*
- * 'disable_acs_redir_param' is initialized in pci_setup(), above, to point
- * to data in the __initdata section which will be freed after the init
- * sequence is complete. We can't allocate memory in pci_setup() because some
- * architectures do not have any memory allocation service available during
- * an early_param() call. So we allocate memory and copy the variable here
- * before the init section is freed.
+ * 'resource_alignment_param' and 'disable_acs_redir_param' are initialized
+ * in pci_setup(), above, to point to data in the __initdata section which
+ * will be freed after the init sequence is complete. We can't allocate memory
+ * in pci_setup() because some architectures do not have any memory allocation
+ * service available during an early_param() call. So we allocate memory and
+ * copy the variable here before the init section is freed.
+ *
*/
static int __init pci_realloc_setup_params(void)
{
+ resource_alignment_param = kstrdup(resource_alignment_param,
+ GFP_KERNEL);
disable_acs_redir_param = kstrdup(disable_acs_redir_param, GFP_KERNEL);
return 0;
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 1be03a97cb92..0113343cfd1e 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -39,6 +39,11 @@ int pci_probe_reset_function(struct pci_dev *dev);
int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
int pci_bus_error_reset(struct pci_dev *dev);
+#define PCI_PM_D2_DELAY 200
+#define PCI_PM_D3_WAIT 10
+#define PCI_PM_D3COLD_WAIT 100
+#define PCI_PM_BUS_WAIT 50
+
/**
* struct pci_platform_pm_ops - Firmware PM callbacks
*
@@ -84,6 +89,8 @@ 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);
+bool pci_check_pme_status(struct pci_dev *dev);
+void pci_pme_wakeup_bus(struct pci_bus *bus);
int __pci_pme_wakeup(struct pci_dev *dev, void *ign);
void pci_pme_restore(struct pci_dev *dev);
bool pci_dev_need_resume(struct pci_dev *dev);
@@ -118,11 +125,25 @@ static inline bool pci_power_manageable(struct pci_dev *pci_dev)
return !pci_has_subordinate(pci_dev) || pci_dev->bridge_d3;
}
+static inline bool pcie_downstream_port(const struct pci_dev *dev)
+{
+ int type = pci_pcie_type(dev);
+
+ return type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_DOWNSTREAM ||
+ type == PCI_EXP_TYPE_PCIE_BRIDGE;
+}
+
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 Virtual Channel */
+int pci_save_vc_state(struct pci_dev *dev);
+void pci_restore_vc_state(struct pci_dev *dev);
+void pci_allocate_vc_save_buffers(struct pci_dev *dev);
+
/* PCI /proc functions */
#ifdef CONFIG_PROC_FS
int pci_proc_attach_device(struct pci_dev *dev);
@@ -196,6 +217,9 @@ extern const struct attribute_group *pcibus_groups[];
extern const struct device_type pci_dev_type;
extern const struct attribute_group *pci_bus_groups[];
+extern unsigned long pci_hotplug_io_size;
+extern unsigned long pci_hotplug_mem_size;
+extern unsigned long pci_hotplug_bus_size;
/**
* pci_match_one_device - Tell if a PCI device structure has a matching
@@ -236,6 +260,9 @@ enum pci_bar_type {
pci_bar_mem64, /* A 64-bit memory BAR */
};
+struct device *pci_get_host_bridge_device(struct pci_dev *dev);
+void pci_put_host_bridge_device(struct device *dev);
+
int pci_configure_extended_tags(struct pci_dev *dev, void *ign);
bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
int crs_timeout);
@@ -256,6 +283,8 @@ 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);
+struct pci_bus *pci_bus_get(struct pci_bus *bus);
+void pci_bus_put(struct pci_bus *bus);
/* PCIe link information */
#define PCIE_SPEED2STR(speed) \
@@ -279,6 +308,7 @@ u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed,
enum pcie_link_width *width);
void __pcie_print_link_status(struct pci_dev *dev, bool verbose);
void pcie_report_downtraining(struct pci_dev *dev);
+void pcie_update_link_speed(struct pci_bus *bus, u16 link_status);
/* Single Root I/O Virtualization */
struct pci_sriov {
@@ -418,11 +448,12 @@ static inline void pci_restore_dpc_state(struct pci_dev *dev) {}
#endif
#ifdef CONFIG_PCI_ATS
+/* Address Translation Service */
+void pci_ats_init(struct pci_dev *dev);
void pci_restore_ats_state(struct pci_dev *dev);
#else
-static inline void pci_restore_ats_state(struct pci_dev *dev)
-{
-}
+static inline void pci_ats_init(struct pci_dev *d) { }
+static inline void pci_restore_ats_state(struct pci_dev *dev) { }
#endif /* CONFIG_PCI_ATS */
#ifdef CONFIG_PCI_IOV
@@ -433,7 +464,7 @@ void pci_iov_update_resource(struct pci_dev *dev, int resno);
resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno);
void pci_restore_iov_state(struct pci_dev *dev);
int pci_iov_bus_range(struct pci_bus *bus);
-
+extern const struct attribute_group sriov_dev_attr_group;
#else
static inline int pci_iov_init(struct pci_dev *dev)
{
@@ -519,10 +550,21 @@ static inline void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev) { }
static inline void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev) { }
#endif
+#ifdef CONFIG_PCIE_ECRC
+void pcie_set_ecrc_checking(struct pci_dev *dev);
+void pcie_ecrc_get_policy(char *str);
+#else
+static inline void pcie_set_ecrc_checking(struct pci_dev *dev) { }
+static inline void pcie_ecrc_get_policy(char *str) { }
+#endif
+
#ifdef CONFIG_PCIE_PTM
void pci_ptm_init(struct pci_dev *dev);
+int pci_enable_ptm(struct pci_dev *dev, u8 *granularity);
#else
static inline void pci_ptm_init(struct pci_dev *dev) { }
+static inline int pci_enable_ptm(struct pci_dev *dev, u8 *granularity)
+{ return -EINVAL; }
#endif
struct pci_dev_reset_methods {
@@ -559,6 +601,10 @@ struct device_node;
int of_pci_parse_bus_range(struct device_node *node, struct resource *res);
int of_get_pci_domain_nr(struct device_node *node);
int of_pci_get_max_link_speed(struct device_node *node);
+void pci_set_of_node(struct pci_dev *dev);
+void pci_release_of_node(struct pci_dev *dev);
+void pci_set_bus_of_node(struct pci_bus *bus);
+void pci_release_bus_of_node(struct pci_bus *bus);
#else
static inline int
@@ -578,6 +624,11 @@ of_pci_get_max_link_speed(struct device_node *node)
{
return -EINVAL;
}
+
+static inline void pci_set_of_node(struct pci_dev *dev) { }
+static inline void pci_release_of_node(struct pci_dev *dev) { }
+static inline void pci_set_bus_of_node(struct pci_bus *bus) { }
+static inline void pci_release_bus_of_node(struct pci_bus *bus) { }
#endif /* CONFIG_OF */
#if defined(CONFIG_OF_ADDRESS)
@@ -608,4 +659,13 @@ static inline void pci_aer_clear_fatal_status(struct pci_dev *dev) { }
static inline void pci_aer_clear_device_status(struct pci_dev *dev) { }
#endif
+#ifdef CONFIG_ACPI
+int pci_acpi_program_hp_params(struct pci_dev *dev);
+#else
+static inline int pci_acpi_program_hp_params(struct pci_dev *dev)
+{
+ return -ENODEV;
+}
+#endif
+
#endif /* DRIVERS_PCI_H */
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index e44af7f4d37f..55a6951cedd1 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -18,7 +18,6 @@
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
-#include <linux/pci-aspm.h>
#include "../pci.h"
#ifdef MODULE_PARAM_PREFIX
@@ -913,10 +912,10 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
/*
* We allocate pcie_link_state for the component on the upstream
- * end of a Link, so there's nothing to do unless this device has a
- * Link on its secondary side.
+ * end of a Link, so there's nothing to do unless this device is
+ * downstream port.
*/
- if (!pdev->has_secondary_link)
+ if (!pcie_downstream_port(pdev))
return;
/* VIA has a strange chipset, root port is under a bridge */
@@ -1070,7 +1069,7 @@ static int __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem)
if (!pci_is_pcie(pdev))
return 0;
- if (pdev->has_secondary_link)
+ if (pcie_downstream_port(pdev))
parent = pdev;
if (!parent || !parent->link_state)
return -EINVAL;
diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c
index 773197a12568..b0e6048a9208 100644
--- a/drivers/pci/pcie/err.c
+++ b/drivers/pci/pcie/err.c
@@ -166,7 +166,7 @@ static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service)
driver = pcie_port_find_service(dev, service);
if (driver && driver->reset_link) {
status = driver->reset_link(dev);
- } else if (dev->has_secondary_link) {
+ } else if (pcie_downstream_port(dev)) {
status = default_reset_link(dev);
} else {
pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n",
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index a3c7338fad86..3bfa57d58402 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1431,26 +1431,38 @@ void set_pcie_port_type(struct pci_dev *pdev)
pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, &reg16);
pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
+ parent = pci_upstream_bridge(pdev);
+ if (!parent)
+ return;
+
/*
- * A Root Port or a PCI-to-PCIe bridge is always the upstream end
- * of a Link. No PCIe component has two Links. Two Links are
- * connected by a Switch that has a Port on each Link and internal
- * logic to connect the two Ports.
+ * Some systems do not identify their upstream/downstream ports
+ * correctly so detect impossible configurations here and correct
+ * the port type accordingly.
*/
type = pci_pcie_type(pdev);
- if (type == PCI_EXP_TYPE_ROOT_PORT ||
- type == PCI_EXP_TYPE_PCIE_BRIDGE)
- pdev->has_secondary_link = 1;
- else if (type == PCI_EXP_TYPE_UPSTREAM ||
- type == PCI_EXP_TYPE_DOWNSTREAM) {
- parent = pci_upstream_bridge(pdev);
-
+ if (type == PCI_EXP_TYPE_DOWNSTREAM) {
/*
- * Usually there's an upstream device (Root Port or Switch
- * Downstream Port), but we can't assume one exists.
+ * If pdev claims to be downstream port but the parent
+ * device is also downstream port assume pdev is actually
+ * upstream port.
*/
- if (parent && !parent->has_secondary_link)
- pdev->has_secondary_link = 1;
+ if (pcie_downstream_port(parent)) {
+ pci_info(pdev, "claims to be downstream port but is acting as upstream port, correcting type\n");
+ pdev->pcie_flags_reg &= ~PCI_EXP_FLAGS_TYPE;
+ pdev->pcie_flags_reg |= PCI_EXP_TYPE_UPSTREAM;
+ }
+ } else if (type == PCI_EXP_TYPE_UPSTREAM) {
+ /*
+ * If pdev claims to be upstream port but the parent
+ * device is also upstream port assume pdev is actually
+ * downstream port.
+ */
+ if (pci_pcie_type(parent) == PCI_EXP_TYPE_UPSTREAM) {
+ pci_info(pdev, "claims to be upstream port but is acting as downstream port, correcting type\n");
+ pdev->pcie_flags_reg &= ~PCI_EXP_FLAGS_TYPE;
+ pdev->pcie_flags_reg |= PCI_EXP_TYPE_DOWNSTREAM;
+ }
}
}
@@ -1920,275 +1932,6 @@ static void pci_configure_mps(struct pci_dev *dev)
p_mps, mps, mpss);
}
-static struct hpp_type0 pci_default_type0 = {
- .revision = 1,
- .cache_line_size = 8,
- .latency_timer = 0x40,
- .enable_serr = 0,
- .enable_perr = 0,
-};
-
-static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp)
-{
- u16 pci_cmd, pci_bctl;
-
- if (!hpp)
- hpp = &pci_default_type0;
-
- if (hpp->revision > 1) {
- pci_warn(dev, "PCI settings rev %d not supported; using defaults\n",
- hpp->revision);
- hpp = &pci_default_type0;
- }
-
- pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpp->cache_line_size);
- pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp->latency_timer);
- pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
- if (hpp->enable_serr)
- pci_cmd |= PCI_COMMAND_SERR;
- if (hpp->enable_perr)
- pci_cmd |= PCI_COMMAND_PARITY;
- pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
-
- /* Program bridge control value */
- if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
- pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
- hpp->latency_timer);
- pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
- if (hpp->enable_perr)
- pci_bctl |= PCI_BRIDGE_CTL_PARITY;
- pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
- }
-}
-
-static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp)
-{
- int pos;
-
- if (!hpp)
- return;
-
- pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
- if (!pos)
- return;
-
- pci_warn(dev, "PCI-X settings not supported\n");
-}
-
-static bool pcie_root_rcb_set(struct pci_dev *dev)
-{
- struct pci_dev *rp = pcie_find_root_port(dev);
- u16 lnkctl;
-
- if (!rp)
- return false;
-
- pcie_capability_read_word(rp, PCI_EXP_LNKCTL, &lnkctl);
- if (lnkctl & PCI_EXP_LNKCTL_RCB)
- return true;
-
- return false;
-}
-
-static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
-{
- int pos;
- u32 reg32;
-
- if (!hpp)
- return;
-
- if (!pci_is_pcie(dev))
- return;
-
- if (hpp->revision > 1) {
- pci_warn(dev, "PCIe settings rev %d not supported\n",
- hpp->revision);
- return;
- }
-
- /*
- * Don't allow _HPX to change MPS or MRRS settings. We manage
- * those to make sure they're consistent with the rest of the
- * platform.
- */
- hpp->pci_exp_devctl_and |= PCI_EXP_DEVCTL_PAYLOAD |
- PCI_EXP_DEVCTL_READRQ;
- hpp->pci_exp_devctl_or &= ~(PCI_EXP_DEVCTL_PAYLOAD |
- PCI_EXP_DEVCTL_READRQ);
-
- /* Initialize Device Control Register */
- pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
- ~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or);
-
- /* Initialize Link Control Register */
- if (pcie_cap_has_lnkctl(dev)) {
-
- /*
- * If the Root Port supports Read Completion Boundary of
- * 128, set RCB to 128. Otherwise, clear it.
- */
- hpp->pci_exp_lnkctl_and |= PCI_EXP_LNKCTL_RCB;
- hpp->pci_exp_lnkctl_or &= ~PCI_EXP_LNKCTL_RCB;
- if (pcie_root_rcb_set(dev))
- hpp->pci_exp_lnkctl_or |= PCI_EXP_LNKCTL_RCB;
-
- pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
- ~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or);
- }
-
- /* Find Advanced Error Reporting Enhanced Capability */
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
- if (!pos)
- return;
-
- /* Initialize Uncorrectable Error Mask Register */
- pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &reg32);
- reg32 = (reg32 & hpp->unc_err_mask_and) | hpp->unc_err_mask_or;
- pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32);
-
- /* Initialize Uncorrectable Error Severity Register */
- pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &reg32);
- reg32 = (reg32 & hpp->unc_err_sever_and) | hpp->unc_err_sever_or;
- pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32);
-
- /* Initialize Correctable Error Mask Register */
- pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg32);
- reg32 = (reg32 & hpp->cor_err_mask_and) | hpp->cor_err_mask_or;
- pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32);
-
- /* Initialize Advanced Error Capabilities and Control Register */
- pci_read_config_dword(dev, pos + PCI_ERR_CAP, &reg32);
- reg32 = (reg32 & hpp->adv_err_cap_and) | hpp->adv_err_cap_or;
-
- /* Don't enable ECRC generation or checking if unsupported */
- if (!(reg32 & PCI_ERR_CAP_ECRC_GENC))
- reg32 &= ~PCI_ERR_CAP_ECRC_GENE;
- if (!(reg32 & PCI_ERR_CAP_ECRC_CHKC))
- reg32 &= ~PCI_ERR_CAP_ECRC_CHKE;
- pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
-
- /*
- * FIXME: The following two registers are not supported yet.
- *
- * o Secondary Uncorrectable Error Severity Register
- * o Secondary Uncorrectable Error Mask Register
- */
-}
-
-static u16 hpx3_device_type(struct pci_dev *dev)
-{
- u16 pcie_type = pci_pcie_type(dev);
- const int pcie_to_hpx3_type[] = {
- [PCI_EXP_TYPE_ENDPOINT] = HPX_TYPE_ENDPOINT,
- [PCI_EXP_TYPE_LEG_END] = HPX_TYPE_LEG_END,
- [PCI_EXP_TYPE_RC_END] = HPX_TYPE_RC_END,
- [PCI_EXP_TYPE_RC_EC] = HPX_TYPE_RC_EC,
- [PCI_EXP_TYPE_ROOT_PORT] = HPX_TYPE_ROOT_PORT,
- [PCI_EXP_TYPE_UPSTREAM] = HPX_TYPE_UPSTREAM,
- [PCI_EXP_TYPE_DOWNSTREAM] = HPX_TYPE_DOWNSTREAM,
- [PCI_EXP_TYPE_PCI_BRIDGE] = HPX_TYPE_PCI_BRIDGE,
- [PCI_EXP_TYPE_PCIE_BRIDGE] = HPX_TYPE_PCIE_BRIDGE,
- };
-
- if (pcie_type >= ARRAY_SIZE(pcie_to_hpx3_type))
- return 0;
-
- return pcie_to_hpx3_type[pcie_type];
-}
-
-static u8 hpx3_function_type(struct pci_dev *dev)
-{
- if (dev->is_virtfn)
- return HPX_FN_SRIOV_VIRT;
- else if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV) > 0)
- return HPX_FN_SRIOV_PHYS;
- else
- return HPX_FN_NORMAL;
-}
-
-static bool hpx3_cap_ver_matches(u8 pcie_cap_id, u8 hpx3_cap_id)
-{
- u8 cap_ver = hpx3_cap_id & 0xf;
-
- if ((hpx3_cap_id & BIT(4)) && cap_ver >= pcie_cap_id)
- return true;
- else if (cap_ver == pcie_cap_id)
- return true;
-
- return false;
-}
-
-static void program_hpx_type3_register(struct pci_dev *dev,
- const struct hpx_type3 *reg)
-{
- u32 match_reg, write_reg, header, orig_value;
- u16 pos;
-
- if (!(hpx3_device_type(dev) & reg->device_type))
- return;
-
- if (!(hpx3_function_type(dev) & reg->function_type))
- return;
-
- switch (reg->config_space_location) {
- case HPX_CFG_PCICFG:
- pos = 0;
- break;
- case HPX_CFG_PCIE_CAP:
- pos = pci_find_capability(dev, reg->pci_exp_cap_id);
- if (pos == 0)
- return;
-
- break;
- case HPX_CFG_PCIE_CAP_EXT:
- pos = pci_find_ext_capability(dev, reg->pci_exp_cap_id);
- if (pos == 0)
- return;
-
- pci_read_config_dword(dev, pos, &header);
- if (!hpx3_cap_ver_matches(PCI_EXT_CAP_VER(header),
- reg->pci_exp_cap_ver))
- return;
-
- break;
- case HPX_CFG_VEND_CAP: /* Fall through */
- case HPX_CFG_DVSEC: /* Fall through */
- default:
- pci_warn(dev, "Encountered _HPX type 3 with unsupported config space location");
- return;
- }
-
- pci_read_config_dword(dev, pos + reg->match_offset, &match_reg);
-
- if ((match_reg & reg->match_mask_and) != reg->match_value)
- return;
-
- pci_read_config_dword(dev, pos + reg->reg_offset, &write_reg);
- orig_value = write_reg;
- write_reg &= reg->reg_mask_and;
- write_reg |= reg->reg_mask_or;
-
- if (orig_value == write_reg)
- return;
-
- pci_write_config_dword(dev, pos + reg->reg_offset, write_reg);
-
- pci_dbg(dev, "Applied _HPX3 at [0x%x]: 0x%08x -> 0x%08x",
- pos, orig_value, write_reg);
-}
-
-static void program_hpx_type3(struct pci_dev *dev, struct hpx_type3 *hpx3)
-{
- if (!hpx3)
- return;
-
- if (!pci_is_pcie(dev))
- return;
-
- program_hpx_type3_register(dev, hpx3);
-}
-
int pci_configure_extended_tags(struct pci_dev *dev, void *ign)
{
struct pci_host_bridge *host;
@@ -2369,13 +2112,6 @@ static void pci_configure_serr(struct pci_dev *dev)
static void pci_configure_device(struct pci_dev *dev)
{
- static const struct hotplug_program_ops hp_ops = {
- .program_type0 = program_hpp_type0,
- .program_type1 = program_hpp_type1,
- .program_type2 = program_hpp_type2,
- .program_type3 = program_hpx_type3,
- };
-
pci_configure_mps(dev);
pci_configure_extended_tags(dev, NULL);
pci_configure_relaxed_ordering(dev);
@@ -2383,7 +2119,7 @@ static void pci_configure_device(struct pci_dev *dev)
pci_configure_eetlp_prefix(dev);
pci_configure_serr(dev);
- pci_acpi_program_hp_params(dev, &hp_ops);
+ pci_acpi_program_hp_params(dev);
}
static void pci_release_capabilities(struct pci_dev *dev)
@@ -2764,12 +2500,8 @@ static int only_one_child(struct pci_bus *bus)
* A PCIe Downstream Port normally leads to a Link with only Device
* 0 on it (PCIe spec r3.1, sec 7.3.1). As an optimization, scan
* only for Device 0 in that situation.
- *
- * Checking has_secondary_link is a hack to identify Downstream
- * Ports because sometimes Switches are configured such that the
- * PCIe Port Type labels are backwards.
*/
- if (bridge && pci_is_pcie(bridge) && bridge->has_secondary_link)
+ if (bridge && pci_is_pcie(bridge) && pcie_downstream_port(bridge))
return 1;
return 0;
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 44524d351a26..87f591caccd9 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -20,7 +20,6 @@
#include <linux/delay.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
-#include <linux/pci-aspm.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/ktime.h>
@@ -4502,6 +4501,19 @@ static int pci_quirk_mf_endpoint_acs(struct pci_dev *dev, u16 acs_flags)
return acs_flags ? 0 : 1;
}
+static int pci_quirk_brcm_acs(struct pci_dev *dev, u16 acs_flags)
+{
+ /*
+ * iProc PAXB Root Ports don't advertise an ACS capability, but
+ * they do not allow peer-to-peer transactions between Root Ports.
+ * Allow each Root Port to be in a separate IOMMU group by masking
+ * SV/RR/CR/UF bits.
+ */
+ acs_flags &= ~(PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF);
+
+ return acs_flags ? 0 : 1;
+}
+
static const struct pci_dev_acs_enabled {
u16 vendor;
u16 device;
@@ -4595,6 +4607,7 @@ static const struct pci_dev_acs_enabled {
{ 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 },
+ { PCI_VENDOR_ID_BROADCOM, 0xD714, pci_quirk_brcm_acs },
/* Amazon Annapurna Labs */
{ PCI_VENDOR_ID_AMAZON_ANNAPURNA_LABS, 0x0031, pci_quirk_al_acs },
{ 0 }
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index 7f4e65872b8d..bade14002fd8 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -15,7 +15,6 @@
#include "pci.h"
DECLARE_RWSEM(pci_bus_sem);
-EXPORT_SYMBOL_GPL(pci_bus_sem);
/*
* pci_for_each_dma_alias - Iterate over DMA aliases for a device
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 79b1fa6519be..e7dbe21705ba 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -1662,8 +1662,8 @@ static int iov_resources_unassigned(struct pci_dev *dev, void *data)
int i;
bool *unassigned = data;
- for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) {
- struct resource *r = &dev->resource[i];
+ for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
+ struct resource *r = &dev->resource[i + PCI_IOV_RESOURCES];
struct pci_bus_region region;
/* Not assigned or rejected by kernel? */
diff --git a/drivers/pci/vc.c b/drivers/pci/vc.c
index 5acd9c02683a..9ae9fb9339e8 100644
--- a/drivers/pci/vc.c
+++ b/drivers/pci/vc.c
@@ -13,6 +13,8 @@
#include <linux/pci_regs.h>
#include <linux/types.h>
+#include "pci.h"
+
/**
* pci_vc_save_restore_dwords - Save or restore a series of dwords
* @dev: device
@@ -105,7 +107,7 @@ static void pci_vc_enable(struct pci_dev *dev, int pos, int res)
struct pci_dev *link = NULL;
/* Enable VCs from the downstream device */
- if (!dev->has_secondary_link)
+ if (!pci_is_pcie(dev) || !pcie_downstream_port(dev))
return;
ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF);