From 9bb04a0c4e261187be904d05c2bcd1da0eebc20c Mon Sep 17 00:00:00 2001 From: Jonathan Yong Date: Sat, 11 Jun 2016 14:13:38 -0500 Subject: PCI: Add Precision Time Measurement (PTM) support Add Precision Time Measurement (PTM) support (see PCIe r3.1, sec 6.22). Enable PTM on PTM Root devices and switch ports. This does not enable PTM on endpoints. There currently are no PTM-capable devices on the market, but it is expected to be supported by the Intel Apollo Lake platform. [bhelgaas: complete rework] Signed-off-by: Jonathan Yong Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux/pci.h') diff --git a/include/linux/pci.h b/include/linux/pci.h index 2599a980340f..96c509fa9d46 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -367,6 +367,11 @@ struct pci_dev { int rom_attr_enabled; /* has display of the rom attribute been enabled? */ struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */ struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ + +#ifdef CONFIG_PCIE_PTM + unsigned int ptm_root:1; + unsigned int ptm_enabled:1; +#endif #ifdef CONFIG_PCI_MSI const struct attribute_group **msi_irq_groups; #endif -- cgit v1.2.3 From eec097d43100a8195fd4f678671ecd5d986dd675 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 13 Jun 2016 11:01:51 -0500 Subject: PCI: Add pci_enable_ptm() for drivers to enable PTM on endpoints Add an pci_enable_ptm() interface so drivers can enable PTM. The PCI core enables PTM on PTM Roots and switches automatically, but we don't enable PTM on endpoints unless a driver requests it. Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/ptm.c | 45 +++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 7 +++++++ include/uapi/linux/pci_regs.h | 1 + 3 files changed, 53 insertions(+) (limited to 'include/linux/pci.h') diff --git a/drivers/pci/pcie/ptm.c b/drivers/pci/pcie/ptm.c index 48eea4e65247..a14ac94b96dc 100644 --- a/drivers/pci/pcie/ptm.c +++ b/drivers/pci/pcie/ptm.c @@ -68,3 +68,48 @@ void pci_ptm_init(struct pci_dev *dev) pci_ptm_info(dev); } + +int pci_enable_ptm(struct pci_dev *dev, u8 *granularity) +{ + int pos; + u32 cap, ctrl; + struct pci_dev *ups; + + if (!pci_is_pcie(dev)) + return -EINVAL; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM); + if (!pos) + return -EINVAL; + + pci_read_config_dword(dev, pos + PCI_PTM_CAP, &cap); + if (!(cap & PCI_PTM_CAP_REQ)) + return -EINVAL; + + /* + * For a PCIe Endpoint, PTM is only useful if the endpoint can + * issue PTM requests to upstream devices that have PTM enabled. + * + * For Root Complex Integrated Endpoints, there is no upstream + * device, so there must be some implementation-specific way to + * associate the endpoint with a time source. + */ + if (pci_pcie_type(dev) == PCI_EXP_TYPE_ENDPOINT) { + ups = pci_upstream_bridge(dev); + if (!ups || !ups->ptm_enabled) + return -EINVAL; + } else if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) { + } else + return -EINVAL; + + ctrl = PCI_PTM_CTRL_ENABLE; + pci_write_config_dword(dev, pos + PCI_PTM_CTRL, ctrl); + dev->ptm_enabled = 1; + + pci_ptm_info(dev); + + if (granularity) + *granularity = 0; + return 0; +} +EXPORT_SYMBOL(pci_enable_ptm); diff --git a/include/linux/pci.h b/include/linux/pci.h index 96c509fa9d46..9e4b6d6f3c8d 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1407,6 +1407,13 @@ static inline void pci_disable_ats(struct pci_dev *d) { } static inline int pci_ats_queue_depth(struct pci_dev *d) { return -ENODEV; } #endif +#ifdef CONFIG_PCIE_PTM +int pci_enable_ptm(struct pci_dev *dev, u8 *granularity); +#else +static inline int pci_enable_ptm(struct pci_dev *dev, u8 *granularity) +{ return -EINVAL; } +#endif + void pci_cfg_access_lock(struct pci_dev *dev); bool pci_cfg_access_trylock(struct pci_dev *dev); void pci_cfg_access_unlock(struct pci_dev *dev); diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 926fff41b417..72bbe1491cbf 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -967,6 +967,7 @@ /* Precision Time Measurement */ #define PCI_PTM_CAP 0x04 /* PTM Capability */ +#define PCI_PTM_CAP_REQ 0x00000001 /* Requester capable */ #define PCI_PTM_CAP_ROOT 0x00000004 /* Root capable */ #define PCI_PTM_CTRL 0x08 /* PTM Control */ #define PCI_PTM_CTRL_ENABLE 0x00000001 /* PTM enable */ -- cgit v1.2.3 From 8b2ec318eece89be5e33d5313a25461a55a3177a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sun, 12 Jun 2016 16:26:40 -0500 Subject: PCI: Add PTM clock granularity information The PTM Control register (PCIe r3.1, sec 7.32.3) contains an Effective Granularity field: This provides information relating to the expected accuracy of the PTM clock, but does not otherwise affect the PTM mechanism. Set the Effective Granularity based on the PTM Root and any intervening PTM Time Sources. This does not set Effective Granularity for Root Complex Integrated Endpoints because I don't know how to figure out clock granularity for them. The spec says: ... system software must set [Effective Granularity] to the value reported in the Local Clock Granularity field by the associated PTM Time Source. but I don't know how to identify the associated PTM Time Source. Normally it's the upstream bridge, but an integrated endpoint has no upstream bridge. Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/ptm.c | 31 +++++++++++++++++++++++++++++-- include/linux/pci.h | 1 + include/uapi/linux/pci_regs.h | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) (limited to 'include/linux/pci.h') diff --git a/drivers/pci/pcie/ptm.c b/drivers/pci/pcie/ptm.c index a14ac94b96dc..bab8ac63c4f3 100644 --- a/drivers/pci/pcie/ptm.c +++ b/drivers/pci/pcie/ptm.c @@ -19,13 +19,29 @@ static void pci_ptm_info(struct pci_dev *dev) { - dev_info(&dev->dev, "PTM enabled%s\n", dev->ptm_root ? " (root)" : ""); + char clock_desc[8]; + + switch (dev->ptm_granularity) { + case 0: + snprintf(clock_desc, sizeof(clock_desc), "unknown"); + break; + case 255: + snprintf(clock_desc, sizeof(clock_desc), ">254ns"); + break; + default: + snprintf(clock_desc, sizeof(clock_desc), "%udns", + dev->ptm_granularity); + break; + } + dev_info(&dev->dev, "PTM enabled%s, %s granularity\n", + dev->ptm_root ? " (root)" : "", clock_desc); } void pci_ptm_init(struct pci_dev *dev) { int pos; u32 cap, ctrl; + u8 local_clock; struct pci_dev *ups; if (!pci_is_pcie(dev)) @@ -45,6 +61,7 @@ void pci_ptm_init(struct pci_dev *dev) return; pci_read_config_dword(dev, pos + PCI_PTM_CAP, &cap); + local_clock = (cap & PCI_PTM_GRANULARITY_MASK) >> 8; /* * There's no point in enabling PTM unless it's enabled in the @@ -55,14 +72,20 @@ void pci_ptm_init(struct pci_dev *dev) ups = pci_upstream_bridge(dev); if (ups && ups->ptm_enabled) { ctrl = PCI_PTM_CTRL_ENABLE; + if (ups->ptm_granularity == 0) + dev->ptm_granularity = 0; + else if (ups->ptm_granularity > local_clock) + dev->ptm_granularity = ups->ptm_granularity; } else { if (cap & PCI_PTM_CAP_ROOT) { ctrl = PCI_PTM_CTRL_ENABLE | PCI_PTM_CTRL_ROOT; dev->ptm_root = 1; + dev->ptm_granularity = local_clock; } else return; } + ctrl |= dev->ptm_granularity << 8; pci_write_config_dword(dev, pos + PCI_PTM_CTRL, ctrl); dev->ptm_enabled = 1; @@ -98,18 +121,22 @@ int pci_enable_ptm(struct pci_dev *dev, u8 *granularity) ups = pci_upstream_bridge(dev); if (!ups || !ups->ptm_enabled) return -EINVAL; + + dev->ptm_granularity = ups->ptm_granularity; } else if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) { + dev->ptm_granularity = 0; } else return -EINVAL; ctrl = PCI_PTM_CTRL_ENABLE; + ctrl |= dev->ptm_granularity << 8; pci_write_config_dword(dev, pos + PCI_PTM_CTRL, ctrl); dev->ptm_enabled = 1; pci_ptm_info(dev); if (granularity) - *granularity = 0; + *granularity = dev->ptm_granularity; return 0; } EXPORT_SYMBOL(pci_enable_ptm); diff --git a/include/linux/pci.h b/include/linux/pci.h index 9e4b6d6f3c8d..7256f33b6a15 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -371,6 +371,7 @@ struct pci_dev { #ifdef CONFIG_PCIE_PTM unsigned int ptm_root:1; unsigned int ptm_enabled:1; + u8 ptm_granularity; #endif #ifdef CONFIG_PCI_MSI const struct attribute_group **msi_irq_groups; diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 72bbe1491cbf..d812172d1d7b 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -969,6 +969,7 @@ #define PCI_PTM_CAP 0x04 /* PTM Capability */ #define PCI_PTM_CAP_REQ 0x00000001 /* Requester capable */ #define PCI_PTM_CAP_ROOT 0x00000004 /* Root capable */ +#define PCI_PTM_GRANULARITY_MASK 0x0000FF00 /* Clock granularity */ #define PCI_PTM_CTRL 0x08 /* PTM Control */ #define PCI_PTM_CTRL_ENABLE 0x00000001 /* PTM enable */ #define PCI_PTM_CTRL_ROOT 0x00000002 /* Root select */ -- cgit v1.2.3 From 032c3d86b4acc4c21e435c85c454eac670c15851 Mon Sep 17 00:00:00 2001 From: Jon Derrick Date: Thu, 25 Aug 2016 17:26:10 -0600 Subject: PCI/AER: Add bus flag to skip source ID matching Allow root port buses to choose to skip source id matching when finding the faulting device. Certain root port devices may return an incorrect source ID and recommend to scan child device registers for AER notifications. Signed-off-by: Jon Derrick Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer/aerdrv_core.c | 7 +++++-- include/linux/pci.h | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include/linux/pci.h') diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 521e39c1b66d..8f5e14cac600 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -132,7 +132,9 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) * When bus id is equal to 0, it might be a bad id * reported by root port. */ - if (!nosourceid && (PCI_BUS_NUM(e_info->id) != 0)) { + if (!nosourceid && + (PCI_BUS_NUM(e_info->id) != 0) && + !(dev->bus->bus_flags & PCI_BUS_FLAGS_NO_AERSID)) { /* Device ID match? */ if (e_info->id == ((dev->bus->number << 8) | dev->devfn)) return true; @@ -147,7 +149,8 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) * 1) nosourceid==y; * 2) bus id is equal to 0. Some ports might lose the bus * id of error source id; - * 3) There are multiple errors and prior id comparing fails; + * 3) bus flag PCI_BUS_FLAGS_NO_AERSID is set + * 4) There are multiple errors and prior ID comparing fails; * We check AER status registers to find possible reporter. */ if (atomic_read(&dev->enable_cnt) == 0) diff --git a/include/linux/pci.h b/include/linux/pci.h index 2599a980340f..57bc838e0666 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -187,8 +187,9 @@ enum pci_irq_reroute_variant { typedef unsigned short __bitwise pci_bus_flags_t; enum pci_bus_flags { - PCI_BUS_FLAGS_NO_MSI = (__force pci_bus_flags_t) 1, - PCI_BUS_FLAGS_NO_MMRBC = (__force pci_bus_flags_t) 2, + PCI_BUS_FLAGS_NO_MSI = (__force pci_bus_flags_t) 1, + PCI_BUS_FLAGS_NO_MMRBC = (__force pci_bus_flags_t) 2, + PCI_BUS_FLAGS_NO_AERSID = (__force pci_bus_flags_t) 4, }; /* These values come from the PCI Express Spec */ -- cgit v1.2.3 From 576243b3f9eaa47ab568ac49574b3a095c2365f1 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 13 Sep 2016 10:31:59 -0600 Subject: PCI: pciehp: Allow exclusive userspace control of indicators PCIe hotplug supports optional Attention and Power Indicators, which are used internally by pciehp. Users can't control the Power Indicator, but they can control the Attention Indicator by writing to a sysfs "attention" file. The Slot Control register has two bits for each indicator, and the PCIe spec defines the encodings for each as (Reserved/On/Blinking/Off). For sysfs "attention" writes, pciehp_set_attention_status() maps into these encodings, so the only useful write values are 0 (Off), 1 (On), and 2 (Blinking). However, some platforms use all four bits for platform-specific indicators, and they need to allow direct user control of them while preventing pciehp from using them at all. Add a "hotplug_user_indicators" flag to the pci_dev structure. When set, pciehp does not use either the Attention Indicator or the Power Indicator, and the low four bits (values 0x0 - 0xf) of sysfs "attention" write values are written directly to the Attention Indicator Control and Power Indicator Control fields. [bhelgaas: changelog, rename flag and accessors to s/attention/indicator/] Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp.h | 3 +++ drivers/pci/hotplug/pciehp_core.c | 3 +++ drivers/pci/hotplug/pciehp_hpc.c | 27 +++++++++++++++++++++++++++ include/linux/pci.h | 3 +++ 4 files changed, 36 insertions(+) (limited to 'include/linux/pci.h') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index e764918641ae..37d70b5ad22f 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -152,6 +152,9 @@ bool pciehp_check_link_active(struct controller *ctrl); void pciehp_release_ctrl(struct controller *ctrl); int pciehp_reset_slot(struct slot *slot, int probe); +int pciehp_set_raw_indicator_status(struct hotplug_slot *h_slot, u8 status); +int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status); + static inline const char *slot_name(struct slot *slot) { return hotplug_slot_name(slot->hotplug_slot); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index ac531e674a05..276e39c64d06 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -114,6 +114,9 @@ static int init_slot(struct controller *ctrl) if (ATTN_LED(ctrl)) { ops->get_attention_status = get_attention_status; ops->set_attention_status = set_attention_status; + } else if (ctrl->pcie->port->hotplug_user_indicators) { + ops->get_attention_status = pciehp_get_raw_indicator_status; + ops->set_attention_status = pciehp_set_raw_indicator_status; } /* register this slot with the hotplug pci core */ diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 4582fdf2d8b5..b57fc6d6e28a 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -355,6 +355,18 @@ static int pciehp_link_enable(struct controller *ctrl) return __pciehp_link_set(ctrl, true); } +int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot, + u8 *status) +{ + struct slot *slot = hotplug_slot->private; + struct pci_dev *pdev = ctrl_dev(slot->ctrl); + u16 slot_ctrl; + + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + *status = (slot_ctrl & (PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC)) >> 6; + return 0; +} + void pciehp_get_attention_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; @@ -431,6 +443,17 @@ int pciehp_query_power_fault(struct slot *slot) return !!(slot_status & PCI_EXP_SLTSTA_PFD); } +int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot, + u8 status) +{ + struct slot *slot = hotplug_slot->private; + struct controller *ctrl = slot->ctrl; + + pcie_write_cmd_nowait(ctrl, status << 6, + PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC); + return 0; +} + void pciehp_set_attention_status(struct slot *slot, u8 value) { struct controller *ctrl = slot->ctrl; @@ -814,6 +837,10 @@ struct controller *pcie_init(struct pcie_device *dev) } ctrl->pcie = dev; pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap); + + if (pdev->hotplug_user_indicators) + slot_cap &= ~(PCI_EXP_SLTCAP_AIP | PCI_EXP_SLTCAP_PIP); + ctrl->slot_cap = slot_cap; mutex_init(&ctrl->ctrl_lock); init_waitqueue_head(&ctrl->queue); diff --git a/include/linux/pci.h b/include/linux/pci.h index 2599a980340f..c81fbf7d5e9e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -308,6 +308,9 @@ struct pci_dev { powered on/off by the corresponding bridge */ unsigned int ignore_hotplug:1; /* Ignore hotplug events */ + unsigned int hotplug_user_indicators:1; /* SlotCtl indicators + controlled exclusively by + user sysfs */ unsigned int d3_delay; /* D3->D0 transition time in ms */ unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */ -- cgit v1.2.3 From 66b808099146166c44157600a166c8372172cd76 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 27 Sep 2016 16:23:34 -0400 Subject: PCI/AER: Cache capability position Save the position of the error reporting capability so it doesn't need to be rediscovered during error handling. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas CC: Lukas Wunner --- drivers/pci/pcie/aer/aerdrv.c | 10 +++++----- drivers/pci/pcie/aer/aerdrv_core.c | 18 ++++++++++++------ drivers/pci/probe.c | 3 ++- include/linux/pci.h | 5 +++++ 4 files changed, 24 insertions(+), 12 deletions(-) (limited to 'include/linux/pci.h') diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 08ce257077db..e99efaa723d5 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -134,7 +134,7 @@ static void aer_enable_rootport(struct aer_rpc *rpc) pcie_capability_clear_word(pdev, PCI_EXP_RTCTL, SYSTEM_ERROR_INTR_ON_MESG_MASK); - aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); + aer_pos = pdev->aer_cap; /* Clear error status */ pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32); pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32); @@ -173,7 +173,7 @@ static void aer_disable_rootport(struct aer_rpc *rpc) */ set_downstream_devices_error_reporting(pdev, false); - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); + pos = pdev->aer_cap; /* Disable Root's interrupt in response to error messages */ pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, ®32); reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; @@ -200,7 +200,7 @@ irqreturn_t aer_irq(int irq, void *context) unsigned long flags; int pos; - pos = pci_find_ext_capability(pdev->port, PCI_EXT_CAP_ID_ERR); + pos = pdev->port->aer_cap; /* * Must lock access to Root Error Status Reg, Root Error ID Reg, * and Root error producer/consumer index @@ -338,7 +338,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev) u32 reg32; int pos; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; /* Disable Root's interrupt in response to error messages */ pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ®32); @@ -391,7 +391,7 @@ static void aer_error_resume(struct pci_dev *dev) pcie_capability_write_word(dev, PCI_EXP_DEVSTA, reg16); /* Clean AER Root Error Status */ - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask); if (dev->error_state == pci_channel_io_normal) diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 9fd18a08b23e..b1303b32053f 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -35,7 +35,7 @@ int pci_enable_pcie_error_reporting(struct pci_dev *dev) if (pcie_aer_get_firmware_first(dev)) return -EIO; - if (!pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) + if (!dev->aer_cap) return -EIO; return pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS); @@ -57,7 +57,7 @@ int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) int pos; u32 status; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; if (!pos) return -EIO; @@ -78,7 +78,7 @@ int pci_cleanup_aer_error_status_regs(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 -EIO; @@ -97,6 +97,12 @@ int pci_cleanup_aer_error_status_regs(struct pci_dev *dev) return 0; } +int pci_aer_init(struct pci_dev *dev) +{ + dev->aer_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + return pci_cleanup_aer_error_status_regs(dev); +} + /** * add_error_device - list device to be handled * @e_info: pointer to error info @@ -154,7 +160,7 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) if (!(reg16 & PCI_EXP_AER_FLAGS)) return false; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; if (!pos) return false; @@ -551,7 +557,7 @@ static void handle_error_source(struct pcie_device *aerdev, * Correctable error does not need software intervention. * No need to go through error recovery process. */ - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; if (pos) pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, info->status); @@ -643,7 +649,7 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) info->status = 0; info->tlp_header_valid = 0; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; /* The device might not support AER */ if (!pos) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 93f280df3428..157572420513 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1666,7 +1666,8 @@ static void pci_init_capabilities(struct pci_dev *dev) /* Enable ACS P2P upstream forwarding */ pci_enable_acs(dev); - pci_cleanup_aer_error_status_regs(dev); + /* Advanced Error Reporting */ + pci_aer_init(dev); } /* diff --git a/include/linux/pci.h b/include/linux/pci.h index 57bc838e0666..ab6b02763916 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -269,6 +269,9 @@ struct pci_dev { unsigned int class; /* 3 bytes: (base,sub,prog-if) */ u8 revision; /* PCI revision, low byte of class word */ u8 hdr_type; /* PCI header type (`multi' flag masked out) */ +#ifdef CONFIG_PCIEAER + u16 aer_cap; /* AER capability offset */ +#endif u8 pcie_cap; /* PCIe capability offset */ u8 msi_cap; /* MSI capability offset */ u8 msix_cap; /* MSI-X capability offset */ @@ -1369,9 +1372,11 @@ static inline bool pcie_aspm_support_enabled(void) { return false; } #ifdef CONFIG_PCIEAER void pci_no_aer(void); bool pci_aer_available(void); +int pci_aer_init(struct pci_dev *dev); #else static inline void pci_no_aer(void) { } static inline bool pci_aer_available(void) { return false; } +static inline int pci_aer_init(struct pci_dev *d) { return -ENODEV; } #endif #ifdef CONFIG_PCIE_ECRC -- cgit v1.2.3