From 1736353f17e10bee352ec690757c66b568140724 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 19 Dec 2017 22:38:29 -0200 Subject: PCI: xgene: Fix the xgene_msi_probe() return code If devm_ioremap_resource() detects an error condition in the return value through IS_ERR(), the return value should be retrieved through PTR_ERR() instead of hardcoding it. Fix the xgene_msi_probe() error return code. Signed-off-by: Fabio Estevam [lorenzo.pieralisi@arm.com: rewrote commit log] Signed-off-by: Lorenzo Pieralisi --- drivers/pci/host/pci-xgene-msi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c index df8e4bd5ddb2..f4c02da84e59 100644 --- a/drivers/pci/host/pci-xgene-msi.c +++ b/drivers/pci/host/pci-xgene-msi.c @@ -456,7 +456,7 @@ static int xgene_msi_probe(struct platform_device *pdev) xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(xgene_msi->msi_regs)) { dev_err(&pdev->dev, "no reg space\n"); - rc = -EINVAL; + rc = PTR_ERR(xgene_msi->msi_regs); goto error; } xgene_msi->msi_addr = res->start; -- cgit v1.2.3 From b4a304489ef95b6b921d1faa46438d9ea89cfd4b Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 20 Apr 2017 22:04:48 -0700 Subject: sparc/PCI: Support arbitrary host bridge address offset Add support for arbitrary bus address offset. Previously we ignored the child (PCI) address in the "ranges" property and assumed it was always zero. That means every host bridge window mapped to PCI bus address zero, e.g., pci_bus 0000:00: root bus resource [mem 0x2000000000000-0x200007fffffff] (bus address [0x00000000-0x7fffffff]) But some systems have host bridge windows with non-zero child addresses, so parse the child address and compute the offset between the parent (CPU) and child (PCI) addresses. This allows windows like these: /pci@305: PCI MEM [mem 0x2000000100000-0x200007effffff] offset 2000000000000 pci_sun4v f02ae7f8: PCI host bridge to bus 0000:00 pci_bus 0000:00: root bus resource [mem 0x2000000100000-0x200007effffff] (bus address [0x00100000-0x7effffff]) [bhelgaas: changelog] Tested-by: Khalid Aziz Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas Acked-by: David S. Miller Cc: sparclinux@vger.kernel.org --- arch/sparc/kernel/pci.c | 6 +++--- arch/sparc/kernel/pci_common.c | 34 ++++++++++++++++++++++++++-------- arch/sparc/kernel/pci_impl.h | 4 ++++ 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index 220d0f36560a..41b20edb427d 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -664,12 +664,12 @@ struct pci_bus *pci_scan_one_pbm(struct pci_pbm_info *pbm, printk("PCI: Scanning PBM %s\n", node->full_name); pci_add_resource_offset(&resources, &pbm->io_space, - pbm->io_space.start); + pbm->io_offset); pci_add_resource_offset(&resources, &pbm->mem_space, - pbm->mem_space.start); + pbm->mem_offset); if (pbm->mem64_space.flags) pci_add_resource_offset(&resources, &pbm->mem64_space, - pbm->mem_space.start); + pbm->mem64_offset); pbm->busn.start = pbm->pci_first_busno; pbm->busn.end = pbm->pci_last_busno; pbm->busn.flags = IORESOURCE_BUS; diff --git a/arch/sparc/kernel/pci_common.c b/arch/sparc/kernel/pci_common.c index 1e10fb26fa88..14fa74f90fdb 100644 --- a/arch/sparc/kernel/pci_common.c +++ b/arch/sparc/kernel/pci_common.c @@ -397,6 +397,8 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) int i, saw_mem, saw_io; int num_pbm_ranges; + /* Corresponding generic code in of_pci_get_host_bridge_resources() */ + saw_mem = saw_io = 0; pbm_ranges = of_get_property(pbm->op->dev.of_node, "ranges", &i); if (!pbm_ranges) { @@ -411,13 +413,16 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) for (i = 0; i < num_pbm_ranges; i++) { const struct linux_prom_pci_ranges *pr = &pbm_ranges[i]; - unsigned long a, size; + unsigned long a, size, region_a; u32 parent_phys_hi, parent_phys_lo; + u32 child_phys_mid, child_phys_lo; u32 size_hi, size_lo; int type; parent_phys_hi = pr->parent_phys_hi; parent_phys_lo = pr->parent_phys_lo; + child_phys_mid = pr->child_phys_mid; + child_phys_lo = pr->child_phys_lo; if (tlb_type == hypervisor) parent_phys_hi &= 0x0fffffff; @@ -427,6 +432,8 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) type = (pr->child_phys_hi >> 24) & 0x3; a = (((unsigned long)parent_phys_hi << 32UL) | ((unsigned long)parent_phys_lo << 0UL)); + region_a = (((unsigned long)child_phys_mid << 32UL) | + ((unsigned long)child_phys_lo << 0UL)); size = (((unsigned long)size_hi << 32UL) | ((unsigned long)size_lo << 0UL)); @@ -441,6 +448,7 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) pbm->io_space.start = a; pbm->io_space.end = a + size - 1UL; pbm->io_space.flags = IORESOURCE_IO; + pbm->io_offset = a - region_a; saw_io = 1; break; @@ -449,6 +457,7 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) pbm->mem_space.start = a; pbm->mem_space.end = a + size - 1UL; pbm->mem_space.flags = IORESOURCE_MEM; + pbm->mem_offset = a - region_a; saw_mem = 1; break; @@ -457,6 +466,7 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) pbm->mem64_space.start = a; pbm->mem64_space.end = a + size - 1UL; pbm->mem64_space.flags = IORESOURCE_MEM; + pbm->mem64_offset = a - region_a; saw_mem = 1; break; @@ -472,14 +482,22 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) prom_halt(); } - printk("%s: PCI IO[%llx] MEM[%llx]", - pbm->name, - pbm->io_space.start, - pbm->mem_space.start); + if (pbm->io_space.flags) + printk("%s: PCI IO %pR offset %llx\n", + pbm->name, &pbm->io_space, pbm->io_offset); + if (pbm->mem_space.flags) + printk("%s: PCI MEM %pR offset %llx\n", + pbm->name, &pbm->mem_space, pbm->mem_offset); + if (pbm->mem64_space.flags && pbm->mem_space.flags) { + if (pbm->mem64_space.start <= pbm->mem_space.end) + pbm->mem64_space.start = pbm->mem_space.end + 1; + if (pbm->mem64_space.start > pbm->mem64_space.end) + pbm->mem64_space.flags = 0; + } + if (pbm->mem64_space.flags) - printk(" MEM64[%llx]", - pbm->mem64_space.start); - printk("\n"); + printk("%s: PCI MEM64 %pR offset %llx\n", + pbm->name, &pbm->mem64_space, pbm->mem64_offset); pbm->io_space.name = pbm->mem_space.name = pbm->name; pbm->mem64_space.name = pbm->name; diff --git a/arch/sparc/kernel/pci_impl.h b/arch/sparc/kernel/pci_impl.h index ac172961d276..4e3d15189fa9 100644 --- a/arch/sparc/kernel/pci_impl.h +++ b/arch/sparc/kernel/pci_impl.h @@ -100,6 +100,10 @@ struct pci_pbm_info { struct resource mem_space; struct resource mem64_space; struct resource busn; + /* offset */ + resource_size_t io_offset; + resource_size_t mem_offset; + resource_size_t mem64_offset; /* Base of PCI Config space, can be per-PBM or shared. */ unsigned long config_space; -- cgit v1.2.3 From 7babfa5b4bf0a9574c9b30f657b1d87abc5a84cf Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 15 Dec 2017 16:15:12 -0600 Subject: rapidio/tsi721: use PCI_EXP_DEVCTL2_COMP_TIMEOUT macro Use the existing PCI_EXP_DEVCTL2_COMP_TIMEOUT macro instead of hard-coding the PCIe Completion Timeout Value mask. No functional change intended. Signed-off-by: Bjorn Helgaas Acked-by: Alexandre Bounine --- drivers/rapidio/devices/tsi721.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c index 9a68914100ad..bb655854713d 100644 --- a/drivers/rapidio/devices/tsi721.c +++ b/drivers/rapidio/devices/tsi721.c @@ -2880,8 +2880,9 @@ static int tsi721_probe(struct pci_dev *pdev, "Invalid MRRS override value %d", pcie_mrrs); } - /* Adjust PCIe completion timeout. */ - pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL2, 0xf, 0x2); + /* Set PCIe completion timeout to 1-10ms */ + pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_COMP_TIMEOUT, 0x2); /* * FIXUP: correct offsets of MSI-X tables in the MSI-X Capability Block -- cgit v1.2.3 From 57b29646bb4804732d654d85ca574ced263d7bc2 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Mon, 27 Nov 2017 11:57:46 -0500 Subject: drm/i915: Deprecate pci_get_bus_and_slot() pci_get_bus_and_slot() is restrictive such that it assumes domain=0 as where a PCI device is present. This restricts the device drivers to be reused for other domain numbers. Getting ready to remove pci_get_bus_and_slot() function in favor of pci_get_domain_bus_and_slot(). Extract the domain number from drm_device and pass it into pci_get_domain_bus_and_slot() function. Signed-off-by: Sinan Kaya Signed-off-by: Bjorn Helgaas --- drivers/gpu/drm/i915/i915_drv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 173d0095e3b2..ca17508fd28c 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -434,7 +434,10 @@ static int i915_getparam(struct drm_device *dev, void *data, static int i915_get_bridge_dev(struct drm_i915_private *dev_priv) { - dev_priv->bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0)); + int domain = pci_domain_nr(dev_priv->drm.pdev->bus); + + dev_priv->bridge_dev = + pci_get_domain_bus_and_slot(domain, 0, PCI_DEVFN(0, 0)); if (!dev_priv->bridge_dev) { DRM_ERROR("bridge device not found\n"); return -1; -- cgit v1.2.3 From 5cf0c37a71da0f3a4802806c597b21d99c33ca60 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 19 Dec 2017 00:38:02 -0500 Subject: PCI: Remove pci_get_bus_and_slot() function pci_get_bus_and_slot() is restrictive such that it assumes domain=0 as where a PCI device is present. This restricts the device drivers to be reused for other domain numbers. Now that all users of pci_get_bus_and_slot() switched to pci_get_domain_bus_and_slot(), it is now safe to remove this function. Signed-off-by: Sinan Kaya Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/linux/pci.h b/include/linux/pci.h index 024a1beda008..25b7a3535d26 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -949,11 +949,6 @@ struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device, struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn); struct pci_dev *pci_get_domain_bus_and_slot(int domain, unsigned int bus, unsigned int devfn); -static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus, - unsigned int devfn) -{ - return pci_get_domain_bus_and_slot(0, bus, devfn); -} struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from); int pci_dev_present(const struct pci_device_id *ids); @@ -1661,9 +1656,6 @@ static inline struct pci_bus *pci_find_next_bus(const struct pci_bus *from) static inline struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn) { return NULL; } -static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus, - unsigned int devfn) -{ return NULL; } static inline struct pci_dev *pci_get_domain_bus_and_slot(int domain, unsigned int bus, unsigned int devfn) { return NULL; } -- cgit v1.2.3 From 5b0764cac9f1b70a6704b0e546be67c24cc05517 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 16 Feb 2018 10:55:38 -0600 Subject: PCI: Probe for device reset support during enumeration Previously we called pci_probe_reset_function() in this path: pci_sysfs_init # late_initcall for_each_pci_dev(dev) pci_create_sysfs_dev_files(dev) pci_create_capabilities_sysfs(dev) pci_probe_reset_function pci_dev_specific_reset pcie_has_flr pcie_capability_read_dword pci_sysfs_init() is a late_initcall, and a driver may have already claimed one of these devices and enabled runtime power management for it, so the device could already be in D3 by the time we get to pci_sysfs_init(). The device itself should respond to the config read even while it's in D3hot, but if an upstream bridge is also in D3hot, the read won't even reach the device because the bridge won't forward it downstream to the device. If the bridge is a PCIe port, it should complete the read as an Unsupported Request, which may be reported to the CPU as an exception or as invalid data. Avoid this case by probing for reset support from pci_init_capabilities(), before a driver can claim the device. The device may be in D3hot, but any bridges leading to it should be in D0, so the device's config space should be fully accessible at that point. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pci-sysfs.c | 3 +-- drivers/pci/probe.c | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index eb6bee8724cc..4933f0270471 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1542,11 +1542,10 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev) /* Active State Power Management */ pcie_aspm_create_sysfs_dev_files(dev); - if (!pci_probe_reset_function(dev)) { + if (dev->reset_fn) { retval = device_create_file(&dev->dev, &reset_attr); if (retval) goto error; - dev->reset_fn = 1; } return 0; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ef5377438a1e..489660d0d384 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2121,6 +2121,9 @@ static void pci_init_capabilities(struct pci_dev *dev) /* Advanced Error Reporting */ pci_aer_init(dev); + + if (pci_probe_reset_function(dev) == 0) + dev->reset_fn = 1; } /* -- cgit v1.2.3 From 204f4afa7ae50239c39adb13af42f5720fe7e9a5 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 16 Feb 2018 15:22:39 -0600 Subject: PCI: Remove redundant probes for device reset support We probe every device for whether it supports reset so we can tell whether to create a sysfs "reset" file for it. We do that probe in pci_init_capabilities() during enumeration and save the result in dev->reset_fn. The result doesn't depend on any other devices on the bus and shouldn't change after boot, so we don't need to do the probe again. Remove the pci_probe_reset_function() calls and rely on the dev->reset_fn we found during enumeration. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pci.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index f6a4dd10d9b0..4db740e4f50a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4450,9 +4450,8 @@ int pci_reset_function(struct pci_dev *dev) { int rc; - rc = pci_probe_reset_function(dev); - if (rc) - return rc; + if (!dev->reset_fn) + return -ENOTTY; pci_dev_lock(dev); pci_dev_save_and_disable(dev); @@ -4487,9 +4486,8 @@ int pci_reset_function_locked(struct pci_dev *dev) { int rc; - rc = pci_probe_reset_function(dev); - if (rc) - return rc; + if (!dev->reset_fn) + return -ENOTTY; pci_dev_save_and_disable(dev); @@ -4511,9 +4509,8 @@ int pci_try_reset_function(struct pci_dev *dev) { int rc; - rc = pci_probe_reset_function(dev); - if (rc) - return rc; + if (!dev->reset_fn) + return -ENOTTY; if (!pci_dev_trylock(dev)) return -EAGAIN; -- cgit v1.2.3 From 3ecac020d6dd09259414f423b577347ebee9f533 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 8 Feb 2018 23:20:35 +1100 Subject: PCI/AER: Move pci_uevent_ers() out of pci.h There's no reason pci_uevent_ers() needs to be inline in pci.h, so move it out to a C file. Given it's used by AER the obvious location would be somewhere in drivers/pci/pcie/aer, but because it's also used by powerpc EEH code unfortunately that doesn't work in the case where EEH is enabled but PCIEPORTBUS is not. So for now put it in pci-driver.c, next to pci_uevent(), with an appropriate #ifdef so it's not built if AER and EEH are both disabled. While we're moving it also fix up the kernel doc comment for @pdev to be accurate. Reported-by: Linus Torvalds Signed-off-by: Michael Ellerman Signed-off-by: Bjorn Helgaas Reviewed-by: Bryant G. Ly --- drivers/pci/pci-driver.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 38 +++----------------------------------- 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 3bed6beda051..8876b98546ce 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1517,6 +1517,42 @@ static int pci_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } +#if defined(CONFIG_PCIEAER) || defined(CONFIG_EEH) +/** + * pci_uevent_ers - emit a uevent during recovery path of PCI device + * @pdev: PCI device undergoing error recovery + * @err_type: type of error event + */ +void pci_uevent_ers(struct pci_dev *pdev, enum pci_ers_result err_type) +{ + int idx = 0; + char *envp[3]; + + switch (err_type) { + case PCI_ERS_RESULT_NONE: + case PCI_ERS_RESULT_CAN_RECOVER: + envp[idx++] = "ERROR_EVENT=BEGIN_RECOVERY"; + envp[idx++] = "DEVICE_ONLINE=0"; + break; + case PCI_ERS_RESULT_RECOVERED: + envp[idx++] = "ERROR_EVENT=SUCCESSFUL_RECOVERY"; + envp[idx++] = "DEVICE_ONLINE=1"; + break; + case PCI_ERS_RESULT_DISCONNECT: + envp[idx++] = "ERROR_EVENT=FAILED_RECOVERY"; + envp[idx++] = "DEVICE_ONLINE=0"; + break; + default: + break; + } + + if (idx > 0) { + envp[idx++] = NULL; + kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp); + } +} +#endif + static int pci_bus_num_vf(struct device *dev) { return pci_num_vf(to_pci_dev(dev)); diff --git a/include/linux/pci.h b/include/linux/pci.h index 024a1beda008..19c1dbcff0c6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2280,41 +2280,9 @@ static inline bool pci_is_thunderbolt_attached(struct pci_dev *pdev) return false; } -/** - * pci_uevent_ers - emit a uevent during recovery path of pci device - * @pdev: pci device to check - * @err_type: type of error event - * - */ -static inline void pci_uevent_ers(struct pci_dev *pdev, - enum pci_ers_result err_type) -{ - int idx = 0; - char *envp[3]; - - switch (err_type) { - case PCI_ERS_RESULT_NONE: - case PCI_ERS_RESULT_CAN_RECOVER: - envp[idx++] = "ERROR_EVENT=BEGIN_RECOVERY"; - envp[idx++] = "DEVICE_ONLINE=0"; - break; - case PCI_ERS_RESULT_RECOVERED: - envp[idx++] = "ERROR_EVENT=SUCCESSFUL_RECOVERY"; - envp[idx++] = "DEVICE_ONLINE=1"; - break; - case PCI_ERS_RESULT_DISCONNECT: - envp[idx++] = "ERROR_EVENT=FAILED_RECOVERY"; - envp[idx++] = "DEVICE_ONLINE=0"; - break; - default: - break; - } - - if (idx > 0) { - envp[idx++] = NULL; - kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp); - } -} +#if defined(CONFIG_PCIEAER) || defined(CONFIG_EEH) +void pci_uevent_ers(struct pci_dev *pdev, enum pci_ers_result err_type); +#endif /* Provide the legacy pci_dma_* API */ #include -- cgit v1.2.3 From c37e627f9565368ed7bd1f3cf59a2d223ddba85a Mon Sep 17 00:00:00 2001 From: Frederick Lawler Date: Tue, 13 Feb 2018 21:52:18 -0600 Subject: PCI/portdrv: Move pcieport_if.h to drivers/pci/pcie/ Move pcieport_if.h from include/linux to drivers/pci/pcie/pcieport_if.h because the interfaces there are only used by the PCI core. Replace all uses of #include with relative paths to the new file location, e.g., #include "../pcieport_if.h" Signed-off-by: Frederick Lawler Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp.h | 3 +- drivers/pci/pcie/aer/aerdrv.c | 1 - drivers/pci/pcie/aer/aerdrv.h | 3 +- drivers/pci/pcie/pcie-dpc.c | 3 +- drivers/pci/pcie/pcieport_if.h | 71 +++++++++++++++++++++++++++++++++++++++++ drivers/pci/pcie/pme.c | 2 +- drivers/pci/pcie/portdrv_acpi.c | 2 +- drivers/pci/pcie/portdrv_bus.c | 2 +- drivers/pci/pcie/portdrv_core.c | 2 +- drivers/pci/pcie/portdrv_pci.c | 2 +- include/linux/pcieport_if.h | 71 ----------------------------------------- 11 files changed, 82 insertions(+), 80 deletions(-) create mode 100644 drivers/pci/pcie/pcieport_if.h delete mode 100644 include/linux/pcieport_if.h diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 636ed8f4b869..08072bcaa381 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -20,10 +20,11 @@ #include #include #include /* signal_pending() */ -#include #include #include +#include "../pcie/pcieport_if.h" + #define MY_NAME "pciehp" extern bool pciehp_poll_mode; diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index da8331f5684d..28329e16ad8f 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include "aerdrv.h" diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 5449e5ce139d..568326f385b7 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -10,10 +10,11 @@ #define _AERDRV_H_ #include -#include #include #include +#include "../pcieport_if.h" + #define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ PCI_EXP_RTCTL_SENFEE| \ PCI_EXP_RTCTL_SEFEE) diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/pcie-dpc.c index 38e40c6c576f..bac895de4c72 100644 --- a/drivers/pci/pcie/pcie-dpc.c +++ b/drivers/pci/pcie/pcie-dpc.c @@ -10,7 +10,8 @@ #include #include #include -#include + +#include "pcieport_if.h" #include "../pci.h" #include "aer/aerdrv.h" diff --git a/drivers/pci/pcie/pcieport_if.h b/drivers/pci/pcie/pcieport_if.h new file mode 100644 index 000000000000..b69769dbf659 --- /dev/null +++ b/drivers/pci/pcie/pcieport_if.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * File: pcieport_if.h + * Purpose: PCI Express Port Bus Driver's IF Data Structure + * + * Copyright (C) 2004 Intel + * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) + */ + +#ifndef _PCIEPORT_IF_H_ +#define _PCIEPORT_IF_H_ + +/* Port Type */ +#define PCIE_ANY_PORT (~0) + +/* Service Type */ +#define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */ +#define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT) +#define PCIE_PORT_SERVICE_AER_SHIFT 1 /* Advanced Error Reporting */ +#define PCIE_PORT_SERVICE_AER (1 << PCIE_PORT_SERVICE_AER_SHIFT) +#define PCIE_PORT_SERVICE_HP_SHIFT 2 /* Native Hotplug */ +#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT) +#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */ +#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT) +#define PCIE_PORT_SERVICE_DPC_SHIFT 4 /* Downstream Port Containment */ +#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT) + +struct pcie_device { + int irq; /* Service IRQ/MSI/MSI-X Vector */ + struct pci_dev *port; /* Root/Upstream/Downstream Port */ + u32 service; /* Port service this device represents */ + void *priv_data; /* Service Private Data */ + struct device device; /* Generic Device Interface */ +}; +#define to_pcie_device(d) container_of(d, struct pcie_device, device) + +static inline void set_service_data(struct pcie_device *dev, void *data) +{ + dev->priv_data = data; +} + +static inline void *get_service_data(struct pcie_device *dev) +{ + return dev->priv_data; +} + +struct pcie_port_service_driver { + const char *name; + int (*probe) (struct pcie_device *dev); + void (*remove) (struct pcie_device *dev); + int (*suspend) (struct pcie_device *dev); + int (*resume) (struct pcie_device *dev); + + /* Device driver may resume normal operations */ + void (*error_resume)(struct pci_dev *dev); + + /* Link Reset Capability - AER service driver specific */ + pci_ers_result_t (*reset_link) (struct pci_dev *dev); + + int port_type; /* Type of the port this driver can handle */ + u32 service; /* Port service this device represents */ + + struct device_driver driver; +}; +#define to_service_driver(d) \ + container_of(d, struct pcie_port_service_driver, driver) + +int pcie_port_service_register(struct pcie_port_service_driver *new); +void pcie_port_service_unregister(struct pcie_port_service_driver *new); + +#endif /* _PCIEPORT_IF_H_ */ diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 5480f54f7612..d29678958d92 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -14,9 +14,9 @@ #include #include #include -#include #include +#include "pcieport_if.h" #include "../pci.h" #include "portdrv.h" diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c index 319c94976873..c7d8debb4a5c 100644 --- a/drivers/pci/pcie/portdrv_acpi.c +++ b/drivers/pci/pcie/portdrv_acpi.c @@ -10,8 +10,8 @@ #include #include #include -#include +#include "pcieport_if.h" #include "aer/aerdrv.h" #include "../pci.h" #include "portdrv.h" diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c index f0fba552a0e2..b5c5697cfb30 100644 --- a/drivers/pci/pcie/portdrv_bus.c +++ b/drivers/pci/pcie/portdrv_bus.c @@ -13,7 +13,7 @@ #include #include -#include +#include "pcieport_if.h" #include "portdrv.h" static int pcie_port_bus_match(struct device *dev, struct device_driver *drv); diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index ef3bad4ad010..bab9cb71130f 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -15,9 +15,9 @@ #include #include #include -#include #include +#include "pcieport_if.h" #include "../pci.h" #include "portdrv.h" diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index fb1c1bb87316..13dbe846a1d1 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -15,11 +15,11 @@ #include #include #include -#include #include #include #include +#include "pcieport_if.h" #include "../pci.h" #include "portdrv.h" diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h deleted file mode 100644 index b69769dbf659..000000000000 --- a/include/linux/pcieport_if.h +++ /dev/null @@ -1,71 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * File: pcieport_if.h - * Purpose: PCI Express Port Bus Driver's IF Data Structure - * - * Copyright (C) 2004 Intel - * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) - */ - -#ifndef _PCIEPORT_IF_H_ -#define _PCIEPORT_IF_H_ - -/* Port Type */ -#define PCIE_ANY_PORT (~0) - -/* Service Type */ -#define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */ -#define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT) -#define PCIE_PORT_SERVICE_AER_SHIFT 1 /* Advanced Error Reporting */ -#define PCIE_PORT_SERVICE_AER (1 << PCIE_PORT_SERVICE_AER_SHIFT) -#define PCIE_PORT_SERVICE_HP_SHIFT 2 /* Native Hotplug */ -#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT) -#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */ -#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT) -#define PCIE_PORT_SERVICE_DPC_SHIFT 4 /* Downstream Port Containment */ -#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT) - -struct pcie_device { - int irq; /* Service IRQ/MSI/MSI-X Vector */ - struct pci_dev *port; /* Root/Upstream/Downstream Port */ - u32 service; /* Port service this device represents */ - void *priv_data; /* Service Private Data */ - struct device device; /* Generic Device Interface */ -}; -#define to_pcie_device(d) container_of(d, struct pcie_device, device) - -static inline void set_service_data(struct pcie_device *dev, void *data) -{ - dev->priv_data = data; -} - -static inline void *get_service_data(struct pcie_device *dev) -{ - return dev->priv_data; -} - -struct pcie_port_service_driver { - const char *name; - int (*probe) (struct pcie_device *dev); - void (*remove) (struct pcie_device *dev); - int (*suspend) (struct pcie_device *dev); - int (*resume) (struct pcie_device *dev); - - /* Device driver may resume normal operations */ - void (*error_resume)(struct pci_dev *dev); - - /* Link Reset Capability - AER service driver specific */ - pci_ers_result_t (*reset_link) (struct pci_dev *dev); - - int port_type; /* Type of the port this driver can handle */ - u32 service; /* Port service this device represents */ - - struct device_driver driver; -}; -#define to_service_driver(d) \ - container_of(d, struct pcie_port_service_driver, driver) - -int pcie_port_service_register(struct pcie_port_service_driver *new); -void pcie_port_service_unregister(struct pcie_port_service_driver *new); - -#endif /* _PCIEPORT_IF_H_ */ -- cgit v1.2.3 From 4ef76ad0462cf25ce948541c8724eaa8a8365e1d Mon Sep 17 00:00:00 2001 From: Feng Kan Date: Tue, 20 Feb 2018 19:19:27 -0800 Subject: PCI: Add ACS quirk for Ampere root ports The Ampere Computing PCIe root port does not support ACS at this point. However, the hardware provides isolation and source validation through the SMMU. The stream ID generated by the PCIe ports contain both the bus/device/function number as well as the port ID in its 3 most significant bits. Turn on ACS but disable all the peer-to-peer features. APM is being rebranded to Ampere. The Vendor and Device IDs change, but the functionality stays the same. Signed-off-by: Feng Kan Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 9 +++++++++ include/linux/pci_ids.h | 1 + 2 files changed, 10 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index fc734014206f..57748a3b83f0 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4514,6 +4514,15 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_CAVIUM, PCI_ANY_ID, pci_quirk_cavium_acs }, /* APM X-Gene */ { PCI_VENDOR_ID_AMCC, 0xE004, pci_quirk_xgene_acs }, + /* Ampere Computing */ + { PCI_VENDOR_ID_AMPERE, 0xE005, pci_quirk_xgene_acs }, + { PCI_VENDOR_ID_AMPERE, 0xE006, pci_quirk_xgene_acs }, + { PCI_VENDOR_ID_AMPERE, 0xE007, pci_quirk_xgene_acs }, + { PCI_VENDOR_ID_AMPERE, 0xE008, pci_quirk_xgene_acs }, + { PCI_VENDOR_ID_AMPERE, 0xE009, pci_quirk_xgene_acs }, + { PCI_VENDOR_ID_AMPERE, 0xE00A, pci_quirk_xgene_acs }, + { PCI_VENDOR_ID_AMPERE, 0xE00B, pci_quirk_xgene_acs }, + { PCI_VENDOR_ID_AMPERE, 0xE00C, pci_quirk_xgene_acs }, { 0 } }; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index a6b30667a331..c875d4223f44 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1333,6 +1333,7 @@ #define PCI_DEVICE_ID_IMS_TT3D 0x9135 #define PCI_VENDOR_ID_AMCC 0x10e8 +#define PCI_VENDOR_ID_AMPERE 0x1def #define PCI_VENDOR_ID_INTERG 0x10ea #define PCI_DEVICE_ID_INTERG_1682 0x1682 -- cgit v1.2.3 From 08622940b5cd2c8799992ca69e368caf1608af8d Mon Sep 17 00:00:00 2001 From: Ulf Magnusson Date: Tue, 6 Feb 2018 23:11:06 +0100 Subject: PCI: vmd: Fix malformed Kconfig default 'default N' should be 'default n', though they happen to have the same effect here, due to undefined symbols (N in this case) evaluating to n in a tristate sense. Remove the default instead of changing it. bool and tristate symbols implicitly default to n. Signed-off-by: Ulf Magnusson [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas --- drivers/pci/host/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index a4ed7484d127..dc8a2a175f19 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -215,7 +215,6 @@ config PCIE_TANGO_SMP8759 config VMD depends on PCI_MSI && X86_64 && SRCU tristate "Intel Volume Management Device Driver" - default N ---help--- Adds support for the Intel Volume Management Device (VMD). VMD is a secondary PCI host bridge that allows PCI Express root ports, -- cgit v1.2.3 From 38b35992b7d2729095a6e78a0f06f2f04c5a4e90 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 23 Feb 2018 12:29:49 +0000 Subject: PCI: rcar-gen2: Remove duplicated bit-wise or of RCAR_PCI_INT_SIGRETABORT Bit pattern RCAR_PCI_INT_SIGRETABORT is being bit-wise or'd twice; remove the redundant 2nd RCAR_PCI_INT_SIGRETABORT. Signed-off-by: Colin Ian King Signed-off-by: Lorenzo Pieralisi Reviewed-by: Geert Uytterhoeven --- drivers/pci/host/pci-rcar-gen2.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index a28370bb2b2a..dd4f1a6b57c5 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -52,7 +52,6 @@ #define RCAR_PCI_INT_B (1 << 17) #define RCAR_PCI_INT_PME (1 << 19) #define RCAR_PCI_INT_ALLERRORS (RCAR_PCI_INT_SIGTABORT | \ - RCAR_PCI_INT_SIGRETABORT | \ RCAR_PCI_INT_SIGRETABORT | \ RCAR_PCI_INT_REMABORT | \ RCAR_PCI_INT_PERR | \ -- cgit v1.2.3 From 832e4e1f76b8a84991e9db56fdcef1ebce839b8b Mon Sep 17 00:00:00 2001 From: Thomas Vincent-Cross Date: Tue, 27 Feb 2018 20:20:36 +1100 Subject: PCI: Add function 1 DMA alias quirk for Marvell 88SE9220 Add Marvell 88SE9220 DMA quirk as found and tested on bug 42679. Link: https://bugzilla.kernel.org/show_bug.cgi?id=42679 Signed-off-by: Thomas Vincent-Cross Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 57748a3b83f0..ffdfaac116b2 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3896,6 +3896,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9182, /* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c46 */ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0, quirk_dma_func1_alias); +/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c127 */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9220, + quirk_dma_func1_alias); /* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c49 */ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9230, quirk_dma_func1_alias); -- cgit v1.2.3 From cb5e0d060fb1f3136e96acecbd4001a7f0cbac94 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 27 Feb 2018 14:14:08 -0600 Subject: PCI: Protect restore with device lock to be consistent Commit b014e96d1abb ("PCI: Protect pci_error_handlers->reset_notify() usage with device_lock()") added protection around pci_dev_restore() function so a device-specific remove callback does not cause a race condition with hotplug. pci_dev_lock() usage has been forgotten in two places. Add locks for pci_slot_restore() and moving pci_dev_restore() inside the locks for pci_try_reset_function(). Signed-off-by: Sinan Kaya Signed-off-by: Bjorn Helgaas Reviewed-by: Christoph Hellwig --- drivers/pci/pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 4db740e4f50a..660c848aa14a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4517,9 +4517,9 @@ int pci_try_reset_function(struct pci_dev *dev) pci_dev_save_and_disable(dev); rc = __pci_reset_function_locked(dev); + pci_dev_restore(dev); pci_dev_unlock(dev); - pci_dev_restore(dev); return rc; } EXPORT_SYMBOL_GPL(pci_try_reset_function); @@ -4727,7 +4727,9 @@ static void pci_slot_restore(struct pci_slot *slot) list_for_each_entry(dev, &slot->bus->devices, bus_list) { if (!dev->slot || dev->slot != slot) continue; + pci_dev_lock(dev); pci_dev_restore(dev); + pci_dev_unlock(dev); if (dev->subordinate) pci_bus_restore(dev->subordinate); } -- cgit v1.2.3 From 91295d79d65892eabd02a2a75fd4ac88197d17a1 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 27 Feb 2018 14:14:08 -0600 Subject: PCI: Handle FLR failure and allow other reset types pci_flr_wait() and pci_af_flr() functions assume graceful return even though the device is inaccessible under error conditions. Return -ENOTTY in error cases so that __pci_reset_function_locked() can try other reset types if AF_FLR/FLR reset fails. Signed-off-by: Sinan Kaya Signed-off-by: Bjorn Helgaas Reviewed-by: Christoph Hellwig --- drivers/pci/pci.c | 18 ++++++++++-------- include/linux/pci.h | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 660c848aa14a..7aa11b1fbee3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4017,7 +4017,7 @@ int pci_wait_for_pending_transaction(struct pci_dev *dev) } EXPORT_SYMBOL(pci_wait_for_pending_transaction); -static void pci_flr_wait(struct pci_dev *dev) +static int pci_flr_wait(struct pci_dev *dev) { int delay = 1, timeout = 60000; u32 id; @@ -4046,7 +4046,7 @@ static void pci_flr_wait(struct pci_dev *dev) if (delay > timeout) { pci_warn(dev, "not ready %dms after FLR; giving up\n", 100 + delay - 1); - return; + return -ENOTTY; } if (delay > 1000) @@ -4060,6 +4060,8 @@ static void pci_flr_wait(struct pci_dev *dev) if (delay > 1000) pci_info(dev, "ready %dms after FLR\n", 100 + delay - 1); + + return 0; } /** @@ -4088,13 +4090,13 @@ static bool pcie_has_flr(struct pci_dev *dev) * device supports FLR before calling this function, e.g. by using the * pcie_has_flr() helper. */ -void pcie_flr(struct pci_dev *dev) +int pcie_flr(struct pci_dev *dev) { if (!pci_wait_for_pending_transaction(dev)) pci_err(dev, "timed out waiting for pending transaction; performing function level reset anyway\n"); pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR); - pci_flr_wait(dev); + return pci_flr_wait(dev); } EXPORT_SYMBOL_GPL(pcie_flr); @@ -4127,8 +4129,7 @@ static int pci_af_flr(struct pci_dev *dev, int probe) pci_err(dev, "timed out waiting for pending transaction; performing AF function level reset anyway\n"); pci_write_config_byte(dev, pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR); - pci_flr_wait(dev); - return 0; + return pci_flr_wait(dev); } /** @@ -4379,8 +4380,9 @@ int __pci_reset_function_locked(struct pci_dev *dev) if (rc != -ENOTTY) return rc; if (pcie_has_flr(dev)) { - pcie_flr(dev); - return 0; + rc = pcie_flr(dev); + if (rc != -ENOTTY) + return rc; } rc = pci_af_flr(dev, 0); if (rc != -ENOTTY) diff --git a/include/linux/pci.h b/include/linux/pci.h index 024a1beda008..af75d9d76189 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1082,7 +1082,7 @@ int pcie_get_mps(struct pci_dev *dev); int pcie_set_mps(struct pci_dev *dev, int mps); int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, enum pcie_link_width *width); -void pcie_flr(struct pci_dev *dev); +int pcie_flr(struct pci_dev *dev); int __pci_reset_function_locked(struct pci_dev *dev); int pci_reset_function(struct pci_dev *dev); int pci_reset_function_locked(struct pci_dev *dev); -- cgit v1.2.3 From a2758b6b8fdba5f1045f571fdb39d9bdb8ba0813 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 27 Feb 2018 14:14:10 -0600 Subject: PCI: Rename pci_flr_wait() to pci_dev_wait() and make it generic PCIe r4.0, sec 2.3.1, Request Handling Rules, says: Valid reset conditions after which a device is permitted to return CRS are: * Cold, Warm, and Hot Resets, * FLR * A reset initiated in response to a D3hot to D0 uninitialized Try to reuse FLR implementation towards other reset types. Signed-off-by: Sinan Kaya Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 48 +++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7aa11b1fbee3..9493b97436c3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -126,6 +126,9 @@ static int __init pcie_port_pm_setup(char *str) } __setup("pcie_port_pm=", pcie_port_pm_setup); +/* Time to wait after a reset for device to become responsive */ +#define PCIE_RESET_READY_POLL_MS 60000 + /** * pci_bus_max_busnr - returns maximum PCI bus number of given bus' children * @bus: pointer to PCI bus structure to search @@ -4017,20 +4020,13 @@ int pci_wait_for_pending_transaction(struct pci_dev *dev) } EXPORT_SYMBOL(pci_wait_for_pending_transaction); -static int pci_flr_wait(struct pci_dev *dev) +static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout) { - int delay = 1, timeout = 60000; + int delay = 1; u32 id; /* - * Per PCIe r3.1, sec 6.6.2, a device must complete an FLR within - * 100ms, but may silently discard requests while the FLR is in - * progress. Wait 100ms before trying to access the device. - */ - msleep(100); - - /* - * After 100ms, the device should not silently discard config + * After reset, the device should not silently discard config * requests, but it may still indicate that it needs more time by * responding to them with CRS completions. The Root Port will * generally synthesize ~0 data to complete the read (except when @@ -4044,14 +4040,14 @@ static int pci_flr_wait(struct pci_dev *dev) pci_read_config_dword(dev, PCI_COMMAND, &id); while (id == ~0) { if (delay > timeout) { - pci_warn(dev, "not ready %dms after FLR; giving up\n", - 100 + delay - 1); + pci_warn(dev, "not ready %dms after %s; giving up\n", + delay - 1, reset_type); return -ENOTTY; } if (delay > 1000) - pci_info(dev, "not ready %dms after FLR; waiting\n", - 100 + delay - 1); + pci_info(dev, "not ready %dms after %s; waiting\n", + delay - 1, reset_type); msleep(delay); delay *= 2; @@ -4059,7 +4055,8 @@ static int pci_flr_wait(struct pci_dev *dev) } if (delay > 1000) - pci_info(dev, "ready %dms after FLR\n", 100 + delay - 1); + pci_info(dev, "ready %dms after %s\n", delay - 1, + reset_type); return 0; } @@ -4096,7 +4093,15 @@ int pcie_flr(struct pci_dev *dev) pci_err(dev, "timed out waiting for pending transaction; performing function level reset anyway\n"); pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR); - return pci_flr_wait(dev); + + /* + * Per PCIe r4.0, sec 6.6.2, a device must complete an FLR within + * 100ms, but may silently discard requests while the FLR is in + * progress. Wait 100ms before trying to access the device. + */ + msleep(100); + + return pci_dev_wait(dev, "FLR", PCIE_RESET_READY_POLL_MS); } EXPORT_SYMBOL_GPL(pcie_flr); @@ -4129,7 +4134,16 @@ static int pci_af_flr(struct pci_dev *dev, int probe) pci_err(dev, "timed out waiting for pending transaction; performing AF function level reset anyway\n"); pci_write_config_byte(dev, pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR); - return pci_flr_wait(dev); + + /* + * Per Advanced Capabilities for Conventional PCI ECN, 13 April 2006, + * updated 27 July 2006; a device must complete an FLR within + * 100ms, but may silently discard requests while the FLR is in + * progress. Wait 100ms before trying to access the device. + */ + msleep(100); + + return pci_dev_wait(dev, "AF_FLR", PCIE_RESET_READY_POLL_MS); } /** -- cgit v1.2.3 From db89ed14a2f2fcaa157a57fe3dd32cdb48459e2b Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 19 Jan 2018 21:26:51 -0600 Subject: PCI: altera: Fix bool initialization in tlp_read_packet() Bool variables should be initialized only through true and false values; update tlp_read_packet() code to comply. Detected using the Coccinelle tool. Fixes: eaa6111b70a7 ("PCI: altera: Add Altera PCIe host controller driver") Signed-off-by: Gustavo A. R. Silva [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi Acked-by: Ley Foon Tan --- drivers/pci/host/pcie-altera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c index 2235f4760951..a6af62e0256d 100644 --- a/drivers/pci/host/pcie-altera.c +++ b/drivers/pci/host/pcie-altera.c @@ -145,7 +145,7 @@ static bool altera_pcie_valid_device(struct altera_pcie *pcie, static int tlp_read_packet(struct altera_pcie *pcie, u32 *value) { int i; - bool sop = 0; + bool sop = false; u32 ctrl; u32 reg0, reg1; u32 comp_status = 1; -- cgit v1.2.3 From 205adda79a408f4a2945f636e1c076f7db3b2045 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Sun, 25 Feb 2018 10:01:42 +0800 Subject: PCI: cpqphp: Fix possible NULL pointer dereference Check io_node for NULL before dereferencing it. Signed-off-by: Shawn Lin Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/cpqphp_ctrl.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c index b1b6e45253b2..616df442520b 100644 --- a/drivers/pci/hotplug/cpqphp_ctrl.c +++ b/drivers/pci/hotplug/cpqphp_ctrl.c @@ -2812,18 +2812,16 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func dbg("CND: length = 0x%x\n", base); io_node = get_io_resource(&(resources->io_head), base); + if (!io_node) + return -ENOMEM; dbg("Got io_node start = %8.8x, length = %8.8x next (%p)\n", io_node->base, io_node->length, io_node->next); dbg("func (%p) io_head (%p)\n", func, func->io_head); /* allocate the resource to the board */ - if (io_node) { - base = io_node->base; - - io_node->next = func->io_head; - func->io_head = io_node; - } else - return -ENOMEM; + base = io_node->base; + io_node->next = func->io_head; + func->io_head = io_node; } else if ((temp_register & 0x0BL) == 0x08) { /* Map prefetchable memory */ base = temp_register & 0xFFFFFFF0; -- cgit v1.2.3 From f51af8a63c5f633b572d00319ecdfa31f266b8fb Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 27 Feb 2018 17:19:52 -0600 Subject: PCI/ASPM: Declare threshold_ns as u32, not u64 aspm_calc_l1ss_info() computes l1_2_threshold in microseconds as: l1_2_threshold = 2 + 4 + t_common_mode + t_power_on; where t_common_mode is at most 255us: PCI_L1SS_CAP_CM_RESTORE_TIME 0x0000ff00 <-- 8 bits; <256us and t_power_on is at most 31 * 100us = 3100us: PCI_L1SS_CAP_P_PWR_ON_VALUE 0x00f80000 <-- 5 bits; <32 PCI_L1SS_CAP_P_PWR_ON_SCALE 0x00030000 <-- *2us, *10us, or *100us So l1_2_threshold is at most 2 + 4 + 255 + 3100 = 3361, which means threshold_ns is at most 3361 * 1000 = 3361000, which easily fits in a u32. Declare threshold_ns as u32, not u64. This fixes a Coverity warning. Addresses-Coverity-ID: 1462501 Signed-off-by: Gustavo A. R. Silva [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko --- drivers/pci/pcie/aspm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 57feef2ecfe7..8633fc4e1c11 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -322,7 +322,7 @@ static u32 calc_l1ss_pwron(struct pci_dev *pdev, u32 scale, u32 val) static void encode_l12_threshold(u32 threshold_us, u32 *scale, u32 *value) { - u64 threshold_ns = threshold_us * 1000; + u32 threshold_ns = threshold_us * 1000; /* See PCIe r3.1, sec 7.33.3 and sec 6.18 */ if (threshold_ns < 32) { -- cgit v1.2.3 From 04875177dbe034055f23960981ecf6fb8ea1d638 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Mon, 22 Jan 2018 15:12:01 -0500 Subject: PCI/ASPM: Don't warn if already in common clock mode Previously we emitted a warning if we tried to configure common clock mode the link was already configured to common clock mode by the UEFI BIOS. Bail out silently in that case instead of emitting the warning: pci 0004:00:00.0: ASPM: Could not configure common clock Signed-off-by: Sinan Kaya [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 8633fc4e1c11..95a2f222b64e 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -228,6 +228,24 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link) if (!(reg16 & PCI_EXP_LNKSTA_SLC)) same_clock = 0; + /* Port might be already in common clock mode */ + pcie_capability_read_word(parent, PCI_EXP_LNKCTL, ®16); + if (same_clock && (reg16 & PCI_EXP_LNKCTL_CCC)) { + bool consistent = true; + + list_for_each_entry(child, &linkbus->devices, bus_list) { + pcie_capability_read_word(child, PCI_EXP_LNKCTL, + ®16); + if (!(reg16 & PCI_EXP_LNKCTL_CCC)) { + consistent = false; + break; + } + } + if (consistent) + return; + pci_warn(parent, "ASPM: current common clock configuration is broken, reconfiguring\n"); + } + /* Configure downstream component, all functions */ list_for_each_entry(child, &linkbus->devices, bus_list) { pcie_capability_read_word(child, PCI_EXP_LNKCTL, ®16); -- cgit v1.2.3 From 28f8f1833be700c95cf9cb198c788649db0b3b65 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 19 Feb 2018 12:59:51 +0000 Subject: powerpc/pci: Use generic pci_mmap_resource_range() Commit f719582435 ("PCI: Add pci_mmap_resource_range() and use it for ARM64") added this generic function with the intent of using it everywhere and ultimately killing the old arch-specific implementations. Remove the powerpc-specific pci_mmap_page_range() and use the generic pci_mmap_resource_range() instead. Powerpc can mmap I/O port space, so supply the powerpc-specific pci_iobar_pfn() required to make that work. Signed-off-by: David Woodhouse [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas --- arch/powerpc/include/asm/pci.h | 9 ++-- arch/powerpc/kernel/pci-common.c | 106 ++++----------------------------------- 2 files changed, 15 insertions(+), 100 deletions(-) diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index d82802ff5088..401c62aad5e4 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -76,10 +76,11 @@ extern int pci_proc_domain(struct pci_bus *bus); struct vm_area_struct; -/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() and it does WC */ -#define HAVE_PCI_MMAP 1 -#define arch_can_pci_mmap_io() 1 -#define arch_can_pci_mmap_wc() 1 +/* Tell PCI code what kind of PCI resource mappings we support */ +#define HAVE_PCI_MMAP 1 +#define ARCH_GENERIC_PCI_MMAP_RESOURCE 1 +#define arch_can_pci_mmap_io() 1 +#define arch_can_pci_mmap_wc() 1 extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t count); diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 446c79611d56..fe9733ffffaa 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -410,72 +410,22 @@ static int pci_read_irq_line(struct pci_dev *pci_dev) } /* - * Platform support for /proc/bus/pci/X/Y mmap()s, - * modelled on the sparc64 implementation by Dave Miller. + * Platform support for /proc/bus/pci/X/Y mmap()s. * -- paulus. */ - -/* - * Adjust vm_pgoff of VMA such that it is the physical page offset - * corresponding to the 32-bit pci bus offset for DEV requested by the user. - * - * Basically, the user finds the base address for his device which he wishes - * to mmap. They read the 32-bit value from the config space base register, - * add whatever PAGE_SIZE multiple offset they wish, and feed this into the - * offset parameter of mmap on /proc/bus/pci/XXX for that device. - * - * Returns negative error code on failure, zero on success. - */ -static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, - resource_size_t *offset, - enum pci_mmap_state mmap_state) +int pci_iobar_pfn(struct pci_dev *pdev, int bar, struct vm_area_struct *vma) { - struct pci_controller *hose = pci_bus_to_host(dev->bus); - unsigned long io_offset = 0; - int i, res_bit; - - if (hose == NULL) - return NULL; /* should never happen */ - - /* If memory, add on the PCI bridge address offset */ - if (mmap_state == pci_mmap_mem) { -#if 0 /* See comment in pci_resource_to_user() for why this is disabled */ - *offset += hose->pci_mem_offset; -#endif - res_bit = IORESOURCE_MEM; - } else { - io_offset = (unsigned long)hose->io_base_virt - _IO_BASE; - *offset += io_offset; - res_bit = IORESOURCE_IO; - } - - /* - * Check that the offset requested corresponds to one of the - * resources of the device. - */ - for (i = 0; i <= PCI_ROM_RESOURCE; i++) { - struct resource *rp = &dev->resource[i]; - int flags = rp->flags; + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + resource_size_t ioaddr = pci_resource_start(pdev, bar); - /* treat ROM as memory (should be already) */ - if (i == PCI_ROM_RESOURCE) - flags |= IORESOURCE_MEM; - - /* Active and same type? */ - if ((flags & res_bit) == 0) - continue; - - /* In the range of this resource? */ - if (*offset < (rp->start & PAGE_MASK) || *offset > rp->end) - continue; + if (!hose) + return -EINVAL; - /* found it! construct the final physical address */ - if (mmap_state == pci_mmap_io) - *offset += hose->io_base_phys - io_offset; - return rp; - } + /* Convert to an offset within this PCI controller */ + ioaddr -= (unsigned long)hose->io_base_virt - _IO_BASE; - return NULL; + vma->vm_pgoff += (ioaddr + hose->io_base_phys) >> PAGE_SHIFT; + return 0; } /* @@ -527,42 +477,6 @@ pgprot_t pci_phys_mem_access_prot(struct file *file, return prot; } - -/* - * Perform the actual remap of the pages for a PCI device mapping, as - * appropriate for this architecture. The region in the process to map - * is described by vm_start and vm_end members of VMA, the base physical - * address is found in vm_pgoff. - * The pci device structure is provided so that architectures may make mapping - * decisions on a per-device or per-bus basis. - * - * Returns a negative error code on failure, zero on success. - */ -int pci_mmap_page_range(struct pci_dev *dev, int bar, - struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine) -{ - resource_size_t offset = - ((resource_size_t)vma->vm_pgoff) << PAGE_SHIFT; - struct resource *rp; - int ret; - - rp = __pci_mmap_make_offset(dev, &offset, mmap_state); - if (rp == NULL) - return -EINVAL; - - vma->vm_pgoff = offset >> PAGE_SHIFT; - if (write_combine) - vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot); - else - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - - ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, vma->vm_page_prot); - - return ret; -} - /* This provides legacy IO read access on a bus */ int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t size) { -- cgit v1.2.3 From 46e15a2a71e5c6252859e4ade38b7fdbb897a7a1 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 19 Feb 2018 13:02:33 +0000 Subject: xtensa/PCI: Use generic pci_mmap_resource_range() Commit f719582435 ("PCI: Add pci_mmap_resource_range() and use it for ARM64") added this generic function with the intent of using it everywhere and ultimately killing the old arch-specific implementations. Remove the xtensa-specific pci_mmap_page_range() and use the generic pci_mmap_resource_range() instead. Xtensa can mmap I/O port space, so supply the xtensa-specific pci_iobar_pfn() required to make that work. Signed-off-by: David Woodhouse [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas Acked-by: Max Filippov --- arch/xtensa/include/asm/pci.h | 7 ++-- arch/xtensa/kernel/pci.c | 94 ++++--------------------------------------- 2 files changed, 12 insertions(+), 89 deletions(-) diff --git a/arch/xtensa/include/asm/pci.h b/arch/xtensa/include/asm/pci.h index 5c83798e3b2e..d5a82153a7c5 100644 --- a/arch/xtensa/include/asm/pci.h +++ b/arch/xtensa/include/asm/pci.h @@ -44,9 +44,10 @@ extern struct pci_controller* pcibios_alloc_controller(void); #define PCI_DMA_BUS_IS_PHYS (1) -/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ -#define HAVE_PCI_MMAP 1 -#define arch_can_pci_mmap_io() 1 +/* Tell PCI code what kind of PCI resource mappings we support */ +#define HAVE_PCI_MMAP 1 +#define ARCH_GENERIC_PCI_MMAP_RESOURCE 1 +#define arch_can_pci_mmap_io() 1 #endif /* __KERNEL__ */ diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c index d981f01c8d89..b7c7a60c7000 100644 --- a/arch/xtensa/kernel/pci.c +++ b/arch/xtensa/kernel/pci.c @@ -39,7 +39,6 @@ * pcibios_align_resource * pcibios_fixup_bus * pci_bus_add_device - * pci_mmap_page_range */ struct pci_controller* pci_ctrl_head; @@ -258,98 +257,21 @@ pci_controller_num(struct pci_dev *dev) #endif /* CONFIG_PROC_FS */ /* - * Platform support for /proc/bus/pci/X/Y mmap()s, - * modelled on the sparc64 implementation by Dave Miller. + * Platform support for /proc/bus/pci/X/Y mmap()s. * -- paulus. */ -/* - * Adjust vm_pgoff of VMA such that it is the physical page offset - * corresponding to the 32-bit pci bus offset for DEV requested by the user. - * - * Basically, the user finds the base address for his device which he wishes - * to mmap. They read the 32-bit value from the config space base register, - * add whatever PAGE_SIZE multiple offset they wish, and feed this into the - * offset parameter of mmap on /proc/bus/pci/XXX for that device. - * - * Returns negative error code on failure, zero on success. - */ -static __inline__ int -__pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state) +int pci_iobar_pfn(struct pci_dev *pdev, int bar, struct vm_area_struct *vma) { - struct pci_controller *pci_ctrl = (struct pci_controller*) dev->sysdata; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - unsigned long io_offset = 0; - int i, res_bit; + struct pci_controller *pci_ctrl = (struct pci_controller*) pdev->sysdata; + resource_size_t ioaddr = pci_resource_start(pdev, bar); if (pci_ctrl == 0) return -EINVAL; /* should never happen */ - /* If memory, add on the PCI bridge address offset */ - if (mmap_state == pci_mmap_mem) { - res_bit = IORESOURCE_MEM; - } else { - io_offset = (unsigned long)pci_ctrl->io_space.base; - offset += io_offset; - res_bit = IORESOURCE_IO; - } - - /* - * Check that the offset requested corresponds to one of the - * resources of the device. - */ - for (i = 0; i <= PCI_ROM_RESOURCE; i++) { - struct resource *rp = &dev->resource[i]; - int flags = rp->flags; - - /* treat ROM as memory (should be already) */ - if (i == PCI_ROM_RESOURCE) - flags |= IORESOURCE_MEM; - - /* Active and same type? */ - if ((flags & res_bit) == 0) - continue; - - /* In the range of this resource? */ - if (offset < (rp->start & PAGE_MASK) || offset > rp->end) - continue; - - /* found it! construct the final physical address */ - if (mmap_state == pci_mmap_io) - offset += pci_ctrl->io_space.start - io_offset; - vma->vm_pgoff = offset >> PAGE_SHIFT; - return 0; - } - - return -EINVAL; -} + /* Convert to an offset within this PCI controller */ + ioaddr -= (unsigned long)pci_ctrl->io_space.base; -/* - * Perform the actual remap of the pages for a PCI device mapping, as - * appropriate for this architecture. The region in the process to map - * is described by vm_start and vm_end members of VMA, the base physical - * address is found in vm_pgoff. - * The pci device structure is provided so that architectures may make mapping - * decisions on a per-device or per-bus basis. - * - * Returns a negative error code on failure, zero on success. - */ -int pci_mmap_page_range(struct pci_dev *dev, int bar, - struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, - int write_combine) -{ - int ret; - - ret = __pci_mmap_make_offset(dev, vma, mmap_state); - if (ret < 0) - return ret; - - vma->vm_page_prot = pgprot_device(vma->vm_page_prot); - - ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start,vma->vm_page_prot); - - return ret; + vma->vm_pgoff += (ioaddr + pci_ctrl->io_space.start) >> PAGE_SHIFT; + return 0; } -- cgit v1.2.3 From 36cc14ac14c0d49d33820a82dab52a7edc802fef Mon Sep 17 00:00:00 2001 From: Rolf Evers-Fischer Date: Wed, 28 Feb 2018 18:32:18 +0100 Subject: PCI: endpoint: Simplify name allocation for EPF device This commit replaces allocating and freeing the intermediate 'buf'/'func_name' with a combination of 'kstrndup()' and 'len'. 'len' is the required length of 'epf->name'. 'epf->name' should be either the first part of 'name' preceding the '.' or the complete 'name', if there is no '.' in the name. Signed-off-by: Rolf Evers-Fischer Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I Reviewed-by: Andy Shevchenko --- drivers/pci/endpoint/pci-epf-core.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 766ce1dca2ec..1f2506f32bb9 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -200,8 +200,7 @@ struct pci_epf *pci_epf_create(const char *name) int ret; struct pci_epf *epf; struct device *dev; - char *func_name; - char *buf; + int len; epf = kzalloc(sizeof(*epf), GFP_KERNEL); if (!epf) { @@ -209,20 +208,11 @@ struct pci_epf *pci_epf_create(const char *name) goto err_ret; } - buf = kstrdup(name, GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto free_epf; - } - - func_name = buf; - buf = strchrnul(buf, '.'); - *buf = '\0'; - - epf->name = kstrdup(func_name, GFP_KERNEL); + len = strchrnul(name, '.') - name; + epf->name = kstrndup(name, len, GFP_KERNEL); if (!epf->name) { ret = -ENOMEM; - goto free_func_name; + goto free_epf; } dev = &epf->dev; @@ -238,16 +228,12 @@ struct pci_epf *pci_epf_create(const char *name) if (ret) goto put_dev; - kfree(func_name); return epf; put_dev: put_device(dev); kfree(epf->name); -free_func_name: - kfree(func_name); - free_epf: kfree(epf); -- cgit v1.2.3 From 9eef6a5c3b0bf90eb292d462ea267bcb6ad1c334 Mon Sep 17 00:00:00 2001 From: Rolf Evers-Fischer Date: Wed, 28 Feb 2018 18:32:19 +0100 Subject: PCI: endpoint: Fix kernel panic after put_device() 'put_device()' calls the relase function 'pci_epf_dev_release()', which already frees 'epf->name' and 'epf'. Therefore we must not free them again after 'put_device()'. Fixes: 5e8cb4033807 ("PCI: endpoint: Add EP core layer to enable EP controller and EP functions") Signed-off-by: Rolf Evers-Fischer Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I Reviewed-by: Andy Shevchenko --- drivers/pci/endpoint/pci-epf-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 1f2506f32bb9..1878a6776519 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -232,7 +232,7 @@ struct pci_epf *pci_epf_create(const char *name) put_dev: put_device(dev); - kfree(epf->name); + return ERR_PTR(ret); free_epf: kfree(epf); -- cgit v1.2.3 From 50ee106137f2947958b2931de853010582e9d719 Mon Sep 17 00:00:00 2001 From: Rolf Evers-Fischer Date: Wed, 28 Feb 2018 18:32:20 +0100 Subject: PCI: endpoint: Remove goto labels in pci_epf_create() Remove the pci_epf_create() goto labels completely and handle the errors at the respective call site to simplify the function error handling. Signed-off-by: Rolf Evers-Fischer Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/pci-epf-core.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 1878a6776519..59ed29e550e9 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -203,16 +203,14 @@ struct pci_epf *pci_epf_create(const char *name) int len; epf = kzalloc(sizeof(*epf), GFP_KERNEL); - if (!epf) { - ret = -ENOMEM; - goto err_ret; - } + if (!epf) + return ERR_PTR(-ENOMEM); len = strchrnul(name, '.') - name; epf->name = kstrndup(name, len, GFP_KERNEL); if (!epf->name) { - ret = -ENOMEM; - goto free_epf; + kfree(epf); + return ERR_PTR(-ENOMEM); } dev = &epf->dev; @@ -221,24 +219,18 @@ struct pci_epf *pci_epf_create(const char *name) dev->type = &pci_epf_type; ret = dev_set_name(dev, "%s", name); - if (ret) - goto put_dev; + if (ret) { + put_device(dev); + return ERR_PTR(ret); + } ret = device_add(dev); - if (ret) - goto put_dev; + if (ret) { + put_device(dev); + return ERR_PTR(ret); + } return epf; - -put_dev: - put_device(dev); - return ERR_PTR(ret); - -free_epf: - kfree(epf); - -err_ret: - return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(pci_epf_create); -- cgit v1.2.3 From af7b9b7983c4bae7ca931f3d18c10ae572b5673c Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Wed, 14 Feb 2018 11:27:58 +0800 Subject: dt-bindings: PCI: MediaTek: fix dtc warnings dtc recently added PCI bus checks. Fix these warnings: Warning (pci_bridge): Node /pcie@1a140000/pcie@0,0 missing bus-range for PCI bridge Warning (pci_bridge): Node /pcie@1a140000/pcie@1,0 missing bus-range for PCI bridge Warning (pci_bridge): Node /pcie@1a140000/pcie@2,0 missing bus-range for PCI bridge Warning (unit_address_format): Failed prerequisite 'pci_bridge' Warning (pci_device_reg): Failed prerequisite 'pci_bridge' Warning (pci_device_bus_num): Failed prerequisite 'pci_bridge' Signed-off-by: Ryder Lee Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- Documentation/devicetree/bindings/pci/mediatek-pcie.txt | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie.txt b/Documentation/devicetree/bindings/pci/mediatek-pcie.txt index 3a6ce55dd310..20227a875ac8 100644 --- a/Documentation/devicetree/bindings/pci/mediatek-pcie.txt +++ b/Documentation/devicetree/bindings/pci/mediatek-pcie.txt @@ -78,7 +78,7 @@ Examples for MT7623: #reset-cells = <1>; }; - pcie: pcie-controller@1a140000 { + pcie: pcie@1a140000 { compatible = "mediatek,mt7623-pcie"; device_type = "pci"; reg = <0 0x1a140000 0 0x1000>, /* PCIe shared registers */ @@ -111,7 +111,6 @@ Examples for MT7623: 0x83000000 0 0x60000000 0 0x60000000 0 0x10000000>; /* memory space */ pcie@0,0 { - device_type = "pci"; reg = <0x0000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; @@ -123,7 +122,6 @@ Examples for MT7623: }; pcie@1,0 { - device_type = "pci"; reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; @@ -135,7 +133,6 @@ Examples for MT7623: }; pcie@2,0 { - device_type = "pci"; reg = <0x1000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; @@ -148,6 +145,7 @@ Examples for MT7623: }; Examples for MT2712: + pcie: pcie@11700000 { compatible = "mediatek,mt2712-pcie"; device_type = "pci"; @@ -169,7 +167,6 @@ Examples for MT2712: ranges = <0x82000000 0 0x20000000 0x0 0x20000000 0 0x10000000>; pcie0: pcie@0,0 { - device_type = "pci"; reg = <0x0000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; @@ -189,7 +186,6 @@ Examples for MT2712: }; pcie1: pcie@1,0 { - device_type = "pci"; reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; @@ -210,6 +206,7 @@ Examples for MT2712: }; Examples for MT7622: + pcie: pcie@1a140000 { compatible = "mediatek,mt7622-pcie"; device_type = "pci"; @@ -243,7 +240,6 @@ Examples for MT7622: ranges = <0x82000000 0 0x20000000 0x0 0x20000000 0 0x10000000>; pcie0: pcie@0,0 { - device_type = "pci"; reg = <0x0000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; @@ -263,7 +259,6 @@ Examples for MT7622: }; pcie1: pcie@1,0 { - device_type = "pci"; reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; -- cgit v1.2.3 From 9df1c6ecbf172fedb1f4f76585338860595b9bf7 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 1 Mar 2018 09:26:55 +0800 Subject: PCI: Fix NULL pointer dereference in of_pci_bus_find_domain_nr() If the "parent" pointer passed to of_pci_bus_find_domain_nr() is NULL, don't dereference it. Signed-off-by: Shawn Lin Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index f6a4dd10d9b0..b7ff5786b76b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5611,8 +5611,9 @@ static int of_pci_bus_find_domain_nr(struct device *parent) use_dt_domains = 0; domain = pci_get_new_domain_nr(); } else { - dev_err(parent, "Node %pOF has inconsistent \"linux,pci-domain\" property in DT\n", - parent->of_node); + if (parent) + pr_err("Node %pOF has ", parent->of_node); + pr_err("Inconsistent \"linux,pci-domain\" property in DT\n"); domain = -1; } -- cgit v1.2.3 From 527557a44cf21d7555082cd7e94b9dc60fc3d51a Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Wed, 28 Feb 2018 15:30:32 +0530 Subject: PCI: tegra: Free resources on probe failure tegra_pcie_probe() can fail in multiple instances, this patch takes care of freeing the resources which are allocated before probe fail. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding Tested-by: Thierry Reding --- drivers/pci/host/pci-tegra.c | 99 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 20 deletions(-) diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index dd9b3bcc41c3..a175cf682042 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -549,14 +549,25 @@ static int tegra_pcie_request_resources(struct tegra_pcie *pcie) pci_add_resource(windows, &pcie->busn); err = devm_request_pci_bus_resources(dev, windows); - if (err < 0) + if (err < 0) { + pci_free_resource_list(windows); return err; + } pci_remap_iospace(&pcie->pio, pcie->io.start); return 0; } +static void tegra_pcie_free_resources(struct tegra_pcie *pcie) +{ + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + struct list_head *windows = &host->windows; + + pci_unmap_iospace(&pcie->pio); + pci_free_resource_list(windows); +} + static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin) { struct tegra_pcie *pcie = pdev->bus->sysdata; @@ -966,24 +977,35 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) return 0; } -static void tegra_pcie_power_off(struct tegra_pcie *pcie) +static void tegra_pcie_disable_controller(struct tegra_pcie *pcie) { - struct device *dev = pcie->dev; - const struct tegra_pcie_soc *soc = pcie->soc; int err; - /* TODO: disable and unprepare clocks? */ + reset_control_assert(pcie->pcie_xrst); - if (soc->program_uphy) { + if (pcie->soc->program_uphy) { err = tegra_pcie_phy_power_off(pcie); if (err < 0) - dev_err(dev, "failed to power off PHY(s): %d\n", err); + dev_err(pcie->dev, "failed to power off PHY(s): %d\n", + err); } +} + +static void tegra_pcie_power_off(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + const struct tegra_pcie_soc *soc = pcie->soc; + int err; - reset_control_assert(pcie->pcie_xrst); reset_control_assert(pcie->afi_rst); reset_control_assert(pcie->pex_rst); + clk_disable_unprepare(pcie->pll_e); + if (soc->has_cml_clk) + clk_disable_unprepare(pcie->cml_clk); + clk_disable_unprepare(pcie->afi_clk); + clk_disable_unprepare(pcie->pex_clk); + if (!dev->pm_domain) tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); @@ -1192,6 +1214,30 @@ static int tegra_pcie_phys_get(struct tegra_pcie *pcie) return 0; } +static void tegra_pcie_phys_put(struct tegra_pcie *pcie) +{ + struct tegra_pcie_port *port; + struct device *dev = pcie->dev; + int err, i; + + if (pcie->legacy_phy) { + err = phy_exit(pcie->phy); + if (err < 0) + dev_err(dev, "failed to teardown PHY: %d\n", err); + return; + } + + list_for_each_entry(port, &pcie->ports, list) { + for (i = 0; i < port->lanes; i++) { + err = phy_exit(port->phys[i]); + if (err < 0) + dev_err(dev, "failed to teardown PHY#%u: %d\n", + i, err); + } + } +} + + static int tegra_pcie_get_resources(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; @@ -1223,7 +1269,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) err = tegra_pcie_power_on(pcie); if (err) { dev_err(dev, "failed to power up: %d\n", err); - return err; + goto phys_put; } pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads"); @@ -1275,6 +1321,9 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) return 0; +phys_put: + if (soc->program_uphy) + tegra_pcie_phys_put(pcie); poweroff: tegra_pcie_power_off(pcie); return err; @@ -1282,20 +1331,15 @@ poweroff: static int tegra_pcie_put_resources(struct tegra_pcie *pcie) { - struct device *dev = pcie->dev; const struct tegra_pcie_soc *soc = pcie->soc; - int err; if (pcie->irq > 0) free_irq(pcie->irq, pcie); tegra_pcie_power_off(pcie); - if (soc->program_uphy) { - err = phy_exit(pcie->phy); - if (err < 0) - dev_err(dev, "failed to teardown PHY: %d\n", err); - } + if (soc->program_uphy) + tegra_pcie_phys_put(pcie); return 0; } @@ -2035,6 +2079,16 @@ static void tegra_pcie_enable_ports(struct tegra_pcie *pcie) } } +static void tegra_pcie_disable_ports(struct tegra_pcie *pcie) +{ + struct tegra_pcie_port *port, *tmp; + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + tegra_pcie_port_disable(port); + tegra_pcie_port_free(port); + } +} + static const struct tegra_pcie_soc tegra20_pcie = { .num_ports = 2, .msi_base_shift = 0, @@ -2265,7 +2319,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) err = tegra_pcie_request_resources(pcie); if (err) - goto put_resources; + goto disable_controller; /* setup the AFI address translations */ tegra_pcie_setup_translations(pcie); @@ -2274,7 +2328,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) err = tegra_pcie_enable_msi(pcie); if (err < 0) { dev_err(dev, "failed to enable MSI support: %d\n", err); - goto put_resources; + goto free_resources; } } @@ -2289,7 +2343,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) err = pci_scan_root_bus_bridge(host); if (err < 0) { dev_err(dev, "failed to register host: %d\n", err); - goto disable_msi; + goto disable_ports; } pci_bus_size_bridges(host->bus); @@ -2308,9 +2362,14 @@ static int tegra_pcie_probe(struct platform_device *pdev) return 0; -disable_msi: +disable_ports: + tegra_pcie_disable_ports(pcie); if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_disable_msi(pcie); +free_resources: + tegra_pcie_free_resources(pcie); +disable_controller: + tegra_pcie_disable_controller(pcie); put_resources: tegra_pcie_put_resources(pcie); return err; -- cgit v1.2.3 From 662b94c3195654c225174c680094555c0d750d41 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Wed, 28 Feb 2018 15:30:33 +0530 Subject: PCI: tegra: Add loadable kernel module support Implement remove callback function for Tegra PCIe driver to add loadable kernel module support. Per PCIe r3.0, sec 5.3.3.2.1, PCIe root port should broadcast PME_Turn_Off message before PCIe link goes to L2. PME_Turn_Off broadcast mechanism is implemented in AFI module. Each Tegra PCIe root port has its own PME_Turn_Off and PME_TO_Ack bitmap in AFI_PME register, program this register to broadcast PME_Turn_Off message. Once PME_TO_Ack is recieved driver will turn OFF PCIe clock, power gate PCIe partition and turn OFF regulators. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding Tested-by: Thierry Reding --- drivers/pci/host/pci-tegra.c | 103 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 3 deletions(-) diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index a175cf682042..ab057f6f5153 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -18,10 +18,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -139,6 +141,8 @@ #define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) #define AFI_INTR_EN_PRSNT_SENSE (1 << 8) +#define AFI_PCIE_PME 0xf0 + #define AFI_PCIE_CONFIG 0x0f8 #define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) #define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe @@ -219,6 +223,8 @@ #define PADS_REFCLK_CFG_PREDI_SHIFT 8 /* 11:8 */ #define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */ +#define PME_ACK_TIMEOUT 10000 + struct tegra_msi { struct msi_controller chip; DECLARE_BITMAP(used, INT_PCI_MSI_NR); @@ -230,8 +236,16 @@ struct tegra_msi { }; /* used to differentiate between Tegra SoC generations */ +struct tegra_pcie_port_soc { + struct { + u8 turnoff_bit; + u8 ack_bit; + } pme; +}; + struct tegra_pcie_soc { unsigned int num_ports; + const struct tegra_pcie_port_soc *ports; unsigned int msi_base_shift; u32 pads_pll_ctl; u32 tx_ref_sel; @@ -1344,6 +1358,32 @@ static int tegra_pcie_put_resources(struct tegra_pcie *pcie) return 0; } +static void tegra_pcie_pme_turnoff(struct tegra_pcie_port *port) +{ + struct tegra_pcie *pcie = port->pcie; + const struct tegra_pcie_soc *soc = pcie->soc; + int err; + u32 val; + u8 ack_bit; + + val = afi_readl(pcie, AFI_PCIE_PME); + val |= (0x1 << soc->ports[port->index].pme.turnoff_bit); + afi_writel(pcie, val, AFI_PCIE_PME); + + ack_bit = soc->ports[port->index].pme.ack_bit; + err = readl_poll_timeout(pcie->afi + AFI_PCIE_PME, val, + val & (0x1 << ack_bit), 1, PME_ACK_TIMEOUT); + if (err) + dev_err(pcie->dev, "PME Ack is not received on port: %d\n", + port->index); + + usleep_range(10000, 11000); + + val = afi_readl(pcie, AFI_PCIE_PME); + val &= ~(0x1 << soc->ports[port->index].pme.turnoff_bit); + afi_writel(pcie, val, AFI_PCIE_PME); +} + static int tegra_msi_alloc(struct tegra_msi *chip) { int msi; @@ -2089,8 +2129,14 @@ static void tegra_pcie_disable_ports(struct tegra_pcie *pcie) } } +static const struct tegra_pcie_port_soc tegra20_pcie_ports[] = { + { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, + { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, +}; + static const struct tegra_pcie_soc tegra20_pcie = { .num_ports = 2, + .ports = tegra20_pcie_ports, .msi_base_shift = 0, .pads_pll_ctl = PADS_PLL_CTL_TEGRA20, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10, @@ -2104,8 +2150,15 @@ static const struct tegra_pcie_soc tegra20_pcie = { .program_uphy = true, }; +static const struct tegra_pcie_port_soc tegra30_pcie_ports[] = { + { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, + { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, + { .pme.turnoff_bit = 16, .pme.ack_bit = 18 }, +}; + static const struct tegra_pcie_soc tegra30_pcie = { .num_ports = 3, + .ports = tegra30_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, @@ -2122,6 +2175,7 @@ static const struct tegra_pcie_soc tegra30_pcie = { static const struct tegra_pcie_soc tegra124_pcie = { .num_ports = 2, + .ports = tegra20_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, @@ -2137,6 +2191,7 @@ static const struct tegra_pcie_soc tegra124_pcie = { static const struct tegra_pcie_soc tegra210_pcie = { .num_ports = 2, + .ports = tegra20_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, @@ -2150,8 +2205,15 @@ static const struct tegra_pcie_soc tegra210_pcie = { .program_uphy = true, }; +static const struct tegra_pcie_port_soc tegra186_pcie_ports[] = { + { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, + { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, + { .pme.turnoff_bit = 12, .pme.ack_bit = 14 }, +}; + static const struct tegra_pcie_soc tegra186_pcie = { .num_ports = 3, + .ports = tegra186_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, @@ -2263,6 +2325,12 @@ static const struct file_operations tegra_pcie_ports_ops = { .release = seq_release, }; +static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie) +{ + debugfs_remove_recursive(pcie->debugfs); + pcie->debugfs = NULL; +} + static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) { struct dentry *file; @@ -2279,8 +2347,7 @@ static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) return 0; remove: - debugfs_remove_recursive(pcie->debugfs); - pcie->debugfs = NULL; + tegra_pcie_debugfs_exit(pcie); return -ENOMEM; } @@ -2298,6 +2365,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) pcie = pci_host_bridge_priv(host); host->sysdata = pcie; + platform_set_drvdata(pdev, pcie); pcie->soc = of_device_get_match_data(dev); INIT_LIST_HEAD(&pcie->ports); @@ -2375,6 +2443,33 @@ put_resources: return err; } +static int tegra_pcie_remove(struct platform_device *pdev) +{ + struct tegra_pcie *pcie = platform_get_drvdata(pdev); + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + struct tegra_pcie_port *port; + + if (IS_ENABLED(CONFIG_DEBUG_FS)) + tegra_pcie_debugfs_exit(pcie); + + pci_stop_root_bus(host->bus); + pci_remove_root_bus(host->bus); + + list_for_each_entry(port, &pcie->ports, list) + tegra_pcie_pme_turnoff(port); + + tegra_pcie_disable_ports(pcie); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + tegra_pcie_disable_msi(pcie); + + tegra_pcie_free_resources(pcie); + tegra_pcie_disable_controller(pcie); + tegra_pcie_put_resources(pcie); + + return 0; +} + static struct platform_driver tegra_pcie_driver = { .driver = { .name = "tegra-pcie", @@ -2382,5 +2477,7 @@ static struct platform_driver tegra_pcie_driver = { .suppress_bind_attrs = true, }, .probe = tegra_pcie_probe, + .remove = tegra_pcie_remove, }; -builtin_platform_driver(tegra_pcie_driver); +module_platform_driver(tegra_pcie_driver); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From abbcf0e2a99d55433b2ee44794e6f875fc36aae2 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 27 Feb 2018 14:14:10 -0600 Subject: PCI: Wait for device to become ready after a power management reset PCIe r4.0, sec 2.3.1, Request Handling Rules, indicates that a device can return CRS Completion Status following a D3hot to D0 transition. Wait until the device becomes ready in that situation. Signed-off-by: Sinan Kaya Signed-off-by: Bjorn Helgaas Reviewed-by: Christoph Hellwig --- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 9493b97436c3..a3042e475901 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4188,7 +4188,7 @@ static int pci_pm_reset(struct pci_dev *dev, int probe) pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr); pci_dev_d3_sleep(dev); - return 0; + return pci_dev_wait(dev, "PM D3->D0", PCIE_RESET_READY_POLL_MS); } void pci_reset_secondary_bus(struct pci_dev *dev) -- cgit v1.2.3 From 01fd61c0b9bd85ab41fb60fbd781d44882ee6887 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 27 Feb 2018 14:14:11 -0600 Subject: PCI: Add a return type for pci_reset_bridge_secondary_bus() Add a return value to pci_reset_bridge_secondary_bus() so we can return an error if the device doesn't become ready after the reset. Signed-off-by: Sinan Kaya Signed-off-by: Bjorn Helgaas Reviewed-by: Christoph Hellwig --- drivers/pci/pci.c | 4 +++- include/linux/pci.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a3042e475901..dde40506ffe5 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4229,9 +4229,11 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) * Use the bridge control register to assert reset on the secondary bus. * Devices on the secondary bus are left in power-on state. */ -void pci_reset_bridge_secondary_bus(struct pci_dev *dev) +int pci_reset_bridge_secondary_bus(struct pci_dev *dev) { pcibios_reset_secondary_bus(dev); + + return 0; } EXPORT_SYMBOL_GPL(pci_reset_bridge_secondary_bus); diff --git a/include/linux/pci.h b/include/linux/pci.h index af75d9d76189..562875d34b98 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1095,7 +1095,7 @@ int pci_reset_bus(struct pci_bus *bus); int pci_try_reset_bus(struct pci_bus *bus); void pci_reset_secondary_bus(struct pci_dev *dev); void pcibios_reset_secondary_bus(struct pci_dev *dev); -void pci_reset_bridge_secondary_bus(struct pci_dev *dev); +int pci_reset_bridge_secondary_bus(struct pci_dev *dev); void pci_update_resource(struct pci_dev *dev, int resno); int __must_check pci_assign_resource(struct pci_dev *dev, int i); int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align); -- cgit v1.2.3 From 6b2f1351af567110cec80d7c067314c633a14f50 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 27 Feb 2018 14:14:12 -0600 Subject: PCI: Wait for device to become ready after secondary bus reset Setting Secondary Bus Reset of a downstream port sends a hot reset. PCIe r4.0, sec 2.3.1, Request Handling Rules, indicates that a device can return CRS Completion Status following such a reset. Wait until the device becomes ready in that situation. Signed-off-by: Sinan Kaya Signed-off-by: Bjorn Helgaas Reviewed-by: Christoph Hellwig --- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index dde40506ffe5..0b8e8ee84bbc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4233,7 +4233,7 @@ int pci_reset_bridge_secondary_bus(struct pci_dev *dev) { pcibios_reset_secondary_bus(dev); - return 0; + return pci_dev_wait(dev, "bus reset", PCIE_RESET_READY_POLL_MS); } EXPORT_SYMBOL_GPL(pci_reset_bridge_secondary_bus); -- cgit v1.2.3 From be20f6b063f51cd77d071b803ee24f23200dc559 Mon Sep 17 00:00:00 2001 From: KarimAllah Ahmed Date: Wed, 17 Jan 2018 19:30:29 +0100 Subject: PCI/IOV: Skip INTx config reads for VFs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per PCIe r4.0, sec 9.2.1.4, VFs can not implement INTX, and their Interrupt Line and Interrupt Pin registers must be RO Zero. Some devices have thousands of VFs, so skip reading the registers as an optimization. Signed-off-by: KarimAllah Ahmed Signed-off-by: Jan H. Schönherr [bhelgaas: changelog, comment] Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 489660d0d384..a1cddca37793 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1230,6 +1230,13 @@ static void pci_read_irq(struct pci_dev *dev) { unsigned char irq; + /* VFs are not allowed to use INTx, so skip the config reads */ + if (dev->is_virtfn) { + dev->pin = 0; + dev->irq = 0; + return; + } + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq); dev->pin = irq; if (irq) -- cgit v1.2.3 From 7c5925afbc58c6d6b384e1dc051bb992969bf787 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Tue, 6 Mar 2018 11:54:53 +0000 Subject: PCI: dwc: Move MSI IRQs allocation to IRQ domains hierarchical API Implement a multiplexed IRQ domain hierarchy API in the pcie-designware host bridge driver that funnels all MSI IRQs into a single parent interrupt, moving away from the obsolete struct msi_controller based API. Although the old implementation API is still available, pcie-designware will now use the multiplexed IRQ domains hierarchical API. Remove all existing dwc based host bridges MSI IRQs handlers, in that the hierarchical API now handles MSI IRQs through the hierarchical/chained MSI domain implementation. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Tested-by: Niklas Cassel Tested-by: Shawn Guo Acked-by: Jingoo Han Acked-by: Marc Zyngier --- drivers/pci/dwc/pci-exynos.c | 18 -- drivers/pci/dwc/pci-imx6.c | 18 -- drivers/pci/dwc/pci-keystone-dw.c | 89 +--------- drivers/pci/dwc/pci-keystone.c | 1 + drivers/pci/dwc/pci-keystone.h | 1 + drivers/pci/dwc/pcie-artpec6.c | 18 -- drivers/pci/dwc/pcie-designware-host.c | 294 +++++++++++++++++++++++++++++---- drivers/pci/dwc/pcie-designware-plat.c | 16 -- drivers/pci/dwc/pcie-designware.h | 18 ++ drivers/pci/dwc/pcie-histb.c | 15 -- drivers/pci/dwc/pcie-qcom.c | 16 -- 11 files changed, 290 insertions(+), 214 deletions(-) diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-exynos.c index ca6278113936..4cc1e5df8c79 100644 --- a/drivers/pci/dwc/pci-exynos.c +++ b/drivers/pci/dwc/pci-exynos.c @@ -294,15 +294,6 @@ static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) return IRQ_HANDLED; } -static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg) -{ - struct exynos_pcie *ep = arg; - struct dw_pcie *pci = ep->pci; - struct pcie_port *pp = &pci->pp; - - return dw_handle_msi_irq(pp); -} - static void exynos_pcie_msi_init(struct exynos_pcie *ep) { struct dw_pcie *pci = ep->pci; @@ -428,15 +419,6 @@ static int __init exynos_add_pcie_port(struct exynos_pcie *ep, dev_err(dev, "failed to get msi irq\n"); return pp->msi_irq; } - - ret = devm_request_irq(dev, pp->msi_irq, - exynos_pcie_msi_irq_handler, - IRQF_SHARED | IRQF_NO_THREAD, - "exynos-pcie", ep); - if (ret) { - dev_err(dev, "failed to request msi irq\n"); - return ret; - } } pp->root_bus_nr = -1; diff --git a/drivers/pci/dwc/pci-imx6.c b/drivers/pci/dwc/pci-imx6.c index 4fddbd08b089..4818ef875f8a 100644 --- a/drivers/pci/dwc/pci-imx6.c +++ b/drivers/pci/dwc/pci-imx6.c @@ -542,15 +542,6 @@ static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie) return -EINVAL; } -static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg) -{ - struct imx6_pcie *imx6_pcie = arg; - struct dw_pcie *pci = imx6_pcie->pci; - struct pcie_port *pp = &pci->pp; - - return dw_handle_msi_irq(pp); -} - static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) { struct dw_pcie *pci = imx6_pcie->pci; @@ -674,15 +665,6 @@ static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, dev_err(dev, "failed to get MSI irq\n"); return -ENODEV; } - - ret = devm_request_irq(dev, pp->msi_irq, - imx6_pcie_msi_handler, - IRQF_SHARED | IRQF_NO_THREAD, - "mx6-pcie-msi", imx6_pcie); - if (ret) { - dev_err(dev, "failed to request MSI irq\n"); - return ret; - } } pp->root_bus_nr = -1; diff --git a/drivers/pci/dwc/pci-keystone-dw.c b/drivers/pci/dwc/pci-keystone-dw.c index 99a0e7076221..86e613afb019 100644 --- a/drivers/pci/dwc/pci-keystone-dw.c +++ b/drivers/pci/dwc/pci-keystone-dw.c @@ -120,20 +120,15 @@ void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset) } } -static void ks_dw_pcie_msi_irq_ack(struct irq_data *d) +void ks_dw_pcie_msi_irq_ack(int irq, struct pcie_port *pp) { - u32 offset, reg_offset, bit_pos; + u32 reg_offset, bit_pos; struct keystone_pcie *ks_pcie; - struct msi_desc *msi; - struct pcie_port *pp; struct dw_pcie *pci; - msi = irq_data_get_msi_desc(d); - pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi); pci = to_dw_pcie_from_pp(pp); ks_pcie = to_keystone_pcie(pci); - offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); - update_reg_offset_bit_pos(offset, ®_offset, &bit_pos); + update_reg_offset_bit_pos(irq, ®_offset, &bit_pos); ks_dw_app_writel(ks_pcie, MSI0_IRQ_STATUS + (reg_offset << 4), BIT(bit_pos)); @@ -162,85 +157,9 @@ void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) BIT(bit_pos)); } -static void ks_dw_pcie_msi_irq_mask(struct irq_data *d) -{ - struct msi_desc *msi; - struct pcie_port *pp; - u32 offset; - - msi = irq_data_get_msi_desc(d); - pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi); - offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); - - /* Mask the end point if PVM implemented */ - if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (msi->msi_attrib.maskbit) - pci_msi_mask_irq(d); - } - - ks_dw_pcie_msi_clear_irq(pp, offset); -} - -static void ks_dw_pcie_msi_irq_unmask(struct irq_data *d) -{ - struct msi_desc *msi; - struct pcie_port *pp; - u32 offset; - - msi = irq_data_get_msi_desc(d); - pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi); - offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); - - /* Mask the end point if PVM implemented */ - if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (msi->msi_attrib.maskbit) - pci_msi_unmask_irq(d); - } - - ks_dw_pcie_msi_set_irq(pp, offset); -} - -static struct irq_chip ks_dw_pcie_msi_irq_chip = { - .name = "Keystone-PCIe-MSI-IRQ", - .irq_ack = ks_dw_pcie_msi_irq_ack, - .irq_mask = ks_dw_pcie_msi_irq_mask, - .irq_unmask = ks_dw_pcie_msi_irq_unmask, -}; - -static int ks_dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &ks_dw_pcie_msi_irq_chip, - handle_level_irq); - irq_set_chip_data(irq, domain->host_data); - - return 0; -} - -static const struct irq_domain_ops ks_dw_pcie_msi_domain_ops = { - .map = ks_dw_pcie_msi_map, -}; - int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip) { - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); - struct device *dev = pci->dev; - int i; - - pp->irq_domain = irq_domain_add_linear(ks_pcie->msi_intc_np, - MAX_MSI_IRQS, - &ks_dw_pcie_msi_domain_ops, - chip); - if (!pp->irq_domain) { - dev_err(dev, "irq domain init failed\n"); - return -ENXIO; - } - - for (i = 0; i < MAX_MSI_IRQS; i++) - irq_create_mapping(pp->irq_domain, i); - - return 0; + return dw_pcie_allocate_domains(pp); } void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie) diff --git a/drivers/pci/dwc/pci-keystone.c b/drivers/pci/dwc/pci-keystone.c index d4f8ab90c018..d55ae0716adf 100644 --- a/drivers/pci/dwc/pci-keystone.c +++ b/drivers/pci/dwc/pci-keystone.c @@ -297,6 +297,7 @@ static const struct dw_pcie_host_ops keystone_pcie_host_ops = { .msi_clear_irq = ks_dw_pcie_msi_clear_irq, .get_msi_addr = ks_dw_pcie_get_msi_addr, .msi_host_init = ks_dw_pcie_msi_host_init, + .msi_irq_ack = ks_dw_pcie_msi_irq_ack, .scan_bus = ks_dw_pcie_v3_65_scan_bus, }; diff --git a/drivers/pci/dwc/pci-keystone.h b/drivers/pci/dwc/pci-keystone.h index 1dd1f3ef98e7..aa504483e83a 100644 --- a/drivers/pci/dwc/pci-keystone.h +++ b/drivers/pci/dwc/pci-keystone.h @@ -49,6 +49,7 @@ int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val); void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie); void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie); +void ks_dw_pcie_msi_irq_ack(int i, struct pcie_port *pp); void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq); void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq); void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp); diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c index 93b3df9ed1b5..e66cede2b5b7 100644 --- a/drivers/pci/dwc/pcie-artpec6.c +++ b/drivers/pci/dwc/pcie-artpec6.c @@ -383,15 +383,6 @@ static const struct dw_pcie_host_ops artpec6_pcie_host_ops = { .host_init = artpec6_pcie_host_init, }; -static irqreturn_t artpec6_pcie_msi_handler(int irq, void *arg) -{ - struct artpec6_pcie *artpec6_pcie = arg; - struct dw_pcie *pci = artpec6_pcie->pci; - struct pcie_port *pp = &pci->pp; - - return dw_handle_msi_irq(pp); -} - static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie, struct platform_device *pdev) { @@ -406,15 +397,6 @@ static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie, dev_err(dev, "failed to get MSI irq\n"); return pp->msi_irq; } - - ret = devm_request_irq(dev, pp->msi_irq, - artpec6_pcie_msi_handler, - IRQF_SHARED | IRQF_NO_THREAD, - "artpec6-pcie-msi", artpec6_pcie); - if (ret) { - dev_err(dev, "failed to request MSI irq\n"); - return ret; - } } pp->root_bus_nr = -1; diff --git a/drivers/pci/dwc/pcie-designware-host.c b/drivers/pci/dwc/pcie-designware-host.c index 8de2d5c69b1d..a28c496f58ac 100644 --- a/drivers/pci/dwc/pcie-designware-host.c +++ b/drivers/pci/dwc/pcie-designware-host.c @@ -8,6 +8,7 @@ * Author: Jingoo Han */ +#include #include #include #include @@ -50,6 +51,36 @@ static struct irq_chip dw_msi_irq_chip = { .irq_unmask = pci_msi_unmask_irq, }; +static void dw_msi_ack_irq(struct irq_data *d) +{ + irq_chip_ack_parent(d); +} + +static void dw_msi_mask_irq(struct irq_data *d) +{ + pci_msi_mask_irq(d); + irq_chip_mask_parent(d); +} + +static void dw_msi_unmask_irq(struct irq_data *d) +{ + pci_msi_unmask_irq(d); + irq_chip_unmask_parent(d); +} + +static struct irq_chip dw_pcie_msi_irq_chip = { + .name = "PCI-MSI", + .irq_ack = dw_msi_ack_irq, + .irq_mask = dw_msi_mask_irq, + .irq_unmask = dw_msi_unmask_irq, +}; + +static struct msi_domain_info dw_pcie_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), + .chip = &dw_pcie_msi_irq_chip, +}; + /* MSI int handler */ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) { @@ -78,6 +109,194 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) return ret; } +/* Chained MSI interrupt service routine */ +static void dw_chained_msi_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct pcie_port *pp; + + chained_irq_enter(chip, desc); + + pp = irq_desc_get_handler_data(desc); + dw_handle_msi_irq(pp); + + chained_irq_exit(chip, desc); +} + +static void dw_pci_setup_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + u64 msi_target; + + if (pp->ops->get_msi_addr) + msi_target = pp->ops->get_msi_addr(pp); + else + msi_target = (u64)pp->msi_data; + + msg->address_lo = lower_32_bits(msi_target); + msg->address_hi = upper_32_bits(msi_target); + + if (pp->ops->get_msi_data) + msg->data = pp->ops->get_msi_data(pp, data->hwirq); + else + msg->data = data->hwirq; + + dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n", + (int)data->hwirq, msg->address_hi, msg->address_lo); +} + +static int dw_pci_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static void dw_pci_bottom_mask(struct irq_data *data) +{ + struct pcie_port *pp = irq_data_get_irq_chip_data(data); + unsigned int res, bit, ctrl; + unsigned long flags; + + raw_spin_lock_irqsave(&pp->lock, flags); + + if (pp->ops->msi_clear_irq) { + pp->ops->msi_clear_irq(pp, data->hwirq); + } else { + ctrl = data->hwirq / 32; + res = ctrl * 12; + bit = data->hwirq % 32; + + pp->irq_status[ctrl] &= ~(1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, + pp->irq_status[ctrl]); + } + + raw_spin_unlock_irqrestore(&pp->lock, flags); +} + +static void dw_pci_bottom_unmask(struct irq_data *data) +{ + struct pcie_port *pp = irq_data_get_irq_chip_data(data); + unsigned int res, bit, ctrl; + unsigned long flags; + + raw_spin_lock_irqsave(&pp->lock, flags); + + if (pp->ops->msi_set_irq) { + pp->ops->msi_set_irq(pp, data->hwirq); + } else { + ctrl = data->hwirq / 32; + res = ctrl * 12; + bit = data->hwirq % 32; + + pp->irq_status[ctrl] |= 1 << bit; + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, + pp->irq_status[ctrl]); + } + + raw_spin_unlock_irqrestore(&pp->lock, flags); +} + +static void dw_pci_bottom_ack(struct irq_data *d) +{ + struct msi_desc *msi = irq_data_get_msi_desc(d); + struct pcie_port *pp; + + pp = msi_desc_to_pci_sysdata(msi); + + if (pp->ops->msi_irq_ack) + pp->ops->msi_irq_ack(d->hwirq, pp); +} + +static struct irq_chip dw_pci_msi_bottom_irq_chip = { + .name = "DWPCI-MSI", + .irq_ack = dw_pci_bottom_ack, + .irq_compose_msi_msg = dw_pci_setup_msi_msg, + .irq_set_affinity = dw_pci_msi_set_affinity, + .irq_mask = dw_pci_bottom_mask, + .irq_unmask = dw_pci_bottom_unmask, +}; + +static int dw_pcie_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *args) +{ + struct pcie_port *pp = domain->host_data; + unsigned long flags; + u32 i; + int bit; + + raw_spin_lock_irqsave(&pp->lock, flags); + + bit = bitmap_find_free_region(pp->msi_irq_in_use, pp->num_vectors, + order_base_2(nr_irqs)); + + raw_spin_unlock_irqrestore(&pp->lock, flags); + + if (bit < 0) + return -ENOSPC; + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, bit + i, + &dw_pci_msi_bottom_irq_chip, + pp, handle_edge_irq, + NULL, NULL); + + return 0; +} + +static void dw_pcie_irq_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *data = irq_domain_get_irq_data(domain, virq); + struct pcie_port *pp = irq_data_get_irq_chip_data(data); + unsigned long flags; + + raw_spin_lock_irqsave(&pp->lock, flags); + bitmap_release_region(pp->msi_irq_in_use, data->hwirq, + order_base_2(nr_irqs)); + raw_spin_unlock_irqrestore(&pp->lock, flags); +} + +static const struct irq_domain_ops dw_pcie_msi_domain_ops = { + .alloc = dw_pcie_irq_domain_alloc, + .free = dw_pcie_irq_domain_free, +}; + +int dw_pcie_allocate_domains(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct fwnode_handle *fwnode = of_node_to_fwnode(pci->dev->of_node); + + pp->irq_domain = irq_domain_create_linear(fwnode, pp->num_vectors, + &dw_pcie_msi_domain_ops, pp); + if (!pp->irq_domain) { + dev_err(pci->dev, "failed to create IRQ domain\n"); + return -ENOMEM; + } + + pp->msi_domain = pci_msi_create_irq_domain(fwnode, + &dw_pcie_msi_domain_info, + pp->irq_domain); + if (!pp->msi_domain) { + dev_err(pci->dev, "failed to create MSI domain\n"); + irq_domain_remove(pp->irq_domain); + return -ENOMEM; + } + + return 0; +} + +void dw_pcie_free_msi(struct pcie_port *pp) +{ + irq_set_chained_handler(pp->msi_irq, NULL); + irq_set_handler_data(pp->msi_irq, NULL); + + irq_domain_remove(pp->msi_domain); + irq_domain_remove(pp->irq_domain); +} + void dw_pcie_msi_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -96,20 +315,21 @@ void dw_pcie_msi_init(struct pcie_port *pp) /* program the msi_data */ dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4, - (u32)(msi_target & 0xffffffff)); + lower_32_bits(msi_target)); dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, - (u32)(msi_target >> 32 & 0xffffffff)); + upper_32_bits(msi_target)); } static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) { - unsigned int res, bit, val; + unsigned int res, bit, ctrl; - res = (irq / 32) * 12; + ctrl = irq / 32; + res = ctrl * 12; bit = irq % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val &= ~(1 << bit); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + pp->irq_status[ctrl] &= ~(1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, + pp->irq_status[ctrl]); } static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, @@ -131,13 +351,14 @@ static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) { - unsigned int res, bit, val; + unsigned int res, bit, ctrl; - res = (irq / 32) * 12; + ctrl = irq / 32; + res = ctrl * 12; bit = irq % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val |= 1 << bit; - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + pp->irq_status[ctrl] |= 1 << bit; + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, + pp->irq_status[ctrl]); } static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) @@ -285,11 +506,13 @@ int dw_pcie_host_init(struct pcie_port *pp) struct device *dev = pci->dev; struct device_node *np = dev->of_node; struct platform_device *pdev = to_platform_device(dev); + struct resource_entry *win, *tmp; struct pci_bus *bus, *child; struct pci_host_bridge *bridge; struct resource *cfg_res; - int i, ret; - struct resource_entry *win, *tmp; + int ret; + + raw_spin_lock_init(&pci->pp.lock); cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); if (cfg_res) { @@ -388,18 +611,33 @@ int dw_pcie_host_init(struct pcie_port *pp) pci->num_viewport = 2; if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (!pp->ops->msi_host_init) { - pp->irq_domain = irq_domain_add_linear(dev->of_node, - MAX_MSI_IRQS, &msi_domain_ops, - &dw_pcie_msi_chip); - if (!pp->irq_domain) { - dev_err(dev, "irq domain init failed\n"); - ret = -ENXIO; + /* + * If a specific SoC driver needs to change the + * default number of vectors, it needs to implement + * the set_num_vectors callback. + */ + if (!pp->ops->set_num_vectors) { + pp->num_vectors = MSI_DEF_NUM_VECTORS; + } else { + pp->ops->set_num_vectors(pp); + + if (pp->num_vectors > MAX_MSI_IRQS || + pp->num_vectors == 0) { + dev_err(dev, + "Invalid number of vectors\n"); goto error; } + } - for (i = 0; i < MAX_MSI_IRQS; i++) - irq_create_mapping(pp->irq_domain, i); + if (!pp->ops->msi_host_init) { + ret = dw_pcie_allocate_domains(pp); + if (ret) + goto error; + + if (pp->msi_irq) + irq_set_chained_handler_and_data(pp->msi_irq, + dw_chained_msi_isr, + pp); } else { ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip); if (ret < 0) @@ -421,10 +659,6 @@ int dw_pcie_host_init(struct pcie_port *pp) bridge->ops = &dw_pcie_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; - if (IS_ENABLED(CONFIG_PCI_MSI)) { - bridge->msi = &dw_pcie_msi_chip; - dw_pcie_msi_chip.dev = dev; - } ret = pci_scan_root_bus_bridge(bridge); if (ret) @@ -593,11 +827,15 @@ static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci) void dw_pcie_setup_rc(struct pcie_port *pp) { - u32 val; + u32 val, ctrl; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); dw_pcie_setup(pci); + /* Initialize IRQ Status array */ + for (ctrl = 0; ctrl < MAX_MSI_CTRLS; ctrl++) + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + (ctrl * 12), 4, + &pp->irq_status[ctrl]); /* setup RC BARs */ dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004); dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000); diff --git a/drivers/pci/dwc/pcie-designware-plat.c b/drivers/pci/dwc/pcie-designware-plat.c index ebdf28bcd67d..5416aa8a07a5 100644 --- a/drivers/pci/dwc/pcie-designware-plat.c +++ b/drivers/pci/dwc/pcie-designware-plat.c @@ -25,13 +25,6 @@ struct dw_plat_pcie { struct dw_pcie *pci; }; -static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg) -{ - struct pcie_port *pp = arg; - - return dw_handle_msi_irq(pp); -} - static int dw_plat_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -63,15 +56,6 @@ static int dw_plat_add_pcie_port(struct pcie_port *pp, pp->msi_irq = platform_get_irq(pdev, 0); if (pp->msi_irq < 0) return pp->msi_irq; - - ret = devm_request_irq(dev, pp->msi_irq, - dw_plat_pcie_msi_irq_handler, - IRQF_SHARED | IRQF_NO_THREAD, - "dw-plat-pcie-msi", pp); - if (ret) { - dev_err(dev, "failed to request MSI IRQ\n"); - return ret; - } } pp->root_bus_nr = -1; diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h index 11b13864a406..c80ee868e017 100644 --- a/drivers/pci/dwc/pcie-designware.h +++ b/drivers/pci/dwc/pcie-designware.h @@ -114,6 +114,7 @@ */ #define MAX_MSI_IRQS 32 #define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32) +#define MSI_DEF_NUM_VECTORS 32 /* Maximum number of inbound/outbound iATUs */ #define MAX_IATU_IN 256 @@ -149,7 +150,9 @@ struct dw_pcie_host_ops { phys_addr_t (*get_msi_addr)(struct pcie_port *pp); u32 (*get_msi_data)(struct pcie_port *pp, int pos); void (*scan_bus)(struct pcie_port *pp); + void (*set_num_vectors)(struct pcie_port *pp); int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip); + void (*msi_irq_ack)(int irq, struct pcie_port *pp); }; struct pcie_port { @@ -174,7 +177,11 @@ struct pcie_port { const struct dw_pcie_host_ops *ops; int msi_irq; struct irq_domain *irq_domain; + struct irq_domain *msi_domain; dma_addr_t msi_data; + u32 num_vectors; + u32 irq_status[MAX_MSI_CTRLS]; + raw_spinlock_t lock; DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); }; @@ -316,8 +323,10 @@ static inline void dw_pcie_dbi_ro_wr_dis(struct dw_pcie *pci) #ifdef CONFIG_PCIE_DW_HOST irqreturn_t dw_handle_msi_irq(struct pcie_port *pp); void dw_pcie_msi_init(struct pcie_port *pp); +void dw_pcie_free_msi(struct pcie_port *pp); void dw_pcie_setup_rc(struct pcie_port *pp); int dw_pcie_host_init(struct pcie_port *pp); +int dw_pcie_allocate_domains(struct pcie_port *pp); #else static inline irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) { @@ -328,6 +337,10 @@ static inline void dw_pcie_msi_init(struct pcie_port *pp) { } +static inline void dw_pcie_free_msi(struct pcie_port *pp) +{ +} + static inline void dw_pcie_setup_rc(struct pcie_port *pp) { } @@ -336,6 +349,11 @@ static inline int dw_pcie_host_init(struct pcie_port *pp) { return 0; } + +static inline int dw_pcie_allocate_domains(struct pcie_port *pp) +{ + return 0; +} #endif #ifdef CONFIG_PCIE_DW_EP diff --git a/drivers/pci/dwc/pcie-histb.c b/drivers/pci/dwc/pcie-histb.c index 70b5c0b108bf..5d47b909340a 100644 --- a/drivers/pci/dwc/pcie-histb.c +++ b/drivers/pci/dwc/pcie-histb.c @@ -207,13 +207,6 @@ static struct dw_pcie_host_ops histb_pcie_host_ops = { .host_init = histb_pcie_host_init, }; -static irqreturn_t histb_pcie_msi_irq_handler(int irq, void *arg) -{ - struct pcie_port *pp = arg; - - return dw_handle_msi_irq(pp); -} - static void histb_pcie_host_disable(struct histb_pcie *hipcie) { reset_control_assert(hipcie->soft_reset); @@ -393,14 +386,6 @@ static int histb_pcie_probe(struct platform_device *pdev) dev_err(dev, "Failed to get MSI IRQ\n"); return pp->msi_irq; } - - ret = devm_request_irq(dev, pp->msi_irq, - histb_pcie_msi_irq_handler, - IRQF_SHARED, "histb-pcie-msi", pp); - if (ret) { - dev_err(dev, "cannot request MSI IRQ\n"); - return ret; - } } hipcie->phy = devm_phy_get(dev, "phy"); diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c index 6310c66e265c..89e5cc5cab64 100644 --- a/drivers/pci/dwc/pcie-qcom.c +++ b/drivers/pci/dwc/pcie-qcom.c @@ -180,13 +180,6 @@ static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500); } -static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg) -{ - struct pcie_port *pp = arg; - - return dw_handle_msi_irq(pp); -} - static int qcom_pcie_establish_link(struct qcom_pcie *pcie) { struct dw_pcie *pci = pcie->pci; @@ -1262,15 +1255,6 @@ static int qcom_pcie_probe(struct platform_device *pdev) pp->msi_irq = platform_get_irq_byname(pdev, "msi"); if (pp->msi_irq < 0) return pp->msi_irq; - - ret = devm_request_irq(dev, pp->msi_irq, - qcom_pcie_msi_irq_handler, - IRQF_SHARED | IRQF_NO_THREAD, - "qcom-pcie-msi", pp); - if (ret) { - dev_err(dev, "cannot request msi irq\n"); - return ret; - } } ret = phy_init(pcie->phy); -- cgit v1.2.3 From 3f43ccc4ea1b912ff24679576b4278fafbb190b3 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Tue, 6 Mar 2018 11:54:54 +0000 Subject: PCI: dwc: Remove old MSI IRQs API Remove the unused old MSI IRQs API from pcie-designware based on struct msi_controller that should now be considered obsolete. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Tested-by: Niklas Cassel Tested-by: Shawn Guo Acked-by: Marc Zyngier --- drivers/pci/dwc/pci-keystone-dw.c | 2 +- drivers/pci/dwc/pci-keystone.h | 3 +- drivers/pci/dwc/pci-layerscape.c | 3 +- drivers/pci/dwc/pcie-designware-host.c | 190 +-------------------------------- drivers/pci/dwc/pcie-designware.h | 2 +- 5 files changed, 5 insertions(+), 195 deletions(-) diff --git a/drivers/pci/dwc/pci-keystone-dw.c b/drivers/pci/dwc/pci-keystone-dw.c index 86e613afb019..0682213328e9 100644 --- a/drivers/pci/dwc/pci-keystone-dw.c +++ b/drivers/pci/dwc/pci-keystone-dw.c @@ -157,7 +157,7 @@ void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) BIT(bit_pos)); } -int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip) +int ks_dw_pcie_msi_host_init(struct pcie_port *pp) { return dw_pcie_allocate_domains(pp); } diff --git a/drivers/pci/dwc/pci-keystone.h b/drivers/pci/dwc/pci-keystone.h index aa504483e83a..8a13da391543 100644 --- a/drivers/pci/dwc/pci-keystone.h +++ b/drivers/pci/dwc/pci-keystone.h @@ -53,6 +53,5 @@ void ks_dw_pcie_msi_irq_ack(int i, struct pcie_port *pp); void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq); void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq); void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp); -int ks_dw_pcie_msi_host_init(struct pcie_port *pp, - struct msi_controller *chip); +int ks_dw_pcie_msi_host_init(struct pcie_port *pp); int ks_dw_pcie_link_up(struct dw_pcie *pci); diff --git a/drivers/pci/dwc/pci-layerscape.c b/drivers/pci/dwc/pci-layerscape.c index a7b4159631ae..3724d3ef7008 100644 --- a/drivers/pci/dwc/pci-layerscape.c +++ b/drivers/pci/dwc/pci-layerscape.c @@ -182,8 +182,7 @@ static int ls1021_pcie_host_init(struct pcie_port *pp) return ls_pcie_host_init(pp); } -static int ls_pcie_msi_host_init(struct pcie_port *pp, - struct msi_controller *chip) +static int ls_pcie_msi_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct device *dev = pci->dev; diff --git a/drivers/pci/dwc/pcie-designware-host.c b/drivers/pci/dwc/pcie-designware-host.c index a28c496f58ac..193a0fa7b709 100644 --- a/drivers/pci/dwc/pcie-designware-host.c +++ b/drivers/pci/dwc/pcie-designware-host.c @@ -43,14 +43,6 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, return dw_pcie_write(pci->dbi_base + where, size, val); } -static struct irq_chip dw_msi_irq_chip = { - .name = "PCI-MSI", - .irq_enable = pci_msi_unmask_irq, - .irq_disable = pci_msi_mask_irq, - .irq_mask = pci_msi_mask_irq, - .irq_unmask = pci_msi_unmask_irq, -}; - static void dw_msi_ack_irq(struct irq_data *d) { irq_chip_ack_parent(d); @@ -320,186 +312,6 @@ void dw_pcie_msi_init(struct pcie_port *pp) upper_32_bits(msi_target)); } -static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) -{ - unsigned int res, bit, ctrl; - - ctrl = irq / 32; - res = ctrl * 12; - bit = irq % 32; - pp->irq_status[ctrl] &= ~(1 << bit); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, - pp->irq_status[ctrl]); -} - -static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, - unsigned int nvec, unsigned int pos) -{ - unsigned int i; - - for (i = 0; i < nvec; i++) { - irq_set_msi_desc_off(irq_base, i, NULL); - /* Disable corresponding interrupt on MSI controller */ - if (pp->ops->msi_clear_irq) - pp->ops->msi_clear_irq(pp, pos + i); - else - dw_pcie_msi_clear_irq(pp, pos + i); - } - - bitmap_release_region(pp->msi_irq_in_use, pos, order_base_2(nvec)); -} - -static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) -{ - unsigned int res, bit, ctrl; - - ctrl = irq / 32; - res = ctrl * 12; - bit = irq % 32; - pp->irq_status[ctrl] |= 1 << bit; - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, - pp->irq_status[ctrl]); -} - -static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) -{ - int irq, pos0, i; - struct pcie_port *pp; - - pp = (struct pcie_port *)msi_desc_to_pci_sysdata(desc); - pos0 = bitmap_find_free_region(pp->msi_irq_in_use, MAX_MSI_IRQS, - order_base_2(no_irqs)); - if (pos0 < 0) - goto no_valid_irq; - - irq = irq_find_mapping(pp->irq_domain, pos0); - if (!irq) - goto no_valid_irq; - - /* - * irq_create_mapping (called from dw_pcie_host_init) pre-allocates - * descs so there is no need to allocate descs here. We can therefore - * assume that if irq_find_mapping above returns non-zero, then the - * descs are also successfully allocated. - */ - - for (i = 0; i < no_irqs; i++) { - if (irq_set_msi_desc_off(irq, i, desc) != 0) { - clear_irq_range(pp, irq, i, pos0); - goto no_valid_irq; - } - /*Enable corresponding interrupt in MSI interrupt controller */ - if (pp->ops->msi_set_irq) - pp->ops->msi_set_irq(pp, pos0 + i); - else - dw_pcie_msi_set_irq(pp, pos0 + i); - } - - *pos = pos0; - desc->nvec_used = no_irqs; - desc->msi_attrib.multiple = order_base_2(no_irqs); - - return irq; - -no_valid_irq: - *pos = pos0; - return -ENOSPC; -} - -static void dw_msi_setup_msg(struct pcie_port *pp, unsigned int irq, u32 pos) -{ - struct msi_msg msg; - u64 msi_target; - - if (pp->ops->get_msi_addr) - msi_target = pp->ops->get_msi_addr(pp); - else - msi_target = (u64)pp->msi_data; - - msg.address_lo = (u32)(msi_target & 0xffffffff); - msg.address_hi = (u32)(msi_target >> 32 & 0xffffffff); - - if (pp->ops->get_msi_data) - msg.data = pp->ops->get_msi_data(pp, pos); - else - msg.data = pos; - - pci_write_msi_msg(irq, &msg); -} - -static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, - struct msi_desc *desc) -{ - int irq, pos; - struct pcie_port *pp = pdev->bus->sysdata; - - if (desc->msi_attrib.is_msix) - return -EINVAL; - - irq = assign_irq(1, desc, &pos); - if (irq < 0) - return irq; - - dw_msi_setup_msg(pp, irq, pos); - - return 0; -} - -static int dw_msi_setup_irqs(struct msi_controller *chip, struct pci_dev *pdev, - int nvec, int type) -{ -#ifdef CONFIG_PCI_MSI - int irq, pos; - struct msi_desc *desc; - struct pcie_port *pp = pdev->bus->sysdata; - - /* MSI-X interrupts are not supported */ - if (type == PCI_CAP_ID_MSIX) - return -EINVAL; - - WARN_ON(!list_is_singular(&pdev->dev.msi_list)); - desc = list_entry(pdev->dev.msi_list.next, struct msi_desc, list); - - irq = assign_irq(nvec, desc, &pos); - if (irq < 0) - return irq; - - dw_msi_setup_msg(pp, irq, pos); - - return 0; -#else - return -EINVAL; -#endif -} - -static void dw_msi_teardown_irq(struct msi_controller *chip, unsigned int irq) -{ - struct irq_data *data = irq_get_irq_data(irq); - struct msi_desc *msi = irq_data_get_msi_desc(data); - struct pcie_port *pp = (struct pcie_port *)msi_desc_to_pci_sysdata(msi); - - clear_irq_range(pp, irq, 1, data->hwirq); -} - -static struct msi_controller dw_pcie_msi_chip = { - .setup_irq = dw_msi_setup_irq, - .setup_irqs = dw_msi_setup_irqs, - .teardown_irq = dw_msi_teardown_irq, -}; - -static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); - - return 0; -} - -static const struct irq_domain_ops msi_domain_ops = { - .map = dw_pcie_msi_map, -}; - int dw_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -639,7 +451,7 @@ int dw_pcie_host_init(struct pcie_port *pp) dw_chained_msi_isr, pp); } else { - ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip); + ret = pp->ops->msi_host_init(pp); if (ret < 0) goto error; } diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h index c80ee868e017..923f9565a385 100644 --- a/drivers/pci/dwc/pcie-designware.h +++ b/drivers/pci/dwc/pcie-designware.h @@ -151,7 +151,7 @@ struct dw_pcie_host_ops { u32 (*get_msi_data)(struct pcie_port *pp, int pos); void (*scan_bus)(struct pcie_port *pp); void (*set_num_vectors)(struct pcie_port *pp); - int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip); + int (*msi_host_init)(struct pcie_port *pp); void (*msi_irq_ack)(int irq, struct pcie_port *pp); }; -- cgit v1.2.3 From 1f319cb0538a10339d1ca73ee124331d611b43bf Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Tue, 6 Mar 2018 11:54:55 +0000 Subject: PCI: dwc: Expand maximum number of MSI IRQs from 32 to 256 The Synopsys PCIe Root Complex supports up to MSI 256 IRQs distributed over 8 controller registers, therefore the maximum number of MSI IRQs can be changed to 256. The number of controllers can be calculated based on the number of vectors used by the specific SoC driver. Update the dwc host bridge driver maximum number of supported MSI IRQs. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Tested-by: Niklas Cassel Tested-by: Shawn Guo Acked-by: Marc Zyngier --- drivers/pci/dwc/pcie-designware-host.c | 12 ++++++++---- drivers/pci/dwc/pcie-designware.h | 10 +++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/pci/dwc/pcie-designware-host.c b/drivers/pci/dwc/pcie-designware-host.c index 193a0fa7b709..550fdbb5c226 100644 --- a/drivers/pci/dwc/pcie-designware-host.c +++ b/drivers/pci/dwc/pcie-designware-host.c @@ -76,11 +76,13 @@ static struct msi_domain_info dw_pcie_msi_domain_info = { /* MSI int handler */ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) { - u32 val; int i, pos, irq; + u32 val, num_ctrls; irqreturn_t ret = IRQ_NONE; - for (i = 0; i < MAX_MSI_CTRLS; i++) { + num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; + + for (i = 0; i < num_ctrls; i++) { dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, &val); if (!val) @@ -639,13 +641,15 @@ static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci) void dw_pcie_setup_rc(struct pcie_port *pp) { - u32 val, ctrl; + u32 val, ctrl, num_ctrls; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); dw_pcie_setup(pci); + num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; + /* Initialize IRQ Status array */ - for (ctrl = 0; ctrl < MAX_MSI_CTRLS; ctrl++) + for (ctrl = 0; ctrl < num_ctrls; ctrl++) dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + (ctrl * 12), 4, &pp->irq_status[ctrl]); /* setup RC BARs */ diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h index 923f9565a385..fe811dbc12cf 100644 --- a/drivers/pci/dwc/pcie-designware.h +++ b/drivers/pci/dwc/pcie-designware.h @@ -107,13 +107,9 @@ #define MSI_MESSAGE_DATA_32 0x58 #define MSI_MESSAGE_DATA_64 0x5C -/* - * Maximum number of MSI IRQs can be 256 per controller. But keep - * it 32 as of now. Probably we will never need more than 32. If needed, - * then increment it in multiple of 32. - */ -#define MAX_MSI_IRQS 32 -#define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32) +#define MAX_MSI_IRQS 256 +#define MAX_MSI_IRQS_PER_CTRL 32 +#define MAX_MSI_CTRLS (MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL) #define MSI_DEF_NUM_VECTORS 32 /* Maximum number of inbound/outbound iATUs */ -- cgit v1.2.3 From 2380ca5f1f46412d08c96ebea996e049548829bd Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 14 Nov 2017 09:59:03 +0000 Subject: dt-bindings: PCI: rcar: Add device tree support for r8a7743 Add support for r8a7743. The Renesas RZ/G1M(R8A7743)PCIe controller is identical to the R-Car Gen2 family. No driver change is needed due to the fallback compatible value "renesas,pcie-rcar-gen2". Adding the SoC-specific compatible values here has three purposes: 1. Document which SoCs have this hardware module, 2. Allow checkpatch to validate compatible values. 3. Allow the driver to support SoC specific implementations in future as necessary. Signed-off-by: Biju Das Signed-off-by: Lorenzo Pieralisi Reviewed-by: Fabrizio Castro Reviewed-by: Geert Uytterhoeven Acked-by: Simon Horman --- Documentation/devicetree/bindings/pci/rcar-pci.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/rcar-pci.txt b/Documentation/devicetree/bindings/pci/rcar-pci.txt index 76ba3a61d1a3..1fb614e615da 100644 --- a/Documentation/devicetree/bindings/pci/rcar-pci.txt +++ b/Documentation/devicetree/bindings/pci/rcar-pci.txt @@ -1,13 +1,15 @@ * Renesas R-Car PCIe interface Required properties: -compatible: "renesas,pcie-r8a7779" for the R8A7779 SoC; +compatible: "renesas,pcie-r8a7743" for the R8A7743 SoC; + "renesas,pcie-r8a7779" for the R8A7779 SoC; "renesas,pcie-r8a7790" for the R8A7790 SoC; "renesas,pcie-r8a7791" for the R8A7791 SoC; "renesas,pcie-r8a7793" for the R8A7793 SoC; "renesas,pcie-r8a7795" for the R8A7795 SoC; "renesas,pcie-r8a7796" for the R8A7796 SoC; - "renesas,pcie-rcar-gen2" for a generic R-Car Gen2 compatible device. + "renesas,pcie-rcar-gen2" for a generic R-Car Gen2 or + RZ/G1 compatible device. "renesas,pcie-rcar-gen3" for a generic R-Car Gen3 compatible device. When compatible with the generic version, nodes must list the -- cgit v1.2.3 From ae15d8634dca4112037652725cd974af201f1091 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 1 Feb 2018 15:02:23 +0100 Subject: PCI: designware-ep: Fix typo in error message Fix typo in error message. s/deb_base2/dbi_base2/ Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi --- drivers/pci/dwc/pcie-designware-ep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c index 3a6feeff5f5b..9236b998327f 100644 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -322,7 +322,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) struct device_node *np = dev->of_node; if (!pci->dbi_base || !pci->dbi_base2) { - dev_err(dev, "dbi_base/deb_base2 is not populated\n"); + dev_err(dev, "dbi_base/dbi_base2 is not populated\n"); return -EINVAL; } -- cgit v1.2.3 From f625b1ade245ad6810909923585dd07f24391861 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 15 Feb 2018 13:22:48 +0000 Subject: PCI: qcom: Add missing supplies required for msm8996 This patch adds supplies that are required for msm8996. vdda is analog supply that go in to controller, and vddpe_3v3 is supply to PCIe endpoint. Without these supplies PCIe endpoints which require power supplies are not enumerated at all, as there is no one to power it up. Signed-off-by: Srinivas Kandagatla Signed-off-by: Lorenzo Pieralisi Acked-by: Stanimir Varbanov Reviewed-by: Rob Herring --- .../devicetree/bindings/pci/qcom,pcie.txt | 4 ++++ drivers/pci/dwc/pcie-qcom.c | 23 +++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt index 3c9d321b3d3b..1fd703bd73e0 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt @@ -189,6 +189,10 @@ Value type: Definition: A phandle to the analog power supply for IC which generates reference clock +- vddpe-3v3-supply: + Usage: optional + Value type: + Definition: A phandle to the PCIe endpoint power supply - phys: Usage: required for apq8084 diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c index 6310c66e265c..a7c12f737690 100644 --- a/drivers/pci/dwc/pcie-qcom.c +++ b/drivers/pci/dwc/pcie-qcom.c @@ -102,12 +102,14 @@ struct qcom_pcie_resources_1_0_0 { struct regulator *vdda; }; +#define QCOM_PCIE_2_3_2_MAX_SUPPLY 2 struct qcom_pcie_resources_2_3_2 { struct clk *aux_clk; struct clk *master_clk; struct clk *slave_clk; struct clk *cfg_clk; struct clk *pipe_clk; + struct regulator_bulk_data supplies[QCOM_PCIE_2_3_2_MAX_SUPPLY]; }; struct qcom_pcie_resources_2_4_0 { @@ -521,6 +523,14 @@ static int qcom_pcie_get_resources_2_3_2(struct qcom_pcie *pcie) struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; struct dw_pcie *pci = pcie->pci; struct device *dev = pci->dev; + int ret; + + res->supplies[0].supply = "vdda"; + res->supplies[1].supply = "vddpe-3v3"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies), + res->supplies); + if (ret) + return ret; res->aux_clk = devm_clk_get(dev, "aux"); if (IS_ERR(res->aux_clk)) @@ -550,6 +560,8 @@ static void qcom_pcie_deinit_2_3_2(struct qcom_pcie *pcie) clk_disable_unprepare(res->master_clk); clk_disable_unprepare(res->cfg_clk); clk_disable_unprepare(res->aux_clk); + + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); } static void qcom_pcie_post_deinit_2_3_2(struct qcom_pcie *pcie) @@ -567,10 +579,16 @@ static int qcom_pcie_init_2_3_2(struct qcom_pcie *pcie) u32 val; int ret; + ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies); + if (ret < 0) { + dev_err(dev, "cannot enable regulators\n"); + return ret; + } + ret = clk_prepare_enable(res->aux_clk); if (ret) { dev_err(dev, "cannot prepare/enable aux clock\n"); - return ret; + goto err_aux_clk; } ret = clk_prepare_enable(res->cfg_clk); @@ -621,6 +639,9 @@ err_master_clk: err_cfg_clk: clk_disable_unprepare(res->aux_clk); +err_aux_clk: + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); + return ret; } -- cgit v1.2.3 From 68e7c15ceb8d43dff8a91b6be40beac471a0c48d Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 15 Feb 2018 13:26:37 +0000 Subject: PCI: qcom: Use regulator bulk api for apq8064 supplies This patch converts existing regulators to use regulator bulk apis, to make it consistent with msm8996 changes also cut down some redundant code. Signed-off-by: Srinivas Kandagatla Signed-off-by: Lorenzo Pieralisi Acked-by: Stanimir Varbanov --- drivers/pci/dwc/pcie-qcom.c | 52 +++++++++++++-------------------------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c index a7c12f737690..ed4e75472831 100644 --- a/drivers/pci/dwc/pcie-qcom.c +++ b/drivers/pci/dwc/pcie-qcom.c @@ -79,6 +79,7 @@ #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE 0x358 #define SLV_ADDR_SPACE_SZ 0x10000000 +#define QCOM_PCIE_2_1_0_MAX_SUPPLY 3 struct qcom_pcie_resources_2_1_0 { struct clk *iface_clk; struct clk *core_clk; @@ -88,9 +89,7 @@ struct qcom_pcie_resources_2_1_0 { struct reset_control *ahb_reset; struct reset_control *por_reset; struct reset_control *phy_reset; - struct regulator *vdda; - struct regulator *vdda_phy; - struct regulator *vdda_refclk; + struct regulator_bulk_data supplies[QCOM_PCIE_2_1_0_MAX_SUPPLY]; }; struct qcom_pcie_resources_1_0_0 { @@ -218,18 +217,15 @@ static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; struct dw_pcie *pci = pcie->pci; struct device *dev = pci->dev; + int ret; - res->vdda = devm_regulator_get(dev, "vdda"); - if (IS_ERR(res->vdda)) - return PTR_ERR(res->vdda); - - res->vdda_phy = devm_regulator_get(dev, "vdda_phy"); - if (IS_ERR(res->vdda_phy)) - return PTR_ERR(res->vdda_phy); - - res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk"); - if (IS_ERR(res->vdda_refclk)) - return PTR_ERR(res->vdda_refclk); + res->supplies[0].supply = "vdda"; + res->supplies[1].supply = "vdda_phy"; + res->supplies[2].supply = "vdda_refclk"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies), + res->supplies); + if (ret) + return ret; res->iface_clk = devm_clk_get(dev, "iface"); if (IS_ERR(res->iface_clk)) @@ -275,9 +271,7 @@ static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie) clk_disable_unprepare(res->iface_clk); clk_disable_unprepare(res->core_clk); clk_disable_unprepare(res->phy_clk); - regulator_disable(res->vdda); - regulator_disable(res->vdda_phy); - regulator_disable(res->vdda_refclk); + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); } static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) @@ -288,24 +282,12 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) u32 val; int ret; - ret = regulator_enable(res->vdda); - if (ret) { - dev_err(dev, "cannot enable vdda regulator\n"); + ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies); + if (ret < 0) { + dev_err(dev, "cannot enable regulators\n"); return ret; } - ret = regulator_enable(res->vdda_refclk); - if (ret) { - dev_err(dev, "cannot enable vdda_refclk regulator\n"); - goto err_refclk; - } - - ret = regulator_enable(res->vdda_phy); - if (ret) { - dev_err(dev, "cannot enable vdda_phy regulator\n"); - goto err_vdda_phy; - } - ret = reset_control_assert(res->ahb_reset); if (ret) { dev_err(dev, "cannot assert ahb reset\n"); @@ -389,11 +371,7 @@ err_clk_core: err_clk_phy: clk_disable_unprepare(res->iface_clk); err_assert_ahb: - regulator_disable(res->vdda_phy); -err_vdda_phy: - regulator_disable(res->vdda_refclk); -err_refclk: - regulator_disable(res->vdda); + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); return ret; } -- cgit v1.2.3 From db0c25f8aadad8f0be1c6986cf1dcf874d40d79b Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 2 Mar 2018 09:12:00 +0800 Subject: PCI: histb: Fix error path of histb_pcie_host_enable() If clk_prepare_enable() call fails on a particular clock, we should not call clk_disable_unprepare() on this clock, but on the clocks that succeed from clk_prepare_enable() previously. Signed-off-by: Shawn Guo Signed-off-by: Lorenzo Pieralisi --- drivers/pci/dwc/pcie-histb.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/pci/dwc/pcie-histb.c b/drivers/pci/dwc/pcie-histb.c index 70b5c0b108bf..17ed604f5741 100644 --- a/drivers/pci/dwc/pcie-histb.c +++ b/drivers/pci/dwc/pcie-histb.c @@ -276,13 +276,12 @@ static int histb_pcie_host_enable(struct pcie_port *pp) return 0; err_aux_clk: - clk_disable_unprepare(hipcie->aux_clk); -err_pipe_clk: clk_disable_unprepare(hipcie->pipe_clk); -err_sys_clk: +err_pipe_clk: clk_disable_unprepare(hipcie->sys_clk); -err_bus_clk: +err_sys_clk: clk_disable_unprepare(hipcie->bus_clk); +err_bus_clk: return ret; } -- cgit v1.2.3 From 58dfb24349e1d0aa6461d2b0f2811b46fe350280 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 2 Mar 2018 09:12:01 +0800 Subject: PCI: histb: Add an optional regulator for PCIe port power control The power supplies to PCIe port are often controlled by GPIO on some board designs. Let's add an optional regulator which can be backed by GPIO to control the power. Signed-off-by: Shawn Guo Signed-off-by: Lorenzo Pieralisi Acked-by: Rob Herring --- .../bindings/pci/hisilicon-histb-pcie.txt | 1 + drivers/pci/dwc/pcie-histb.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/hisilicon-histb-pcie.txt b/Documentation/devicetree/bindings/pci/hisilicon-histb-pcie.txt index c84bc027930b..760b4d740616 100644 --- a/Documentation/devicetree/bindings/pci/hisilicon-histb-pcie.txt +++ b/Documentation/devicetree/bindings/pci/hisilicon-histb-pcie.txt @@ -34,6 +34,7 @@ Required properties Optional properties: - reset-gpios: The gpio to generate PCIe PERST# assert and deassert signal. +- vpcie-supply: The regulator in charge of PCIe port power. - phys: List of phandle and phy mode specifier, should be 0. - phy-names: Must be "phy". diff --git a/drivers/pci/dwc/pcie-histb.c b/drivers/pci/dwc/pcie-histb.c index 17ed604f5741..4cef0a514944 100644 --- a/drivers/pci/dwc/pcie-histb.c +++ b/drivers/pci/dwc/pcie-histb.c @@ -61,6 +61,7 @@ struct histb_pcie { struct reset_control *bus_reset; void __iomem *ctrl; int reset_gpio; + struct regulator *vpcie; }; static u32 histb_pcie_readl(struct histb_pcie *histb_pcie, u32 reg) @@ -227,6 +228,9 @@ static void histb_pcie_host_disable(struct histb_pcie *hipcie) if (gpio_is_valid(hipcie->reset_gpio)) gpio_set_value_cansleep(hipcie->reset_gpio, 0); + + if (hipcie->vpcie) + regulator_disable(hipcie->vpcie); } static int histb_pcie_host_enable(struct pcie_port *pp) @@ -237,6 +241,14 @@ static int histb_pcie_host_enable(struct pcie_port *pp) int ret; /* power on PCIe device if have */ + if (hipcie->vpcie) { + ret = regulator_enable(hipcie->vpcie); + if (ret) { + dev_err(dev, "failed to enable regulator: %d\n", ret); + return ret; + } + } + if (gpio_is_valid(hipcie->reset_gpio)) gpio_set_value_cansleep(hipcie->reset_gpio, 1); @@ -282,6 +294,8 @@ err_pipe_clk: err_sys_clk: clk_disable_unprepare(hipcie->bus_clk); err_bus_clk: + if (hipcie->vpcie) + regulator_disable(hipcie->vpcie); return ret; } @@ -331,6 +345,13 @@ static int histb_pcie_probe(struct platform_device *pdev) return PTR_ERR(pci->dbi_base); } + hipcie->vpcie = devm_regulator_get_optional(dev, "vpcie"); + if (IS_ERR(hipcie->vpcie)) { + if (PTR_ERR(hipcie->vpcie) == -EPROBE_DEFER) + return -EPROBE_DEFER; + hipcie->vpcie = NULL; + } + hipcie->reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &of_flags); if (of_flags & OF_GPIO_ACTIVE_LOW) -- cgit v1.2.3 From ef7942603e35e300e6967fa7c17ebc17a0c00f59 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:42:01 -0600 Subject: PCI/portdrv: Merge pcieport_if.h into portdrv.h pcieport_if.h contained the interfaces to register port service driver, e.g., pcie_port_service_register(). portdrv.h contained internal data structures of the port driver. I don't think it's worth keeping those files separate, since both headers and their users are all inside the PCI core. Merge pcieport_if.h directly in drivers/pci/pcie/portdrv.h and update the users to include that instead. Signed-off-by: Bjorn Helgaas Reviewed-by: Christoph Hellwig --- drivers/pci/hotplug/pciehp.h | 2 +- drivers/pci/pcie/aer/aerdrv.h | 2 +- drivers/pci/pcie/pcie-dpc.c | 2 +- drivers/pci/pcie/pcieport_if.h | 71 ----------------------------------------- drivers/pci/pcie/pme.c | 1 - drivers/pci/pcie/portdrv.h | 61 ++++++++++++++++++++++++++++++++++- drivers/pci/pcie/portdrv_acpi.c | 1 - drivers/pci/pcie/portdrv_bus.c | 1 - drivers/pci/pcie/portdrv_core.c | 1 - drivers/pci/pcie/portdrv_pci.c | 1 - 10 files changed, 63 insertions(+), 80 deletions(-) delete mode 100644 drivers/pci/pcie/pcieport_if.h diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 08072bcaa381..88e917c9120f 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -23,7 +23,7 @@ #include #include -#include "../pcie/pcieport_if.h" +#include "../pcie/portdrv.h" #define MY_NAME "pciehp" diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 568326f385b7..a884f68bada4 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -13,7 +13,7 @@ #include #include -#include "../pcieport_if.h" +#include "../portdrv.h" #define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ PCI_EXP_RTCTL_SENFEE| \ diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/pcie-dpc.c index bac895de4c72..8c57d607e603 100644 --- a/drivers/pci/pcie/pcie-dpc.c +++ b/drivers/pci/pcie/pcie-dpc.c @@ -11,7 +11,7 @@ #include #include -#include "pcieport_if.h" +#include "portdrv.h" #include "../pci.h" #include "aer/aerdrv.h" diff --git a/drivers/pci/pcie/pcieport_if.h b/drivers/pci/pcie/pcieport_if.h deleted file mode 100644 index b69769dbf659..000000000000 --- a/drivers/pci/pcie/pcieport_if.h +++ /dev/null @@ -1,71 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * File: pcieport_if.h - * Purpose: PCI Express Port Bus Driver's IF Data Structure - * - * Copyright (C) 2004 Intel - * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) - */ - -#ifndef _PCIEPORT_IF_H_ -#define _PCIEPORT_IF_H_ - -/* Port Type */ -#define PCIE_ANY_PORT (~0) - -/* Service Type */ -#define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */ -#define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT) -#define PCIE_PORT_SERVICE_AER_SHIFT 1 /* Advanced Error Reporting */ -#define PCIE_PORT_SERVICE_AER (1 << PCIE_PORT_SERVICE_AER_SHIFT) -#define PCIE_PORT_SERVICE_HP_SHIFT 2 /* Native Hotplug */ -#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT) -#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */ -#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT) -#define PCIE_PORT_SERVICE_DPC_SHIFT 4 /* Downstream Port Containment */ -#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT) - -struct pcie_device { - int irq; /* Service IRQ/MSI/MSI-X Vector */ - struct pci_dev *port; /* Root/Upstream/Downstream Port */ - u32 service; /* Port service this device represents */ - void *priv_data; /* Service Private Data */ - struct device device; /* Generic Device Interface */ -}; -#define to_pcie_device(d) container_of(d, struct pcie_device, device) - -static inline void set_service_data(struct pcie_device *dev, void *data) -{ - dev->priv_data = data; -} - -static inline void *get_service_data(struct pcie_device *dev) -{ - return dev->priv_data; -} - -struct pcie_port_service_driver { - const char *name; - int (*probe) (struct pcie_device *dev); - void (*remove) (struct pcie_device *dev); - int (*suspend) (struct pcie_device *dev); - int (*resume) (struct pcie_device *dev); - - /* Device driver may resume normal operations */ - void (*error_resume)(struct pci_dev *dev); - - /* Link Reset Capability - AER service driver specific */ - pci_ers_result_t (*reset_link) (struct pci_dev *dev); - - int port_type; /* Type of the port this driver can handle */ - u32 service; /* Port service this device represents */ - - struct device_driver driver; -}; -#define to_service_driver(d) \ - container_of(d, struct pcie_port_service_driver, driver) - -int pcie_port_service_register(struct pcie_port_service_driver *new); -void pcie_port_service_unregister(struct pcie_port_service_driver *new); - -#endif /* _PCIEPORT_IF_H_ */ diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index d29678958d92..3ed67676ea2a 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -16,7 +16,6 @@ #include #include -#include "pcieport_if.h" #include "../pci.h" #include "portdrv.h" diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index a854bc569117..d4009e35702c 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * File: portdrv.h - * Purpose: PCI Express Port Bus Driver's Internal Data Structures + * Purpose: PCI Express Port Bus Driver's Data Structures * * Copyright (C) 2004 Intel * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) @@ -12,7 +12,66 @@ #include +/* Service Type */ +#define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */ +#define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT) +#define PCIE_PORT_SERVICE_AER_SHIFT 1 /* Advanced Error Reporting */ +#define PCIE_PORT_SERVICE_AER (1 << PCIE_PORT_SERVICE_AER_SHIFT) +#define PCIE_PORT_SERVICE_HP_SHIFT 2 /* Native Hotplug */ +#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT) +#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */ +#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT) +#define PCIE_PORT_SERVICE_DPC_SHIFT 4 /* Downstream Port Containment */ +#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT) + #define PCIE_PORT_DEVICE_MAXSERVICES 5 + +/* Port Type */ +#define PCIE_ANY_PORT (~0) + +struct pcie_device { + int irq; /* Service IRQ/MSI/MSI-X Vector */ + struct pci_dev *port; /* Root/Upstream/Downstream Port */ + u32 service; /* Port service this device represents */ + void *priv_data; /* Service Private Data */ + struct device device; /* Generic Device Interface */ +}; +#define to_pcie_device(d) container_of(d, struct pcie_device, device) + +static inline void set_service_data(struct pcie_device *dev, void *data) +{ + dev->priv_data = data; +} + +static inline void *get_service_data(struct pcie_device *dev) +{ + return dev->priv_data; +} + +struct pcie_port_service_driver { + const char *name; + int (*probe) (struct pcie_device *dev); + void (*remove) (struct pcie_device *dev); + int (*suspend) (struct pcie_device *dev); + int (*resume) (struct pcie_device *dev); + + /* Device driver may resume normal operations */ + void (*error_resume)(struct pci_dev *dev); + + /* Link Reset Capability - AER service driver specific */ + pci_ers_result_t (*reset_link) (struct pci_dev *dev); + + int port_type; /* Type of the port this driver can handle */ + u32 service; /* Port service this device represents */ + + struct device_driver driver; +}; +#define to_service_driver(d) \ + container_of(d, struct pcie_port_service_driver, driver) + +int pcie_port_service_register(struct pcie_port_service_driver *new); +void pcie_port_service_unregister(struct pcie_port_service_driver *new); + /* * The PCIe Capability Interrupt Message Number (PCIe r3.1, sec 7.8.2) must * be one of the first 32 MSI-X entries. Per PCI r3.0, sec 6.8.3.1, MSI diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c index c7d8debb4a5c..53f60053bd47 100644 --- a/drivers/pci/pcie/portdrv_acpi.c +++ b/drivers/pci/pcie/portdrv_acpi.c @@ -11,7 +11,6 @@ #include #include -#include "pcieport_if.h" #include "aer/aerdrv.h" #include "../pci.h" #include "portdrv.h" diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c index b5c5697cfb30..4969ccf6b214 100644 --- a/drivers/pci/pcie/portdrv_bus.c +++ b/drivers/pci/pcie/portdrv_bus.c @@ -13,7 +13,6 @@ #include #include -#include "pcieport_if.h" #include "portdrv.h" static int pcie_port_bus_match(struct device *dev, struct device_driver *drv); diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index bab9cb71130f..4268b2fc2c7a 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -17,7 +17,6 @@ #include #include -#include "pcieport_if.h" #include "../pci.h" #include "portdrv.h" diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 13dbe846a1d1..977bd3cca2e5 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -19,7 +19,6 @@ #include #include -#include "pcieport_if.h" #include "../pci.h" #include "portdrv.h" -- cgit v1.2.3 From dcb0453d71e361d4718bb566d99e6ae498284419 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:06:53 -0600 Subject: PCI/PM: Move pcie_clear_root_pme_status() to core Move pcie_clear_root_pme_status() from the port driver to the PCI core so it will be available even when the port driver isn't present. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Reviewed-by: Christoph Hellwig --- drivers/pci/pci.c | 9 +++++++++ drivers/pci/pci.h | 1 + drivers/pci/pcie/portdrv.h | 2 -- drivers/pci/pcie/portdrv_pci.c | 9 --------- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index f6a4dd10d9b0..120e3393fc35 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1683,6 +1683,15 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) } EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state); +/** + * pcie_clear_root_pme_status - Clear root port PME interrupt status. + * @dev: PCIe root port or event collector. + */ +void pcie_clear_root_pme_status(struct pci_dev *dev) +{ + pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME); +} + /** * pci_check_pme_status - Check if given device has generated PME. * @dev: Device to check. diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index fcd81911b127..813ca2c895d8 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -71,6 +71,7 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state); void pci_power_up(struct pci_dev *dev); void pci_disable_enabled_device(struct pci_dev *dev); int pci_finish_runtime_suspend(struct pci_dev *dev); +void pcie_clear_root_pme_status(struct pci_dev *dev); int __pci_pme_wakeup(struct pci_dev *dev, void *ign); void pci_pme_restore(struct pci_dev *dev); bool pci_dev_keep_suspended(struct pci_dev *dev); diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index d4009e35702c..7086086e45d0 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -93,8 +93,6 @@ void pcie_port_bus_unregister(void); struct pci_dev; -void pcie_clear_root_pme_status(struct pci_dev *dev); - #ifdef CONFIG_HOTPLUG_PCI_PCIE extern bool pciehp_msi_disabled; diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 977bd3cca2e5..d6f10a97d400 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -49,15 +49,6 @@ __setup("pcie_ports=", pcie_port_setup); /* global data */ -/** - * pcie_clear_root_pme_status - Clear root port PME interrupt status. - * @dev: PCIe root port or event collector. - */ -void pcie_clear_root_pme_status(struct pci_dev *dev) -{ - pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME); -} - static int pcie_portdrv_restore_config(struct pci_dev *dev) { int retval; -- cgit v1.2.3 From a39bd851dccfdcb89db3d9a6b03283aaf15f310c Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:06:54 -0600 Subject: PCI/PM: Clear PCIe PME Status bit in core, not PCIe port driver fe31e69740ed ("PCI/PCIe: Clear Root PME Status bits early during system resume") added a .resume_noirq() callback to the PCIe port driver to clear the PME Status bit during resume to work around a BIOS issue. The BIOS evidently enabled PME interrupts for ACPI-based runtime wakeups but did not clear the PME Status bit during resume, which meant PMEs after resume did not trigger interrupts because PME Status did not transition from cleared to set. The fix was in the PCIe port driver, so it worked when CONFIG_PCIEPORTBUS was set. But I think we *always* want the fix because the platform may use PME interrupts even if Linux is built without the PCIe port driver. Move the fix from the port driver to the PCI core so we can work around this "PME doesn't work after waking from a sleep state" issue regardless of CONFIG_PCIEPORTBUS. [bhelgaas: folded in warning fix from Arnd Bergmann : https://lkml.kernel.org/r/20180328134747.2062348-1-arnd@arndb.de] Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 13 +++++++++++++ drivers/pci/pcie/portdrv_pci.c | 15 --------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 3bed6beda051..c49af2b679bc 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -714,6 +714,17 @@ static void pci_pm_complete(struct device *dev) #endif /* !CONFIG_PM_SLEEP */ #ifdef CONFIG_SUSPEND +static void pcie_pme_root_status_cleanup(struct pci_dev *pci_dev) +{ + /* + * Some BIOSes forget to clear Root PME Status bits after system + * wakeup, which breaks ACPI-based runtime wakeup on PCI Express. + * Clear those bits now just in case (shouldn't hurt). + */ + if (pci_is_pcie(pci_dev) && + pci_pcie_type(pci_dev) == PCI_EXP_TYPE_ROOT_PORT) + pcie_clear_root_pme_status(pci_dev); +} static int pci_pm_suspend(struct device *dev) { @@ -873,6 +884,8 @@ static int pci_pm_resume_noirq(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); + pcie_pme_root_status_cleanup(pci_dev); + if (drv && drv->pm && drv->pm->resume_noirq) error = drv->pm->resume_noirq(dev); diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index d6f10a97d400..ec9e936c2a5b 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -61,20 +61,6 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev) } #ifdef CONFIG_PM -static int pcie_port_resume_noirq(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - - /* - * Some BIOSes forget to clear Root PME Status bits after system wakeup - * which breaks ACPI-based runtime wakeup on PCI Express, so clear those - * bits now just in case (shouldn't hurt). - */ - if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT) - pcie_clear_root_pme_status(pdev); - return 0; -} - static int pcie_port_runtime_suspend(struct device *dev) { return to_pci_dev(dev)->bridge_d3 ? 0 : -EBUSY; @@ -102,7 +88,6 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { .thaw = pcie_port_device_resume, .poweroff = pcie_port_device_suspend, .restore = pcie_port_device_resume, - .resume_noirq = pcie_port_resume_noirq, .runtime_suspend = pcie_port_runtime_suspend, .runtime_resume = pcie_port_runtime_resume, .runtime_idle = pcie_port_runtime_idle, -- cgit v1.2.3 From 5a1e0baa34fdf458a42e5ff99e3b04d909ba99ea Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 13 Mar 2018 15:03:36 -0500 Subject: PCI/MSI: Don't set up INTx if MSI or MSI-X is enabled If MSI or MSI-X is enabled, the device uses that. It uses INTx only if both MSI and MSI-X are disabled (see PCIe r4.0, sec 7.7.1.2), so if a device already has MSI or MSI-X enabled, there's no need to set up its legacy INTx interrupt. bba6f6fc68e7 ("[PATCH] MSI-X: fix resume crash") changed the cris, frv, x86, and ia64 arches to skip INTx setup when MSI is enabled. The cris and frv arches have since been removed. 16cf0ebc35dd ("x86/PCI: Do not use interrupt links for devices using MSI-X") extended that by changing x86 to also skip INTx setup when MSI-X is enabled. Change ia64 to skip INTx setup when either MSI or MSI-X is enabled by applying the logic from 16cf0ebc35dd to ia64 as well as x86. Tested-by: Tony Luck Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko --- arch/ia64/pci/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index f5ec736100ee..7ccc64d5fe3e 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -398,7 +398,7 @@ pcibios_enable_device (struct pci_dev *dev, int mask) if (ret < 0) return ret; - if (!dev->msi_enabled) + if (!pci_dev_msi_enabled(dev)) return acpi_pci_irq_enable(dev); return 0; } @@ -407,7 +407,7 @@ void pcibios_disable_device (struct pci_dev *dev) { BUG_ON(atomic_read(&dev->enable_cnt)); - if (!dev->msi_enabled) + if (!pci_dev_msi_enabled(dev)) acpi_pci_irq_disable(dev); } -- cgit v1.2.3 From 6de3ff900090a9913cebfdc6e9c6c4b65e96393c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 13 Mar 2018 18:23:06 +0000 Subject: PCI: tegra: Add PCI_MSI_IRQ_DOMAIN kconfig dependency Building the tegra PCIe host driver without MSI results in a link failure: drivers/pci/host/pci-tegra.o:(.data+0x70): undefined reference to `pci_msi_unmask_irq' drivers/pci/host/pci-tegra.o:(.data+0x74): undefined reference to `pci_msi_mask_irq' This adds the same dependency that everyone else uses. Signed-off-by: Arnd Bergmann [lorenzo.pieralisi@arm.com: rewrote commit log] Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/host/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index dc8a2a175f19..0d0177ce436c 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -38,6 +38,7 @@ config PCI_FTPCI100 config PCI_TEGRA bool "NVIDIA Tegra PCIe controller" depends on ARCH_TEGRA + depends on PCI_MSI_IRQ_DOMAIN help Say Y here if you want support for the PCIe host controller found on NVIDIA Tegra SoCs. -- cgit v1.2.3 From 021ad274d7dc31611d4f47f7dd4ac7a224526f30 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Thu, 15 Mar 2018 14:20:53 +0000 Subject: PCI: hv: Serialize the present and eject work items When we hot-remove the device, we first receive a PCI_EJECT message and then receive a PCI_BUS_RELATIONS message with bus_rel->device_count == 0. The first message is offloaded to hv_eject_device_work(), and the second is offloaded to pci_devices_present_work(). Both the paths can be running list_del(&hpdev->list_entry), causing general protection fault, because system_wq can run them concurrently. The patch eliminates the race condition. Since access to present/eject work items is serialized, we do not need the hbus->enum_sem anymore, so remove it. Fixes: 4daace0d8ce8 ("PCI: hv: Add paravirtual PCI front-end for Microsoft Hyper-V VMs") Link: https://lkml.kernel.org/r/KL1P15301MB00064DA6B4D221123B5241CFBFD70@KL1P15301MB0006.APCP153.PROD.OUTLOOK.COM Tested-by: Adrian Suhov Tested-by: Chris Valean Signed-off-by: Dexuan Cui [lorenzo.pieralisi@arm.com: squashed semaphore removal patch] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Michael Kelley Acked-by: Haiyang Zhang Cc: # v4.6+ Cc: Vitaly Kuznetsov Cc: Jack Morgenstein Cc: Stephen Hemminger Cc: K. Y. Srinivasan --- drivers/pci/host/pci-hyperv.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index 2faf38eab785..b7fd5c157d73 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -447,7 +447,6 @@ struct hv_pcibus_device { spinlock_t device_list_lock; /* Protect lists below */ void __iomem *cfg_addr; - struct semaphore enum_sem; struct list_head resources_for_children; struct list_head children; @@ -461,6 +460,8 @@ struct hv_pcibus_device { struct retarget_msi_interrupt retarget_msi_interrupt_params; spinlock_t retarget_msi_interrupt_lock; + + struct workqueue_struct *wq; }; /* @@ -1590,12 +1591,8 @@ static struct hv_pci_dev *get_pcichild_wslot(struct hv_pcibus_device *hbus, * It must also treat the omission of a previously observed device as * notification that the device no longer exists. * - * Note that this function is a work item, and it may not be - * invoked in the order that it was queued. Back to back - * updates of the list of present devices may involve queuing - * multiple work items, and this one may run before ones that - * were sent later. As such, this function only does something - * if is the last one in the queue. + * Note that this function is serialized with hv_eject_device_work(), + * because both are pushed to the ordered workqueue hbus->wq. */ static void pci_devices_present_work(struct work_struct *work) { @@ -1616,11 +1613,6 @@ static void pci_devices_present_work(struct work_struct *work) INIT_LIST_HEAD(&removed); - if (down_interruptible(&hbus->enum_sem)) { - put_hvpcibus(hbus); - return; - } - /* Pull this off the queue and process it if it was the last one. */ spin_lock_irqsave(&hbus->device_list_lock, flags); while (!list_empty(&hbus->dr_list)) { @@ -1637,7 +1629,6 @@ static void pci_devices_present_work(struct work_struct *work) spin_unlock_irqrestore(&hbus->device_list_lock, flags); if (!dr) { - up(&hbus->enum_sem); put_hvpcibus(hbus); return; } @@ -1724,7 +1715,6 @@ static void pci_devices_present_work(struct work_struct *work) break; } - up(&hbus->enum_sem); put_hvpcibus(hbus); kfree(dr); } @@ -1770,7 +1760,7 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus, spin_unlock_irqrestore(&hbus->device_list_lock, flags); get_hvpcibus(hbus); - schedule_work(&dr_wrk->wrk); + queue_work(hbus->wq, &dr_wrk->wrk); } /** @@ -1848,7 +1838,7 @@ static void hv_pci_eject_device(struct hv_pci_dev *hpdev) get_pcichild(hpdev, hv_pcidev_ref_pnp); INIT_WORK(&hpdev->wrk, hv_eject_device_work); get_hvpcibus(hpdev->hbus); - schedule_work(&hpdev->wrk); + queue_work(hpdev->hbus->wq, &hpdev->wrk); } /** @@ -2461,13 +2451,18 @@ static int hv_pci_probe(struct hv_device *hdev, spin_lock_init(&hbus->config_lock); spin_lock_init(&hbus->device_list_lock); spin_lock_init(&hbus->retarget_msi_interrupt_lock); - sema_init(&hbus->enum_sem, 1); init_completion(&hbus->remove_event); + hbus->wq = alloc_ordered_workqueue("hv_pci_%x", 0, + hbus->sysdata.domain); + if (!hbus->wq) { + ret = -ENOMEM; + goto free_bus; + } ret = vmbus_open(hdev->channel, pci_ring_size, pci_ring_size, NULL, 0, hv_pci_onchannelcallback, hbus); if (ret) - goto free_bus; + goto destroy_wq; hv_set_drvdata(hdev, hbus); @@ -2536,6 +2531,8 @@ free_config: hv_free_config_window(hbus); close: vmbus_close(hdev->channel); +destroy_wq: + destroy_workqueue(hbus->wq); free_bus: free_page((unsigned long)hbus); return ret; @@ -2615,6 +2612,7 @@ static int hv_pci_remove(struct hv_device *hdev) irq_domain_free_fwnode(hbus->sysdata.fwnode); put_hvpcibus(hbus); wait_for_completion(&hbus->remove_event); + destroy_workqueue(hbus->wq); free_page((unsigned long)hbus); return 0; } -- cgit v1.2.3 From de0aa7b2f97d348ba7d1e17a00744c989baa0cb6 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Thu, 15 Mar 2018 14:21:08 +0000 Subject: PCI: hv: Fix 2 hang issues in hv_compose_msi_msg() 1. With the patch "x86/vector/msi: Switch to global reservation mode", the recent v4.15 and newer kernels always hang for 1-vCPU Hyper-V VM with SR-IOV. This is because when we reach hv_compose_msi_msg() by request_irq() -> request_threaded_irq() ->__setup_irq()->irq_startup() -> __irq_startup() -> irq_domain_activate_irq() -> ... -> msi_domain_activate() -> ... -> hv_compose_msi_msg(), local irq is disabled in __setup_irq(). Note: when we reach hv_compose_msi_msg() by another code path: pci_enable_msix_range() -> ... -> irq_domain_activate_irq() -> ... -> hv_compose_msi_msg(), local irq is not disabled. hv_compose_msi_msg() depends on an interrupt from the host. With interrupts disabled, a UP VM always hangs in the busy loop in the function, because the interrupt callback hv_pci_onchannelcallback() can not be called. We can do nothing but work it around by polling the channel. This is ugly, but we don't have any other choice. 2. If the host is ejecting the VF device before we reach hv_compose_msi_msg(), in a UP VM, we can hang in hv_compose_msi_msg() forever, because at this time the host doesn't respond to the CREATE_INTERRUPT request. This issue exists the first day the pci-hyperv driver appears in the kernel. Luckily, this can also by worked around by polling the channel for the PCI_EJECT message and hpdev->state, and by checking the PCI vendor ID. Note: actually the above 2 issues also happen to a SMP VM, if "hbus->hdev->channel->target_cpu == smp_processor_id()" is true. Fixes: 4900be83602b ("x86/vector/msi: Switch to global reservation mode") Tested-by: Adrian Suhov Tested-by: Chris Valean Signed-off-by: Dexuan Cui Signed-off-by: Lorenzo Pieralisi Reviewed-by: Michael Kelley Acked-by: Haiyang Zhang Cc: Cc: Stephen Hemminger Cc: K. Y. Srinivasan Cc: Vitaly Kuznetsov Cc: Jack Morgenstein --- drivers/pci/host/pci-hyperv.c | 58 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index b7fd5c157d73..cb694d2a1228 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -521,6 +521,8 @@ struct hv_pci_compl { s32 completion_status; }; +static void hv_pci_onchannelcallback(void *context); + /** * hv_pci_generic_compl() - Invoked for a completion packet * @context: Set up by the sender of the packet. @@ -665,6 +667,31 @@ static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where, } } +static u16 hv_pcifront_get_vendor_id(struct hv_pci_dev *hpdev) +{ + u16 ret; + unsigned long flags; + void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET + + PCI_VENDOR_ID; + + spin_lock_irqsave(&hpdev->hbus->config_lock, flags); + + /* Choose the function to be read. (See comment above) */ + writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr); + /* Make sure the function was chosen before we start reading. */ + mb(); + /* Read from that function's config space. */ + ret = readw(addr); + /* + * mb() is not required here, because the spin_unlock_irqrestore() + * is a barrier. + */ + + spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags); + + return ret; +} + /** * _hv_pcifront_write_config() - Internal PCI config write * @hpdev: The PCI driver's representation of the device @@ -1107,8 +1134,37 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) * Since this function is called with IRQ locks held, can't * do normal wait for completion; instead poll. */ - while (!try_wait_for_completion(&comp.comp_pkt.host_event)) + while (!try_wait_for_completion(&comp.comp_pkt.host_event)) { + /* 0xFFFF means an invalid PCI VENDOR ID. */ + if (hv_pcifront_get_vendor_id(hpdev) == 0xFFFF) { + dev_err_once(&hbus->hdev->device, + "the device has gone\n"); + goto free_int_desc; + } + + /* + * When the higher level interrupt code calls us with + * interrupt disabled, we must poll the channel by calling + * the channel callback directly when channel->target_cpu is + * the current CPU. When the higher level interrupt code + * calls us with interrupt enabled, let's add the + * local_bh_disable()/enable() to avoid race. + */ + local_bh_disable(); + + if (hbus->hdev->channel->target_cpu == smp_processor_id()) + hv_pci_onchannelcallback(hbus); + + local_bh_enable(); + + if (hpdev->state == hv_pcichild_ejecting) { + dev_err_once(&hbus->hdev->device, + "the device is being ejected\n"); + goto free_int_desc; + } + udelay(100); + } if (comp.comp_pkt.completion_status < 0) { dev_err(&hbus->hdev->device, -- cgit v1.2.3 From df3f2159f4e4146d40b244725ce79ed921530b99 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Thu, 15 Mar 2018 14:21:35 +0000 Subject: PCI: hv: Fix a comment typo in _hv_pcifront_read_config() Comment in _hv_pcifront_read_config() contains a typo, fix it. No functional change. Signed-off-by: Dexuan Cui [lorenzo.pieralisi@arm.com: changed commit log] Signed-off-by: Lorenzo Pieralisi Acked-by: Haiyang Zhang Cc: Vitaly Kuznetsov Cc: Stephen Hemminger Cc: K. Y. Srinivasan --- drivers/pci/host/pci-hyperv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index cb694d2a1228..0d2a09833a05 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -656,7 +656,7 @@ static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where, break; } /* - * Make sure the write was done before we release the spinlock + * Make sure the read was done before we release the spinlock * allowing consecutive reads/writes. */ mb(); -- cgit v1.2.3 From fca288c0153b2b97114b9081bc3c33c3735145b6 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Thu, 15 Mar 2018 14:21:43 +0000 Subject: PCI: hv: Remove the bogus test in hv_eject_device_work() When kernel is executing hv_eject_device_work(), hpdev->state value must be hv_pcichild_ejecting; any other value would consist in a bug, therefore replace the bogus check with an explicit WARN_ON() on the condition failure detection. Signed-off-by: Dexuan Cui [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Michael Kelley Acked-by: Haiyang Zhang Cc: Vitaly Kuznetsov Cc: Jack Morgenstein Cc: Stephen Hemminger Cc: K. Y. Srinivasan --- drivers/pci/host/pci-hyperv.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index 0d2a09833a05..0dc2ecdbbe45 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -1842,10 +1842,7 @@ static void hv_eject_device_work(struct work_struct *work) hpdev = container_of(work, struct hv_pci_dev, wrk); - if (hpdev->state != hv_pcichild_ejecting) { - put_pcichild(hpdev, hv_pcidev_ref_pnp); - return; - } + WARN_ON(hpdev->state != hv_pcichild_ejecting); /* * Ejection can come before or after the PCI bus has been set up, so -- cgit v1.2.3 From 948373b3ed1bcf05a237c24675b84804315aff14 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Thu, 15 Mar 2018 14:22:00 +0000 Subject: PCI: hv: Only queue new work items in hv_pci_devices_present() if necessary If there is pending work in hv_pci_devices_present() we just need to add the new dr entry into the dr_list. Add a check to detect pending work items and update the code to skip queuing work if pending work items are detected. Signed-off-by: Dexuan Cui [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Michael Kelley Acked-by: Haiyang Zhang Cc: Vitaly Kuznetsov Cc: Jack Morgenstein Cc: Stephen Hemminger Cc: K. Y. Srinivasan --- drivers/pci/host/pci-hyperv.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index 0dc2ecdbbe45..50cdefe3f6d3 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -1789,6 +1789,7 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus, struct hv_dr_state *dr; struct hv_dr_work *dr_wrk; unsigned long flags; + bool pending_dr; dr_wrk = kzalloc(sizeof(*dr_wrk), GFP_NOWAIT); if (!dr_wrk) @@ -1812,11 +1813,21 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus, } spin_lock_irqsave(&hbus->device_list_lock, flags); + /* + * If pending_dr is true, we have already queued a work, + * which will see the new dr. Otherwise, we need to + * queue a new work. + */ + pending_dr = !list_empty(&hbus->dr_list); list_add_tail(&dr->list_entry, &hbus->dr_list); spin_unlock_irqrestore(&hbus->device_list_lock, flags); - get_hvpcibus(hbus); - queue_work(hbus->wq, &dr_wrk->wrk); + if (pending_dr) { + kfree(dr_wrk); + } else { + get_hvpcibus(hbus); + queue_work(hbus->wq, &dr_wrk->wrk); + } } /** -- cgit v1.2.3 From da76ba50963b81413ffd3613f84ee9e592220b3d Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Wed, 28 Feb 2018 15:30:34 +0530 Subject: PCI: tegra: Add power management support Tegra186 powergate driver is implemented as power domain driver, power partition ungate/gate are registered as power_on/power_off callback functions. There are no direct functions to power gate/ungate host controller in Tegra186. Host controller driver should add "power-domains" property in device tree and implement runtime suspend and resume callback functons. Power gate and ungate is taken care by power domain driver when host controller driver calls pm_runtime_put_sync and pm_runtime_get_sync respectively. Register suspend_noirq & resume_noirq callback functions to allow PCIe to come up after resume from RAM. Both runtime and noirq pm ops share same callback functions. Signed-off-by: Manikanta Maddireddy [lorenzo.pieralisi@arm.com: squashed patch to fix compilation] Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding Tested-by: Thierry Reding --- drivers/pci/host/pci-tegra.c | 180 +++++++++++++++++++++++++++---------------- 1 file changed, 113 insertions(+), 67 deletions(-) diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index ab057f6f5153..389e74be846c 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -1280,31 +1280,25 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) } } - err = tegra_pcie_power_on(pcie); - if (err) { - dev_err(dev, "failed to power up: %d\n", err); - goto phys_put; - } - pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads"); pcie->pads = devm_ioremap_resource(dev, pads); if (IS_ERR(pcie->pads)) { err = PTR_ERR(pcie->pads); - goto poweroff; + goto phys_put; } afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi"); pcie->afi = devm_ioremap_resource(dev, afi); if (IS_ERR(pcie->afi)) { err = PTR_ERR(pcie->afi); - goto poweroff; + goto phys_put; } /* request configuration space, but remap later, on demand */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs"); if (!res) { err = -EADDRNOTAVAIL; - goto poweroff; + goto phys_put; } pcie->cs = *res; @@ -1315,14 +1309,14 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) pcie->cfg = devm_ioremap_resource(dev, &pcie->cs); if (IS_ERR(pcie->cfg)) { err = PTR_ERR(pcie->cfg); - goto poweroff; + goto phys_put; } /* request interrupt */ err = platform_get_irq_byname(pdev, "intr"); if (err < 0) { dev_err(dev, "failed to get IRQ: %d\n", err); - goto poweroff; + goto phys_put; } pcie->irq = err; @@ -1330,7 +1324,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie); if (err) { dev_err(dev, "failed to register IRQ: %d\n", err); - goto poweroff; + goto phys_put; } return 0; @@ -1338,8 +1332,6 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) phys_put: if (soc->program_uphy) tegra_pcie_phys_put(pcie); -poweroff: - tegra_pcie_power_off(pcie); return err; } @@ -1350,8 +1342,6 @@ static int tegra_pcie_put_resources(struct tegra_pcie *pcie) if (pcie->irq > 0) free_irq(pcie->irq, pcie); - tegra_pcie_power_off(pcie); - if (soc->program_uphy) tegra_pcie_phys_put(pcie); @@ -1520,15 +1510,13 @@ static const struct irq_domain_ops msi_domain_ops = { .map = tegra_msi_map, }; -static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) +static int tegra_pcie_msi_setup(struct tegra_pcie *pcie) { struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); struct platform_device *pdev = to_platform_device(pcie->dev); - const struct tegra_pcie_soc *soc = pcie->soc; struct tegra_msi *msi = &pcie->msi; struct device *dev = pcie->dev; int err; - u32 reg; mutex_init(&msi->lock); @@ -1561,6 +1549,20 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) /* setup AFI/FPCI range */ msi->pages = __get_free_pages(GFP_KERNEL, 0); msi->phys = virt_to_phys((void *)msi->pages); + host->msi = &msi->chip; + + return 0; + +err: + irq_domain_remove(msi->domain); + return err; +} + +static void tegra_pcie_enable_msi(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + struct tegra_msi *msi = &pcie->msi; + u32 reg; afi_writel(pcie, msi->phys >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST); afi_writel(pcie, msi->phys, AFI_MSI_AXI_BAR_ST); @@ -1581,20 +1583,29 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) reg = afi_readl(pcie, AFI_INTR_MASK); reg |= AFI_INTR_MASK_MSI_MASK; afi_writel(pcie, reg, AFI_INTR_MASK); +} - host->msi = &msi->chip; +static void tegra_pcie_msi_teardown(struct tegra_pcie *pcie) +{ + struct tegra_msi *msi = &pcie->msi; + unsigned int i, irq; - return 0; + free_pages(msi->pages, 0); + + if (msi->irq > 0) + free_irq(msi->irq, pcie); + + for (i = 0; i < INT_PCI_MSI_NR; i++) { + irq = irq_find_mapping(msi->domain, i); + if (irq > 0) + irq_dispose_mapping(irq); + } -err: irq_domain_remove(msi->domain); - return err; } static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) { - struct tegra_msi *msi = &pcie->msi; - unsigned int i, irq; u32 value; /* mask the MSI interrupt */ @@ -1612,19 +1623,6 @@ static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) afi_writel(pcie, 0, AFI_MSI_EN_VEC6); afi_writel(pcie, 0, AFI_MSI_EN_VEC7); - free_pages(msi->pages, 0); - - if (msi->irq > 0) - free_irq(msi->irq, pcie); - - for (i = 0; i < INT_PCI_MSI_NR; i++) { - irq = irq_find_mapping(msi->domain, i); - if (irq > 0) - irq_dispose_mapping(irq); - } - - irq_domain_remove(msi->domain); - return 0; } @@ -2123,10 +2121,8 @@ static void tegra_pcie_disable_ports(struct tegra_pcie *pcie) { struct tegra_pcie_port *port, *tmp; - list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + list_for_each_entry_safe(port, tmp, &pcie->ports, list) tegra_pcie_port_disable(port); - tegra_pcie_port_free(port); - } } static const struct tegra_pcie_port_soc tegra20_pcie_ports[] = { @@ -2381,26 +2377,22 @@ static int tegra_pcie_probe(struct platform_device *pdev) return err; } - err = tegra_pcie_enable_controller(pcie); - if (err) + err = tegra_pcie_msi_setup(pcie); + if (err < 0) { + dev_err(dev, "failed to enable MSI support: %d\n", err); goto put_resources; + } - err = tegra_pcie_request_resources(pcie); - if (err) - goto disable_controller; - - /* setup the AFI address translations */ - tegra_pcie_setup_translations(pcie); - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - err = tegra_pcie_enable_msi(pcie); - if (err < 0) { - dev_err(dev, "failed to enable MSI support: %d\n", err); - goto free_resources; - } + pm_runtime_enable(pcie->dev); + err = pm_runtime_get_sync(pcie->dev); + if (err) { + dev_err(dev, "fail to enable pcie controller: %d\n", err); + goto teardown_msi; } - tegra_pcie_enable_ports(pcie); + err = tegra_pcie_request_resources(pcie); + if (err) + goto pm_runtime_put; host->busnr = pcie->busn.start; host->dev.parent = &pdev->dev; @@ -2411,7 +2403,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) err = pci_scan_root_bus_bridge(host); if (err < 0) { dev_err(dev, "failed to register host: %d\n", err); - goto disable_ports; + goto free_resources; } pci_bus_size_bridges(host->bus); @@ -2430,14 +2422,13 @@ static int tegra_pcie_probe(struct platform_device *pdev) return 0; -disable_ports: - tegra_pcie_disable_ports(pcie); - if (IS_ENABLED(CONFIG_PCI_MSI)) - tegra_pcie_disable_msi(pcie); free_resources: tegra_pcie_free_resources(pcie); -disable_controller: - tegra_pcie_disable_controller(pcie); +pm_runtime_put: + pm_runtime_put_sync(pcie->dev); + pm_runtime_disable(pcie->dev); +teardown_msi: + tegra_pcie_msi_teardown(pcie); put_resources: tegra_pcie_put_resources(pcie); return err; @@ -2447,13 +2438,32 @@ static int tegra_pcie_remove(struct platform_device *pdev) { struct tegra_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); - struct tegra_pcie_port *port; + struct tegra_pcie_port *port, *tmp; if (IS_ENABLED(CONFIG_DEBUG_FS)) tegra_pcie_debugfs_exit(pcie); pci_stop_root_bus(host->bus); pci_remove_root_bus(host->bus); + tegra_pcie_free_resources(pcie); + pm_runtime_put_sync(pcie->dev); + pm_runtime_disable(pcie->dev); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + tegra_pcie_msi_teardown(pcie); + + tegra_pcie_put_resources(pcie); + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) + tegra_pcie_port_free(port); + + return 0; +} + +static int __maybe_unused tegra_pcie_pm_suspend(struct device *dev) +{ + struct tegra_pcie *pcie = dev_get_drvdata(dev); + struct tegra_pcie_port *port; list_for_each_entry(port, &pcie->ports, list) tegra_pcie_pme_turnoff(port); @@ -2463,18 +2473,54 @@ static int tegra_pcie_remove(struct platform_device *pdev) if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_disable_msi(pcie); - tegra_pcie_free_resources(pcie); tegra_pcie_disable_controller(pcie); - tegra_pcie_put_resources(pcie); + tegra_pcie_power_off(pcie); + + return 0; +} + +static int __maybe_unused tegra_pcie_pm_resume(struct device *dev) +{ + struct tegra_pcie *pcie = dev_get_drvdata(dev); + int err; + + err = tegra_pcie_power_on(pcie); + if (err) { + dev_err(dev, "tegra pcie power on fail: %d\n", err); + return err; + } + err = tegra_pcie_enable_controller(pcie); + if (err) { + dev_err(dev, "tegra pcie controller enable fail: %d\n", err); + goto poweroff; + } + tegra_pcie_setup_translations(pcie); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + tegra_pcie_enable_msi(pcie); + + tegra_pcie_enable_ports(pcie); return 0; + +poweroff: + tegra_pcie_power_off(pcie); + + return err; } +static const struct dev_pm_ops tegra_pcie_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_pcie_pm_suspend, tegra_pcie_pm_resume, NULL) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_pcie_pm_suspend, + tegra_pcie_pm_resume) +}; + static struct platform_driver tegra_pcie_driver = { .driver = { .name = "tegra-pcie", .of_match_table = tegra_pcie_of_match, .suppress_bind_attrs = true, + .pm = &tegra_pcie_pm_ops, }, .probe = tegra_pcie_probe, .remove = tegra_pcie_remove, -- cgit v1.2.3 From f0eb77ae6b857bf8118f7a8ee0a8ba076feed70d Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 19 Mar 2018 13:06:11 -0500 Subject: PCI/VPD: Move VPD access code to vpd.c Move the VPD-related code from access.c to vpd.c. The goal is to encapsulate all the VPD code and structures in vpd.c. No functional change intended. Signed-off-by: Bjorn Helgaas --- drivers/pci/access.c | 368 -------------------------------------------------- drivers/pci/vpd.c | 369 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 369 insertions(+), 368 deletions(-) diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 5e9a9822d9d4..e080eb74bda0 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include #include -#include #include #include #include @@ -264,372 +262,6 @@ PCI_USER_WRITE_CONFIG(byte, u8) PCI_USER_WRITE_CONFIG(word, u16) PCI_USER_WRITE_CONFIG(dword, u32) -/* VPD access through PCI 2.2+ VPD capability */ - -/** - * pci_read_vpd - Read one entry from Vital Product Data - * @dev: pci device struct - * @pos: offset in vpd space - * @count: number of bytes to read - * @buf: pointer to where to store result - */ -ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf) -{ - if (!dev->vpd || !dev->vpd->ops) - return -ENODEV; - return dev->vpd->ops->read(dev, pos, count, buf); -} -EXPORT_SYMBOL(pci_read_vpd); - -/** - * pci_write_vpd - Write entry to Vital Product Data - * @dev: pci device struct - * @pos: offset in vpd space - * @count: number of bytes to write - * @buf: buffer containing write data - */ -ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf) -{ - if (!dev->vpd || !dev->vpd->ops) - return -ENODEV; - return dev->vpd->ops->write(dev, pos, count, buf); -} -EXPORT_SYMBOL(pci_write_vpd); - -/** - * pci_set_vpd_size - Set size of Vital Product Data space - * @dev: pci device struct - * @len: size of vpd space - */ -int pci_set_vpd_size(struct pci_dev *dev, size_t len) -{ - if (!dev->vpd || !dev->vpd->ops) - return -ENODEV; - return dev->vpd->ops->set_size(dev, len); -} -EXPORT_SYMBOL(pci_set_vpd_size); - -#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1) - -/** - * pci_vpd_size - determine actual size of Vital Product Data - * @dev: pci device struct - * @old_size: current assumed size, also maximum allowed size - */ -static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) -{ - size_t off = 0; - unsigned char header[1+2]; /* 1 byte tag, 2 bytes length */ - - while (off < old_size && - pci_read_vpd(dev, off, 1, header) == 1) { - unsigned char tag; - - if (header[0] & PCI_VPD_LRDT) { - /* Large Resource Data Type Tag */ - tag = pci_vpd_lrdt_tag(header); - /* Only read length from known tag items */ - if ((tag == PCI_VPD_LTIN_ID_STRING) || - (tag == PCI_VPD_LTIN_RO_DATA) || - (tag == PCI_VPD_LTIN_RW_DATA)) { - if (pci_read_vpd(dev, off+1, 2, - &header[1]) != 2) { - pci_warn(dev, "invalid large VPD tag %02x size at offset %zu", - tag, off + 1); - return 0; - } - off += PCI_VPD_LRDT_TAG_SIZE + - pci_vpd_lrdt_size(header); - } - } else { - /* Short Resource Data Type Tag */ - off += PCI_VPD_SRDT_TAG_SIZE + - pci_vpd_srdt_size(header); - tag = pci_vpd_srdt_tag(header); - } - - if (tag == PCI_VPD_STIN_END) /* End tag descriptor */ - return off; - - if ((tag != PCI_VPD_LTIN_ID_STRING) && - (tag != PCI_VPD_LTIN_RO_DATA) && - (tag != PCI_VPD_LTIN_RW_DATA)) { - pci_warn(dev, "invalid %s VPD tag %02x at offset %zu", - (header[0] & PCI_VPD_LRDT) ? "large" : "short", - tag, off); - return 0; - } - } - return 0; -} - -/* - * Wait for last operation to complete. - * This code has to spin since there is no other notification from the PCI - * hardware. Since the VPD is often implemented by serial attachment to an - * EEPROM, it may take many milliseconds to complete. - * - * Returns 0 on success, negative values indicate error. - */ -static int pci_vpd_wait(struct pci_dev *dev) -{ - struct pci_vpd *vpd = dev->vpd; - unsigned long timeout = jiffies + msecs_to_jiffies(125); - unsigned long max_sleep = 16; - u16 status; - int ret; - - if (!vpd->busy) - return 0; - - while (time_before(jiffies, timeout)) { - ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR, - &status); - if (ret < 0) - return ret; - - if ((status & PCI_VPD_ADDR_F) == vpd->flag) { - vpd->busy = 0; - return 0; - } - - if (fatal_signal_pending(current)) - return -EINTR; - - usleep_range(10, max_sleep); - if (max_sleep < 1024) - max_sleep *= 2; - } - - pci_warn(dev, "VPD access failed. This is likely a firmware bug on this device. Contact the card vendor for a firmware update\n"); - return -ETIMEDOUT; -} - -static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, - void *arg) -{ - struct pci_vpd *vpd = dev->vpd; - int ret; - loff_t end = pos + count; - u8 *buf = arg; - - if (pos < 0) - return -EINVAL; - - if (!vpd->valid) { - vpd->valid = 1; - vpd->len = pci_vpd_size(dev, vpd->len); - } - - if (vpd->len == 0) - return -EIO; - - if (pos > vpd->len) - return 0; - - if (end > vpd->len) { - end = vpd->len; - count = end - pos; - } - - if (mutex_lock_killable(&vpd->lock)) - return -EINTR; - - ret = pci_vpd_wait(dev); - if (ret < 0) - goto out; - - while (pos < end) { - u32 val; - unsigned int i, skip; - - ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, - pos & ~3); - if (ret < 0) - break; - vpd->busy = 1; - vpd->flag = PCI_VPD_ADDR_F; - ret = pci_vpd_wait(dev); - if (ret < 0) - break; - - ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, &val); - if (ret < 0) - break; - - skip = pos & 3; - for (i = 0; i < sizeof(u32); i++) { - if (i >= skip) { - *buf++ = val; - if (++pos == end) - break; - } - val >>= 8; - } - } -out: - mutex_unlock(&vpd->lock); - return ret ? ret : count; -} - -static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, - const void *arg) -{ - struct pci_vpd *vpd = dev->vpd; - const u8 *buf = arg; - loff_t end = pos + count; - int ret = 0; - - if (pos < 0 || (pos & 3) || (count & 3)) - return -EINVAL; - - if (!vpd->valid) { - vpd->valid = 1; - vpd->len = pci_vpd_size(dev, vpd->len); - } - - if (vpd->len == 0) - return -EIO; - - if (end > vpd->len) - return -EINVAL; - - if (mutex_lock_killable(&vpd->lock)) - return -EINTR; - - ret = pci_vpd_wait(dev); - if (ret < 0) - goto out; - - while (pos < end) { - u32 val; - - val = *buf++; - val |= *buf++ << 8; - val |= *buf++ << 16; - val |= *buf++ << 24; - - ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val); - if (ret < 0) - break; - ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, - pos | PCI_VPD_ADDR_F); - if (ret < 0) - break; - - vpd->busy = 1; - vpd->flag = 0; - ret = pci_vpd_wait(dev); - if (ret < 0) - break; - - pos += sizeof(u32); - } -out: - mutex_unlock(&vpd->lock); - return ret ? ret : count; -} - -static int pci_vpd_set_size(struct pci_dev *dev, size_t len) -{ - struct pci_vpd *vpd = dev->vpd; - - if (len == 0 || len > PCI_VPD_MAX_SIZE) - return -EIO; - - vpd->valid = 1; - vpd->len = len; - - return 0; -} - -static const struct pci_vpd_ops pci_vpd_ops = { - .read = pci_vpd_read, - .write = pci_vpd_write, - .set_size = pci_vpd_set_size, -}; - -static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count, - void *arg) -{ - struct pci_dev *tdev = pci_get_slot(dev->bus, - PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); - ssize_t ret; - - if (!tdev) - return -ENODEV; - - ret = pci_read_vpd(tdev, pos, count, arg); - pci_dev_put(tdev); - return ret; -} - -static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count, - const void *arg) -{ - struct pci_dev *tdev = pci_get_slot(dev->bus, - PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); - ssize_t ret; - - if (!tdev) - return -ENODEV; - - ret = pci_write_vpd(tdev, pos, count, arg); - pci_dev_put(tdev); - return ret; -} - -static int pci_vpd_f0_set_size(struct pci_dev *dev, size_t len) -{ - struct pci_dev *tdev = pci_get_slot(dev->bus, - PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); - int ret; - - if (!tdev) - return -ENODEV; - - ret = pci_set_vpd_size(tdev, len); - pci_dev_put(tdev); - return ret; -} - -static const struct pci_vpd_ops pci_vpd_f0_ops = { - .read = pci_vpd_f0_read, - .write = pci_vpd_f0_write, - .set_size = pci_vpd_f0_set_size, -}; - -int pci_vpd_init(struct pci_dev *dev) -{ - struct pci_vpd *vpd; - u8 cap; - - cap = pci_find_capability(dev, PCI_CAP_ID_VPD); - if (!cap) - return -ENODEV; - - vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC); - if (!vpd) - return -ENOMEM; - - vpd->len = PCI_VPD_MAX_SIZE; - if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) - vpd->ops = &pci_vpd_f0_ops; - else - vpd->ops = &pci_vpd_ops; - mutex_init(&vpd->lock); - vpd->cap = cap; - vpd->busy = 0; - vpd->valid = 0; - dev->vpd = vpd; - return 0; -} - -void pci_vpd_release(struct pci_dev *dev) -{ - kfree(dev->vpd); -} - /** * pci_cfg_access_lock - Lock PCI config reads/writes * @dev: pci device struct diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 70fba57d6103..4596452d58bf 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -7,7 +7,376 @@ */ #include +#include #include +#include +#include "pci.h" + +/* VPD access through PCI 2.2+ VPD capability */ + +/** + * pci_read_vpd - Read one entry from Vital Product Data + * @dev: pci device struct + * @pos: offset in vpd space + * @count: number of bytes to read + * @buf: pointer to where to store result + */ +ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf) +{ + if (!dev->vpd || !dev->vpd->ops) + return -ENODEV; + return dev->vpd->ops->read(dev, pos, count, buf); +} +EXPORT_SYMBOL(pci_read_vpd); + +/** + * pci_write_vpd - Write entry to Vital Product Data + * @dev: pci device struct + * @pos: offset in vpd space + * @count: number of bytes to write + * @buf: buffer containing write data + */ +ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf) +{ + if (!dev->vpd || !dev->vpd->ops) + return -ENODEV; + return dev->vpd->ops->write(dev, pos, count, buf); +} +EXPORT_SYMBOL(pci_write_vpd); + +/** + * pci_set_vpd_size - Set size of Vital Product Data space + * @dev: pci device struct + * @len: size of vpd space + */ +int pci_set_vpd_size(struct pci_dev *dev, size_t len) +{ + if (!dev->vpd || !dev->vpd->ops) + return -ENODEV; + return dev->vpd->ops->set_size(dev, len); +} +EXPORT_SYMBOL(pci_set_vpd_size); + +#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1) + +/** + * pci_vpd_size - determine actual size of Vital Product Data + * @dev: pci device struct + * @old_size: current assumed size, also maximum allowed size + */ +static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) +{ + size_t off = 0; + unsigned char header[1+2]; /* 1 byte tag, 2 bytes length */ + + while (off < old_size && + pci_read_vpd(dev, off, 1, header) == 1) { + unsigned char tag; + + if (header[0] & PCI_VPD_LRDT) { + /* Large Resource Data Type Tag */ + tag = pci_vpd_lrdt_tag(header); + /* Only read length from known tag items */ + if ((tag == PCI_VPD_LTIN_ID_STRING) || + (tag == PCI_VPD_LTIN_RO_DATA) || + (tag == PCI_VPD_LTIN_RW_DATA)) { + if (pci_read_vpd(dev, off+1, 2, + &header[1]) != 2) { + pci_warn(dev, "invalid large VPD tag %02x size at offset %zu", + tag, off + 1); + return 0; + } + off += PCI_VPD_LRDT_TAG_SIZE + + pci_vpd_lrdt_size(header); + } + } else { + /* Short Resource Data Type Tag */ + off += PCI_VPD_SRDT_TAG_SIZE + + pci_vpd_srdt_size(header); + tag = pci_vpd_srdt_tag(header); + } + + if (tag == PCI_VPD_STIN_END) /* End tag descriptor */ + return off; + + if ((tag != PCI_VPD_LTIN_ID_STRING) && + (tag != PCI_VPD_LTIN_RO_DATA) && + (tag != PCI_VPD_LTIN_RW_DATA)) { + pci_warn(dev, "invalid %s VPD tag %02x at offset %zu", + (header[0] & PCI_VPD_LRDT) ? "large" : "short", + tag, off); + return 0; + } + } + return 0; +} + +/* + * Wait for last operation to complete. + * This code has to spin since there is no other notification from the PCI + * hardware. Since the VPD is often implemented by serial attachment to an + * EEPROM, it may take many milliseconds to complete. + * + * Returns 0 on success, negative values indicate error. + */ +static int pci_vpd_wait(struct pci_dev *dev) +{ + struct pci_vpd *vpd = dev->vpd; + unsigned long timeout = jiffies + msecs_to_jiffies(125); + unsigned long max_sleep = 16; + u16 status; + int ret; + + if (!vpd->busy) + return 0; + + while (time_before(jiffies, timeout)) { + ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR, + &status); + if (ret < 0) + return ret; + + if ((status & PCI_VPD_ADDR_F) == vpd->flag) { + vpd->busy = 0; + return 0; + } + + if (fatal_signal_pending(current)) + return -EINTR; + + usleep_range(10, max_sleep); + if (max_sleep < 1024) + max_sleep *= 2; + } + + pci_warn(dev, "VPD access failed. This is likely a firmware bug on this device. Contact the card vendor for a firmware update\n"); + return -ETIMEDOUT; +} + +static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, + void *arg) +{ + struct pci_vpd *vpd = dev->vpd; + int ret; + loff_t end = pos + count; + u8 *buf = arg; + + if (pos < 0) + return -EINVAL; + + if (!vpd->valid) { + vpd->valid = 1; + vpd->len = pci_vpd_size(dev, vpd->len); + } + + if (vpd->len == 0) + return -EIO; + + if (pos > vpd->len) + return 0; + + if (end > vpd->len) { + end = vpd->len; + count = end - pos; + } + + if (mutex_lock_killable(&vpd->lock)) + return -EINTR; + + ret = pci_vpd_wait(dev); + if (ret < 0) + goto out; + + while (pos < end) { + u32 val; + unsigned int i, skip; + + ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, + pos & ~3); + if (ret < 0) + break; + vpd->busy = 1; + vpd->flag = PCI_VPD_ADDR_F; + ret = pci_vpd_wait(dev); + if (ret < 0) + break; + + ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, &val); + if (ret < 0) + break; + + skip = pos & 3; + for (i = 0; i < sizeof(u32); i++) { + if (i >= skip) { + *buf++ = val; + if (++pos == end) + break; + } + val >>= 8; + } + } +out: + mutex_unlock(&vpd->lock); + return ret ? ret : count; +} + +static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, + const void *arg) +{ + struct pci_vpd *vpd = dev->vpd; + const u8 *buf = arg; + loff_t end = pos + count; + int ret = 0; + + if (pos < 0 || (pos & 3) || (count & 3)) + return -EINVAL; + + if (!vpd->valid) { + vpd->valid = 1; + vpd->len = pci_vpd_size(dev, vpd->len); + } + + if (vpd->len == 0) + return -EIO; + + if (end > vpd->len) + return -EINVAL; + + if (mutex_lock_killable(&vpd->lock)) + return -EINTR; + + ret = pci_vpd_wait(dev); + if (ret < 0) + goto out; + + while (pos < end) { + u32 val; + + val = *buf++; + val |= *buf++ << 8; + val |= *buf++ << 16; + val |= *buf++ << 24; + + ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val); + if (ret < 0) + break; + ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, + pos | PCI_VPD_ADDR_F); + if (ret < 0) + break; + + vpd->busy = 1; + vpd->flag = 0; + ret = pci_vpd_wait(dev); + if (ret < 0) + break; + + pos += sizeof(u32); + } +out: + mutex_unlock(&vpd->lock); + return ret ? ret : count; +} + +static int pci_vpd_set_size(struct pci_dev *dev, size_t len) +{ + struct pci_vpd *vpd = dev->vpd; + + if (len == 0 || len > PCI_VPD_MAX_SIZE) + return -EIO; + + vpd->valid = 1; + vpd->len = len; + + return 0; +} + +static const struct pci_vpd_ops pci_vpd_ops = { + .read = pci_vpd_read, + .write = pci_vpd_write, + .set_size = pci_vpd_set_size, +}; + +static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count, + void *arg) +{ + struct pci_dev *tdev = pci_get_slot(dev->bus, + PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + ssize_t ret; + + if (!tdev) + return -ENODEV; + + ret = pci_read_vpd(tdev, pos, count, arg); + pci_dev_put(tdev); + return ret; +} + +static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count, + const void *arg) +{ + struct pci_dev *tdev = pci_get_slot(dev->bus, + PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + ssize_t ret; + + if (!tdev) + return -ENODEV; + + ret = pci_write_vpd(tdev, pos, count, arg); + pci_dev_put(tdev); + return ret; +} + +static int pci_vpd_f0_set_size(struct pci_dev *dev, size_t len) +{ + struct pci_dev *tdev = pci_get_slot(dev->bus, + PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + int ret; + + if (!tdev) + return -ENODEV; + + ret = pci_set_vpd_size(tdev, len); + pci_dev_put(tdev); + return ret; +} + +static const struct pci_vpd_ops pci_vpd_f0_ops = { + .read = pci_vpd_f0_read, + .write = pci_vpd_f0_write, + .set_size = pci_vpd_f0_set_size, +}; + +int pci_vpd_init(struct pci_dev *dev) +{ + struct pci_vpd *vpd; + u8 cap; + + cap = pci_find_capability(dev, PCI_CAP_ID_VPD); + if (!cap) + return -ENODEV; + + vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC); + if (!vpd) + return -ENOMEM; + + vpd->len = PCI_VPD_MAX_SIZE; + if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) + vpd->ops = &pci_vpd_f0_ops; + else + vpd->ops = &pci_vpd_ops; + mutex_init(&vpd->lock); + vpd->cap = cap; + vpd->busy = 0; + vpd->valid = 0; + dev->vpd = vpd; + return 0; +} + +void pci_vpd_release(struct pci_dev *dev) +{ + kfree(dev->vpd); +} int pci_vpd_find_tag(const u8 *buf, unsigned int off, unsigned int len, u8 rdt) { -- cgit v1.2.3 From b1c615c48fa93db64310e8d1a457b364a486fde8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 19 Mar 2018 13:06:17 -0500 Subject: PCI/VPD: Move VPD sysfs code to vpd.c Move the VPD-related sysfs code from pci-sysfs.c to vpd.c. This follows the pattern of pcie_aspm_create_sysfs_dev_files(). The goal is to encapsulate all the VPD code and structures in vpd.c. No functional change intended. Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-sysfs.c | 67 +++---------------------------------------------- drivers/pci/pci.h | 2 ++ drivers/pci/vpd.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 64 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index eb6bee8724cc..4415e624cf7e 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -982,38 +982,6 @@ static ssize_t pci_write_config(struct file *filp, struct kobject *kobj, return count; } -static ssize_t read_vpd_attr(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, - loff_t off, size_t count) -{ - struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); - - if (bin_attr->size > 0) { - if (off > bin_attr->size) - count = 0; - else if (count > bin_attr->size - off) - count = bin_attr->size - off; - } - - return pci_read_vpd(dev, off, count, buf); -} - -static ssize_t write_vpd_attr(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, - loff_t off, size_t count) -{ - struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); - - if (bin_attr->size > 0) { - if (off > bin_attr->size) - count = 0; - else if (count > bin_attr->size - off) - count = bin_attr->size - off; - } - - return pci_write_vpd(dev, off, count, buf); -} - #ifdef HAVE_PCI_LEGACY /** * pci_read_legacy_io - read byte(s) from legacy I/O port space @@ -1517,29 +1485,8 @@ static struct device_attribute reset_attr = __ATTR(reset, 0200, NULL, reset_stor static int pci_create_capabilities_sysfs(struct pci_dev *dev) { int retval; - struct bin_attribute *attr; - - /* If the device has VPD, try to expose it in sysfs. */ - if (dev->vpd) { - attr = kzalloc(sizeof(*attr), GFP_ATOMIC); - if (!attr) - return -ENOMEM; - sysfs_bin_attr_init(attr); - attr->size = 0; - attr->attr.name = "vpd"; - attr->attr.mode = S_IRUSR | S_IWUSR; - attr->read = read_vpd_attr; - attr->write = write_vpd_attr; - retval = sysfs_create_bin_file(&dev->dev.kobj, attr); - if (retval) { - kfree(attr); - return retval; - } - dev->vpd->attr = attr; - } - - /* Active State Power Management */ + pcie_vpd_create_sysfs_dev_files(dev); pcie_aspm_create_sysfs_dev_files(dev); if (!pci_probe_reset_function(dev)) { @@ -1552,11 +1499,7 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev) error: pcie_aspm_remove_sysfs_dev_files(dev); - if (dev->vpd && dev->vpd->attr) { - sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr); - kfree(dev->vpd->attr); - } - + pcie_vpd_remove_sysfs_dev_files(dev); return retval; } @@ -1630,11 +1573,7 @@ err: static void pci_remove_capabilities_sysfs(struct pci_dev *dev) { - if (dev->vpd && dev->vpd->attr) { - sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr); - kfree(dev->vpd->attr); - } - + pcie_vpd_remove_sysfs_dev_files(dev); pcie_aspm_remove_sysfs_dev_files(dev); if (dev->reset_fn) { device_remove_file(&dev->dev, &reset_attr); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index fcd81911b127..1191320a44d5 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -123,6 +123,8 @@ struct pci_vpd { int pci_vpd_init(struct pci_dev *dev); void pci_vpd_release(struct pci_dev *dev); +void pcie_vpd_create_sysfs_dev_files(struct pci_dev *dev); +void pcie_vpd_remove_sysfs_dev_files(struct pci_dev *dev); /* PCI /proc functions */ #ifdef CONFIG_PROC_FS diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 4596452d58bf..55477f24d8b9 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -378,6 +378,73 @@ void pci_vpd_release(struct pci_dev *dev) kfree(dev->vpd); } +static ssize_t read_vpd_attr(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); + + if (bin_attr->size > 0) { + if (off > bin_attr->size) + count = 0; + else if (count > bin_attr->size - off) + count = bin_attr->size - off; + } + + return pci_read_vpd(dev, off, count, buf); +} + +static ssize_t write_vpd_attr(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); + + if (bin_attr->size > 0) { + if (off > bin_attr->size) + count = 0; + else if (count > bin_attr->size - off) + count = bin_attr->size - off; + } + + return pci_write_vpd(dev, off, count, buf); +} + +void pcie_vpd_create_sysfs_dev_files(struct pci_dev *dev) +{ + int retval; + struct bin_attribute *attr; + + if (!dev->vpd) + return; + + attr = kzalloc(sizeof(*attr), GFP_ATOMIC); + if (!attr) + return; + + sysfs_bin_attr_init(attr); + attr->size = 0; + attr->attr.name = "vpd"; + attr->attr.mode = S_IRUSR | S_IWUSR; + attr->read = read_vpd_attr; + attr->write = write_vpd_attr; + retval = sysfs_create_bin_file(&dev->dev.kobj, attr); + if (retval) { + kfree(attr); + return; + } + + dev->vpd->attr = attr; +} + +void pcie_vpd_remove_sysfs_dev_files(struct pci_dev *dev) +{ + if (dev->vpd && dev->vpd->attr) { + sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr); + kfree(dev->vpd->attr); + } +} + int pci_vpd_find_tag(const u8 *buf, unsigned int off, unsigned int len, u8 rdt) { int i; -- cgit v1.2.3 From 996058573b22a7d4e54e281fc624db4b32d85eb4 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 19 Mar 2018 13:06:24 -0500 Subject: PCI/VPD: Move VPD quirks to vpd.c Move the VPD-related quirks from quirks.c to vpd.c, which removes the need for struct pci_vpd outside vpd.c. The goal is to encapsulate all the VPD code and structures in vpd.c. No functional change intended. Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 121 --------------------------------------------------- drivers/pci/vpd.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 121 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index fc734014206f..fbe1127086c8 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1967,31 +1967,6 @@ static void quirk_netmos(struct pci_dev *dev) DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID, PCI_CLASS_COMMUNICATION_SERIAL, 8, quirk_netmos); -/* - * Quirk non-zero PCI functions to route VPD access through function 0 for - * devices that share VPD resources between functions. The functions are - * expected to be identical devices. - */ -static void quirk_f0_vpd_link(struct pci_dev *dev) -{ - struct pci_dev *f0; - - if (!PCI_FUNC(dev->devfn)) - return; - - f0 = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); - if (!f0) - return; - - if (f0->vpd && dev->class == f0->class && - dev->vendor == f0->vendor && dev->device == f0->device) - dev->dev_flags |= PCI_DEV_FLAGS_VPD_REF_F0; - - pci_dev_put(f0); -} -DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, - PCI_CLASS_NETWORK_ETHERNET, 8, quirk_f0_vpd_link); - static void quirk_e100_interrupt(struct pci_dev *dev) { u16 command, pmcsr; @@ -2182,83 +2157,6 @@ static void quirk_via_cx700_pci_parking_caching(struct pci_dev *dev) } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching); -/* - * If a device follows the VPD format spec, the PCI core will not read or - * write past the VPD End Tag. But some vendors do not follow the VPD - * format spec, so we can't tell how much data is safe to access. Devices - * may behave unpredictably if we access too much. Blacklist these devices - * so we don't touch VPD at all. - */ -static void quirk_blacklist_vpd(struct pci_dev *dev) -{ - if (dev->vpd) { - dev->vpd->len = 0; - pci_warn(dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n"); - } -} - -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID, - quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_QLOGIC, 0x2261, quirk_blacklist_vpd); - -/* - * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the - * VPD end tag will hang the device. This problem was initially - * observed when a vpd entry was created in sysfs - * ('/sys/bus/pci/devices//vpd'). A read to this sysfs entry - * will dump 32k of data. Reading a full 32k will cause an access - * beyond the VPD end tag causing the device to hang. Once the device - * is hung, the bnx2 driver will not be able to reset the device. - * We believe that it is legal to read beyond the end tag and - * therefore the solution is to limit the read/write length. - */ -static void quirk_brcm_570x_limit_vpd(struct pci_dev *dev) -{ - /* - * Only disable the VPD capability for 5706, 5706S, 5708, - * 5708S and 5709 rev. A - */ - if ((dev->device == PCI_DEVICE_ID_NX2_5706) || - (dev->device == PCI_DEVICE_ID_NX2_5706S) || - (dev->device == PCI_DEVICE_ID_NX2_5708) || - (dev->device == PCI_DEVICE_ID_NX2_5708S) || - ((dev->device == PCI_DEVICE_ID_NX2_5709) && - (dev->revision & 0xf0) == 0x0)) { - if (dev->vpd) - dev->vpd->len = 0x80; - } -} - -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5706, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5706S, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5708, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5708S, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5709, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5709S, - quirk_brcm_570x_limit_vpd); - static void quirk_brcm_5719_limit_mrrs(struct pci_dev *dev) { u32 rev; @@ -3417,25 +3315,6 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PORT_RIDGE, quirk_thunderbolt_hotplug_msi); -static void quirk_chelsio_extend_vpd(struct pci_dev *dev) -{ - pci_set_vpd_size(dev, 8192); -} - -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x20, quirk_chelsio_extend_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x21, quirk_chelsio_extend_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x22, quirk_chelsio_extend_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x23, quirk_chelsio_extend_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x24, quirk_chelsio_extend_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x25, quirk_chelsio_extend_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x26, quirk_chelsio_extend_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x30, quirk_chelsio_extend_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x31, quirk_chelsio_extend_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x32, quirk_chelsio_extend_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x35, quirk_chelsio_extend_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x36, quirk_chelsio_extend_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x37, quirk_chelsio_extend_vpd); - #ifdef CONFIG_ACPI /* * Apple: Shutdown Cactus Ridge Thunderbolt controller. diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 55477f24d8b9..901b0bedcc5d 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -497,3 +497,123 @@ int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off, return -ENOENT; } EXPORT_SYMBOL_GPL(pci_vpd_find_info_keyword); + +#ifdef CONFIG_PCI_QUIRKS +/* + * Quirk non-zero PCI functions to route VPD access through function 0 for + * devices that share VPD resources between functions. The functions are + * expected to be identical devices. + */ +static void quirk_f0_vpd_link(struct pci_dev *dev) +{ + struct pci_dev *f0; + + if (!PCI_FUNC(dev->devfn)) + return; + + f0 = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + if (!f0) + return; + + if (f0->vpd && dev->class == f0->class && + dev->vendor == f0->vendor && dev->device == f0->device) + dev->dev_flags |= PCI_DEV_FLAGS_VPD_REF_F0; + + pci_dev_put(f0); +} +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, + PCI_CLASS_NETWORK_ETHERNET, 8, quirk_f0_vpd_link); + +/* + * If a device follows the VPD format spec, the PCI core will not read or + * write past the VPD End Tag. But some vendors do not follow the VPD + * format spec, so we can't tell how much data is safe to access. Devices + * may behave unpredictably if we access too much. Blacklist these devices + * so we don't touch VPD at all. + */ +static void quirk_blacklist_vpd(struct pci_dev *dev) +{ + if (dev->vpd) { + dev->vpd->len = 0; + pci_warn(dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n"); + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID, + quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_QLOGIC, 0x2261, quirk_blacklist_vpd); + +/* + * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the + * VPD end tag will hang the device. This problem was initially + * observed when a vpd entry was created in sysfs + * ('/sys/bus/pci/devices//vpd'). A read to this sysfs entry + * will dump 32k of data. Reading a full 32k will cause an access + * beyond the VPD end tag causing the device to hang. Once the device + * is hung, the bnx2 driver will not be able to reset the device. + * We believe that it is legal to read beyond the end tag and + * therefore the solution is to limit the read/write length. + */ +static void quirk_brcm_570x_limit_vpd(struct pci_dev *dev) +{ + /* + * Only disable the VPD capability for 5706, 5706S, 5708, + * 5708S and 5709 rev. A + */ + if ((dev->device == PCI_DEVICE_ID_NX2_5706) || + (dev->device == PCI_DEVICE_ID_NX2_5706S) || + (dev->device == PCI_DEVICE_ID_NX2_5708) || + (dev->device == PCI_DEVICE_ID_NX2_5708S) || + ((dev->device == PCI_DEVICE_ID_NX2_5709) && + (dev->revision & 0xf0) == 0x0)) { + if (dev->vpd) + dev->vpd->len = 0x80; + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5706, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5706S, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5708, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5708S, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5709, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5709S, + quirk_brcm_570x_limit_vpd); + +static void quirk_chelsio_extend_vpd(struct pci_dev *dev) +{ + pci_set_vpd_size(dev, 8192); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x20, quirk_chelsio_extend_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x21, quirk_chelsio_extend_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x22, quirk_chelsio_extend_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x23, quirk_chelsio_extend_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x24, quirk_chelsio_extend_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x25, quirk_chelsio_extend_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x26, quirk_chelsio_extend_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x30, quirk_chelsio_extend_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x31, quirk_chelsio_extend_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x32, quirk_chelsio_extend_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x35, quirk_chelsio_extend_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x36, quirk_chelsio_extend_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x37, quirk_chelsio_extend_vpd); +#endif -- cgit v1.2.3 From f9ea894ca59a7abd931e52700bbe12e87e891c1e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 19 Mar 2018 13:06:34 -0500 Subject: PCI/VPD: Move VPD structures to vpd.c The VPD-related structures are only used in vpd.c, so move them from drivers/pci/pci.h to vpd.c. No functional change intended. Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.h | 17 ----------------- drivers/pci/vpd.c | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 1191320a44d5..9a41a6399967 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -104,23 +104,6 @@ static inline bool pci_power_manageable(struct pci_dev *pci_dev) return !pci_has_subordinate(pci_dev) || pci_dev->bridge_d3; } -struct pci_vpd_ops { - ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); - ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); - int (*set_size)(struct pci_dev *dev, size_t len); -}; - -struct pci_vpd { - const struct pci_vpd_ops *ops; - struct bin_attribute *attr; /* Descriptor for sysfs VPD entry */ - struct mutex lock; - unsigned int len; - u16 flag; - u8 cap; - u8 busy:1; - u8 valid:1; -}; - int pci_vpd_init(struct pci_dev *dev); void pci_vpd_release(struct pci_dev *dev); void pcie_vpd_create_sysfs_dev_files(struct pci_dev *dev); diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 901b0bedcc5d..f24c3600be73 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -14,6 +14,23 @@ /* VPD access through PCI 2.2+ VPD capability */ +struct pci_vpd_ops { + ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); + ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); + int (*set_size)(struct pci_dev *dev, size_t len); +}; + +struct pci_vpd { + const struct pci_vpd_ops *ops; + struct bin_attribute *attr; /* Descriptor for sysfs VPD entry */ + struct mutex lock; + unsigned int len; + u16 flag; + u8 cap; + unsigned int busy:1; + unsigned int valid:1; +}; + /** * pci_read_vpd - Read one entry from Vital Product Data * @dev: pci device struct -- cgit v1.2.3 From d89bd9195db62f51a5aaa88d887d81a7967554bd Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 14 Mar 2018 07:42:56 -0500 Subject: PCI: Report quirk timings with pci_info() instead of pr_debug() With "initcall_debug", we report how long every PCI quirk took. Previously we used pr_debug(), which means you have to figure out how to enable debug output. Log these timings using pci_info() instead so it doesn't depend on DEBUG, CONFIG_DYNAMIC_DEBUG, etc. Also, don't log anything at all unless "initcall_debug" is specified. This matches what we do in do_one_initcall_debug(). Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index fc734014206f..4c6825fc8b0f 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3106,10 +3106,8 @@ static ktime_t fixup_debug_start(struct pci_dev *dev, { ktime_t calltime = 0; - pci_dbg(dev, "calling %pF\n", fn); if (initcall_debug) { - pr_debug("calling %pF @ %i for %s\n", - fn, task_pid_nr(current), dev_name(&dev->dev)); + pci_info(dev, "calling %pF @ %i\n", fn, task_pid_nr(current)); calltime = ktime_get(); } @@ -3126,8 +3124,7 @@ static void fixup_debug_report(struct pci_dev *dev, ktime_t calltime, rettime = ktime_get(); delta = ktime_sub(rettime, calltime); duration = (unsigned long long) ktime_to_ns(delta) >> 10; - pr_debug("pci fixup %pF returned after %lld usecs for %s\n", - fn, duration, dev_name(&dev->dev)); + pci_info(dev, "%pF took %lld usecs\n", fn, duration); } } -- cgit v1.2.3 From 6846b3b5127a9e041f18f0019c38c1fd08acfbaa Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 14 Mar 2018 07:52:30 -0500 Subject: PCI: Report quirks that take more than 10ms With "initcall_debug", we report how long every PCI quirk took. Even without "initcall_debug", report the runtime of any quirk that takes longer than 10ms. This is to make it easier to notice quirks that slow down boot. This was motivated by a report from Paul Menzel that PCI final quirks took half a second at boot. Link: https://lkml.kernel.org/r/44cada166e42007d27b4c3e3aa0744d7@molgen.mpg.de Reported-by: Paul Menzel Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 4c6825fc8b0f..05c49ba387b6 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3104,14 +3104,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb); static ktime_t fixup_debug_start(struct pci_dev *dev, void (*fn)(struct pci_dev *dev)) { - ktime_t calltime = 0; - - if (initcall_debug) { + if (initcall_debug) pci_info(dev, "calling %pF @ %i\n", fn, task_pid_nr(current)); - calltime = ktime_get(); - } - return calltime; + return ktime_get(); } static void fixup_debug_report(struct pci_dev *dev, ktime_t calltime, @@ -3120,12 +3116,11 @@ static void fixup_debug_report(struct pci_dev *dev, ktime_t calltime, ktime_t delta, rettime; unsigned long long duration; - if (initcall_debug) { - rettime = ktime_get(); - delta = ktime_sub(rettime, calltime); - duration = (unsigned long long) ktime_to_ns(delta) >> 10; + rettime = ktime_get(); + delta = ktime_sub(rettime, calltime); + duration = (unsigned long long) ktime_to_ns(delta) >> 10; + if (initcall_debug || duration > 10000) pci_info(dev, "%pF took %lld usecs\n", fn, duration); - } } /* -- cgit v1.2.3 From 33c4c8a588e6cccf3832b84b7792f02153e0ccda Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Mon, 12 Mar 2018 10:41:18 +0100 Subject: PCI: Add Altera vendor ID Add the Altera PCI Vendor id to pci_ids.h and remove the private definitions from xillybus_pcie.c and altera-cvp.c. Signed-off-by: Johannes Thumshirn Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Acked-by: Eli Billauer Acked-by: Bjorn Helgaas Cc: Anatolij Gustschin --- drivers/char/xillybus/xillybus_pcie.c | 1 - drivers/fpga/altera-cvp.c | 2 -- include/linux/pci_ids.h | 2 ++ 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c index dff2d1538164..05e5324f60bd 100644 --- a/drivers/char/xillybus/xillybus_pcie.c +++ b/drivers/char/xillybus/xillybus_pcie.c @@ -24,7 +24,6 @@ MODULE_LICENSE("GPL v2"); #define PCI_DEVICE_ID_XILLYBUS 0xebeb -#define PCI_VENDOR_ID_ALTERA 0x1172 #define PCI_VENDOR_ID_ACTEL 0x11aa #define PCI_VENDOR_ID_LATTICE 0x1204 diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c index 00e73d28077c..77b04e4b3254 100644 --- a/drivers/fpga/altera-cvp.c +++ b/drivers/fpga/altera-cvp.c @@ -384,8 +384,6 @@ static int altera_cvp_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id); static void altera_cvp_remove(struct pci_dev *pdev); -#define PCI_VENDOR_ID_ALTERA 0x1172 - static struct pci_device_id altera_cvp_id_tbl[] = { { PCI_VDEVICE(ALTERA, PCI_ANY_ID) }, { } diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index a6b30667a331..6a96a70fb462 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1561,6 +1561,8 @@ #define PCI_DEVICE_ID_SERVERWORKS_CSB6LPC 0x0227 #define PCI_DEVICE_ID_SERVERWORKS_HT1100LD 0x0408 +#define PCI_VENDOR_ID_ALTERA 0x1172 + #define PCI_VENDOR_ID_SBE 0x1176 #define PCI_DEVICE_ID_SBE_WANXL100 0x0301 #define PCI_DEVICE_ID_SBE_WANXL200 0x0302 -- cgit v1.2.3 From 66e3339df52a10da9e88c2072f26358150965088 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Mon, 12 Mar 2018 10:41:19 +0100 Subject: mcb: Add Altera PCI ID to mcb-pci MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some older PCI-attached MEN FPGAs use an Altera PCI Vendor ID instead of the MEN one. Add it to the PCI ID table so the driver automatically attaches to it. Reported-by: Ben Turner Tested-by: Ben Turner Signed-off-by: Johannes Thumshirn Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Cc: Andreas Geißler --- drivers/mcb/mcb-pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mcb/mcb-pci.c b/drivers/mcb/mcb-pci.c index af4d2f26f1c6..c2d69e33bf2b 100644 --- a/drivers/mcb/mcb-pci.c +++ b/drivers/mcb/mcb-pci.c @@ -117,6 +117,7 @@ static void mcb_pci_remove(struct pci_dev *pdev) static const struct pci_device_id mcb_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_MEN, PCI_DEVICE_ID_MEN_CHAMELEON) }, + { PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_MEN_CHAMELEON) }, { 0 }, }; MODULE_DEVICE_TABLE(pci, mcb_pci_tbl); -- cgit v1.2.3 From 3133e6dd07ed4b21a19ccdbbe4f033a2e4e9aad3 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 13:09:29 -0600 Subject: PCI: Tidy Makefiles Indent things so they line up neatly and remove extra blank lines and superfluous comments. No functional change intended. Signed-off-by: Bjorn Helgaas --- drivers/pci/Makefile | 69 +++++++++++++++++------------------------------ drivers/pci/pcie/Makefile | 16 ++++------- 2 files changed, 29 insertions(+), 56 deletions(-) diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 941970936840..952addc7bacf 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -1,61 +1,40 @@ # SPDX-License-Identifier: GPL-2.0 # # Makefile for the PCI bus specific drivers. -# -obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o remove.o pci.o \ - pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \ - irq.o vpd.o setup-bus.o vc.o mmap.o setup-irq.o +obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o \ + remove.o pci.o pci-driver.o search.o \ + pci-sysfs.o rom.o setup-res.o irq.o vpd.o \ + setup-bus.o vc.o mmap.o setup-irq.o ifdef CONFIG_PCI -obj-$(CONFIG_PROC_FS) += proc.o -obj-$(CONFIG_SYSFS) += slot.o -obj-$(CONFIG_OF) += of.o +obj-$(CONFIG_PROC_FS) += proc.o +obj-$(CONFIG_SYSFS) += slot.o +obj-$(CONFIG_OF) += of.o endif -obj-$(CONFIG_PCI_QUIRKS) += quirks.o - -# Build PCI Express stuff if needed -obj-$(CONFIG_PCIEPORTBUS) += pcie/ - -# Build the PCI Hotplug drivers if we were asked to -obj-$(CONFIG_HOTPLUG_PCI) += hotplug/ - -# Build the PCI MSI interrupt support -obj-$(CONFIG_PCI_MSI) += msi.o - -obj-$(CONFIG_PCI_ATS) += ats.o -obj-$(CONFIG_PCI_IOV) += iov.o - -# -# ACPI Related PCI FW Functions -# ACPI _DSM provided firmware instance and string name -# -obj-$(CONFIG_ACPI) += pci-acpi.o - -# SMBIOS provided firmware instance and labels -obj-$(CONFIG_PCI_LABEL) += pci-label.o - -# Intel MID platform PM support -obj-$(CONFIG_X86_INTEL_MID) += pci-mid.o - -obj-$(CONFIG_PCI_SYSCALL) += syscall.o - -obj-$(CONFIG_PCI_STUB) += pci-stub.o - -obj-$(CONFIG_PCI_ECAM) += ecam.o - +obj-$(CONFIG_PCI_QUIRKS) += quirks.o +obj-$(CONFIG_PCIEPORTBUS) += pcie/ +obj-$(CONFIG_HOTPLUG_PCI) += hotplug/ +obj-$(CONFIG_PCI_MSI) += msi.o +obj-$(CONFIG_PCI_ATS) += ats.o +obj-$(CONFIG_PCI_IOV) += iov.o +obj-$(CONFIG_ACPI) += pci-acpi.o +obj-$(CONFIG_PCI_LABEL) += pci-label.o +obj-$(CONFIG_X86_INTEL_MID) += pci-mid.o +obj-$(CONFIG_PCI_SYSCALL) += syscall.o +obj-$(CONFIG_PCI_STUB) += pci-stub.o +obj-$(CONFIG_PCI_ECAM) += ecam.o obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o -ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG - -# PCI host controller drivers -obj-y += host/ -obj-y += switch/ +obj-y += host/ +obj-y += switch/ +# Endpoint library must be initialized before its users obj-$(CONFIG_PCI_ENDPOINT) += endpoint/ -# Endpoint library must be initialized before its users obj-$(CONFIG_PCIE_CADENCE) += cadence/ # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW obj-y += dwc/ + +ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile index 223e4c34c29a..7d1648bd0766 100644 --- a/drivers/pci/pcie/Makefile +++ b/drivers/pci/pcie/Makefile @@ -1,20 +1,14 @@ # SPDX-License-Identifier: GPL-2.0 # -# Makefile for PCI-Express PORT Driver -# - -# Build PCI Express ASPM if needed -obj-$(CONFIG_PCIEASPM) += aspm.o +# Makefile for PCI Express features and port driver pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o pcieportdrv-$(CONFIG_ACPI) += portdrv_acpi.o obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o -# Build PCI Express AER if needed +obj-$(CONFIG_PCIEASPM) += aspm.o obj-$(CONFIG_PCIEAER) += aer/ - -obj-$(CONFIG_PCIE_PME) += pme.o - -obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o -obj-$(CONFIG_PCIE_PTM) += ptm.o +obj-$(CONFIG_PCIE_PME) += pme.o +obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o +obj-$(CONFIG_PCIE_PTM) += ptm.o -- cgit v1.2.3 From df62ab5e0f75608919df7442654b0fab78246b7b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 16:36:33 -0600 Subject: PCI: Tidy comments Remove pointless comments that tell us the file name, remove blank line comments, follow multi-line comment conventions. No functional change intended. Signed-off-by: Bjorn Helgaas --- drivers/pci/access.c | 12 +++++++----- drivers/pci/ats.c | 10 ++++------ drivers/pci/bus.c | 2 -- drivers/pci/host-bridge.c | 2 +- drivers/pci/iov.c | 8 +++----- drivers/pci/mmap.c | 2 +- drivers/pci/msi.c | 3 +-- drivers/pci/pci-acpi.c | 3 +-- drivers/pci/pci-driver.c | 2 -- drivers/pci/pci-label.c | 5 +++-- drivers/pci/pci-stub.c | 3 ++- drivers/pci/pci-sysfs.c | 3 --- drivers/pci/pci.c | 9 +++++---- drivers/pci/pcie/aer/aerdrv.c | 9 +++------ drivers/pci/pcie/aer/aerdrv.h | 1 - drivers/pci/pcie/aer/aerdrv_acpi.c | 1 - drivers/pci/pcie/aer/aerdrv_core.c | 11 ++++------- drivers/pci/pcie/aer/aerdrv_errprint.c | 3 --- drivers/pci/pcie/aer/ecrc.c | 4 ++-- drivers/pci/pcie/aspm.c | 3 +-- drivers/pci/pcie/portdrv.h | 1 - drivers/pci/pcie/portdrv_core.c | 1 - drivers/pci/pcie/portdrv_pci.c | 2 -- drivers/pci/probe.c | 2 +- drivers/pci/proc.c | 4 ++-- drivers/pci/quirks.c | 14 +++++++------- drivers/pci/rom.c | 4 +--- drivers/pci/search.c | 8 ++++---- drivers/pci/setup-bus.c | 6 +----- drivers/pci/setup-irq.c | 4 +--- drivers/pci/setup-res.c | 10 +++------- drivers/pci/slot.c | 1 - drivers/pci/syscall.c | 9 +++------ drivers/pci/vpd.c | 3 +-- drivers/pci/xen-pcifront.c | 4 ++-- 35 files changed, 64 insertions(+), 105 deletions(-) diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 5e9a9822d9d4..dcaacb4bb880 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -17,9 +17,9 @@ DEFINE_RAW_SPINLOCK(pci_lock); /* - * Wrappers for all PCI configuration access functions. They just check - * alignment, do locking and call the low-level functions pointed to - * by pci_dev->ops. + * Wrappers for all PCI configuration access functions. They just check + * alignment, do locking and call the low-level functions pointed to + * by pci_dev->ops. */ #define PCI_byte_BAD 0 @@ -686,8 +686,10 @@ void pci_cfg_access_unlock(struct pci_dev *dev) raw_spin_lock_irqsave(&pci_lock, flags); - /* This indicates a problem in the caller, but we don't need - * to kill them, unlike a double-block above. */ + /* + * This indicates a problem in the caller, but we don't need + * to kill them, unlike a double-block above. + */ WARN_ON(!dev->block_cfg_access); dev->block_cfg_access = 0; diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 6ad80a1fd5a7..89305b569d3d 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -1,14 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/ats.c - * - * Copyright (C) 2009 Intel Corporation, Yu Zhao - * Copyright (C) 2011 Advanced Micro Devices, - * - * PCI Express I/O Virtualization (IOV) support. + * PCI Express I/O Virtualization (IOV) support * Address Translation Service 1.0 * Page Request Interface added by Joerg Roedel * PASID support added by Joerg Roedel + * + * Copyright (C) 2009 Intel Corporation, Yu Zhao + * Copyright (C) 2011 Advanced Micro Devices, */ #include diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 737d1c52f002..bc2ded4c451f 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/bus.c - * * From setup-res.c, by: * Dave Rusling (david.rusling@reo.mts.dec.com) * David Mosberger (davidm@cs.arizona.edu) diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index ac8d81268296..e01d53f5b32f 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * host bridge related code + * Host bridge related code */ #include diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 677924ae0350..538de9057c23 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -1,12 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/iov.c - * - * Copyright (C) 2009 Intel Corporation, Yu Zhao - * - * PCI Express I/O Virtualization (IOV) support. + * PCI Express I/O Virtualization (IOV) support * Single Root IOV 1.0 * Address Translation Service 1.0 + * + * Copyright (C) 2009 Intel Corporation, Yu Zhao */ #include diff --git a/drivers/pci/mmap.c b/drivers/pci/mmap.c index 814a3ce341fc..24505b08de40 100644 --- a/drivers/pci/mmap.c +++ b/drivers/pci/mmap.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * mmap.c — generic PCI resource mmap helper + * Generic PCI resource mmap helper * * Copyright © 2017 Amazon.com, Inc. or its affiliates. * diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 8b0729c94bb7..30250631efe7 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * File: msi.c - * Purpose: PCI Message Signaled Interrupt (MSI) + * PCI Message Signaled Interrupt (MSI) * * Copyright (C) 2003-2004 Intel * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 78157688dcc9..1abdbf267c19 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * File: pci-acpi.c - * Purpose: Provide PCI support in ACPI + * PCI support in ACPI * * Copyright (C) 2005 David Shaohua Li * Copyright (C) 2004 Tom Long Nguyen diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 3bed6beda051..5f1215222113 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/pci-driver.c - * * (C) Copyright 2002-2004, 2007 Greg Kroah-Hartman * (C) Copyright 2007 Novell Inc. */ diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c index a961a71d950f..a5910f942857 100644 --- a/drivers/pci/pci-label.c +++ b/drivers/pci/pci-label.c @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Purpose: Export the firmware instance and label associated with - * a pci device to sysfs + * Export the firmware instance and label associated with a PCI device to + * sysfs + * * Copyright (C) 2010 Dell Inc. * by Narendra K , * Jordan Hargrave diff --git a/drivers/pci/pci-stub.c b/drivers/pci/pci-stub.c index 10d54f939048..66f8a59fadbd 100644 --- a/drivers/pci/pci-stub.c +++ b/drivers/pci/pci-stub.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -/* pci-stub - simple stub driver to reserve a pci device +/* + * Simple stub driver to reserve a PCI device * * Copyright (C) 2008 Red Hat, Inc. * Author: diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index eb6bee8724cc..db6b9bb86452 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/pci-sysfs.c - * * (C) Copyright 2002-2004 Greg Kroah-Hartman * (C) Copyright 2002-2004 IBM Corp. * (C) Copyright 2003 Matthew Wilcox @@ -12,7 +10,6 @@ * File attributes for PCI devices * * Modeled after usb's driverfs.c - * */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b7ff5786b76b..22f06de93696 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * PCI Bus Services, see include/linux/pci.h for further explanation. + * PCI Bus Services, see include/linux/pci.h for further explanation. * - * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter, - * David Mosberger-Tang + * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter, + * David Mosberger-Tang * - * Copyright 1997 -- 2000 Martin Mares + * Copyright 1997 -- 2000 Martin Mares */ #include @@ -4183,6 +4183,7 @@ void pci_reset_secondary_bus(struct pci_dev *dev) pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &ctrl); ctrl |= PCI_BRIDGE_CTL_BUS_RESET; pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl); + /* * PCI spec v3.0 7.6.4.2 requires minimum Trst of 1ms. Double * this to 2ms to ensure that we meet the minimum requirement. diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index da8331f5684d..9066d6e976d4 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -1,15 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/pcie/aer/aerdrv.c - * - * This file implements the AER root port service driver. The driver will - * register an irq handler. When root port triggers an AER interrupt, the irq - * handler will collect root port status and schedule a work. + * Implement the AER root port service driver. The driver registers an IRQ + * handler. When a root port triggers an AER interrupt, the IRQ handler + * collects root port status and schedules work. * * Copyright (C) 2006 Intel Corp. * Tom Long Nguyen (tom.l.nguyen@intel.com) * Zhang Yanmin (yanmin.zhang@intel.com) - * */ #include diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 5449e5ce139d..72835141d640 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -3,7 +3,6 @@ * Copyright (C) 2006 Intel Corp. * Tom Long Nguyen (tom.l.nguyen@intel.com) * Zhang Yanmin (yanmin.zhang@intel.com) - * */ #ifndef _AERDRV_H_ diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index b2019440e882..08c87de13cb8 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -5,7 +5,6 @@ * Copyright (C) 2006 Intel Corp. * Tom Long Nguyen (tom.l.nguyen@intel.com) * Zhang Yanmin (yanmin.zhang@intel.com) - * */ #include diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index a4bfea52e7d4..0ea5acc40323 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -1,16 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/pcie/aer/aerdrv_core.c - * - * This file implements the core part of PCIe AER. When a PCIe - * error is delivered, an error message will be collected and printed to - * console, then, an error recovery procedure will be executed by following - * the PCI error recovery rules. + * Implement the core part of PCIe AER. When a PCIe error is delivered, an + * error message will be collected and printed to console, then an error + * recovery procedure will be executed by following the PCI error recovery + * rules. * * Copyright (C) 2006 Intel Corp. * Tom Long Nguyen (tom.l.nguyen@intel.com) * Zhang Yanmin (yanmin.zhang@intel.com) - * */ #include diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 6a352e638699..cfc89dd57831 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -1,13 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/pcie/aer/aerdrv_errprint.c - * * Format error messages and print them to console. * * Copyright (C) 2006 Intel Corp. * Tom Long Nguyen (tom.l.nguyen@intel.com) * Zhang Yanmin (yanmin.zhang@intel.com) - * */ #include diff --git a/drivers/pci/pcie/aer/ecrc.c b/drivers/pci/pcie/aer/ecrc.c index 26d3cac9e635..36e671835c41 100644 --- a/drivers/pci/pcie/aer/ecrc.c +++ b/drivers/pci/pcie/aer/ecrc.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Enables/disables PCIe ECRC checking. + * Enable/disable PCIe ECRC checking * - * (C) Copyright 2009 Hewlett-Packard Development Company, L.P. + * (C) Copyright 2009 Hewlett-Packard Development Company, L.P. * Andrew Patterson */ diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 57feef2ecfe7..bb14a104ebbb 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * File: drivers/pci/pcie/aspm.c - * Enabling PCIe link L0s/L1 state and Clock Power Management + * Enable PCIe link L0s/L1 state and Clock Power Management * * Copyright (C) 2007 Intel * Copyright (C) Zhang Yanmin (yanmin.zhang@intel.com) diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index a854bc569117..9681aba0d428 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * File: portdrv.h * Purpose: PCI Express Port Bus Driver's Internal Data Structures * * Copyright (C) 2004 Intel diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index ef3bad4ad010..07833e5cde36 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 /* - * File: portdrv_core.c * Purpose: PCI Express Port Bus Driver's Core Functions * * Copyright (C) 2004 Intel diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index fb1c1bb87316..06d9445997ff 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -1,9 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * File: portdrv_pci.c * Purpose: PCI Express Port Bus Driver * Author: Tom Nguyen - * Version: v1.0 * * Copyright (C) 2004 Intel * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ef5377438a1e..7762ba4d9220 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * probe.c - PCI detection and setup code + * PCI detection and setup code */ #include diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index 58a662e3c4a6..1ee8927a0635 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Procfs interface for the PCI bus. + * Procfs interface for the PCI bus * - * Copyright (c) 1997--1999 Martin Mares + * Copyright (c) 1997--1999 Martin Mares */ #include diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 05c49ba387b6..8bf0ad91432a 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1,15 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 /* - * This file contains work-arounds for many known PCI hardware - * bugs. Devices present only on certain architectures (host - * bridges et cetera) should be handled in arch-specific code. + * This file contains work-arounds for many known PCI hardware bugs. + * Devices present only on certain architectures (host bridges et cetera) + * should be handled in arch-specific code. * - * Note: any quirks for hotpluggable devices must _NOT_ be declared __init. + * Note: any quirks for hotpluggable devices must _NOT_ be declared __init. * - * Copyright (c) 1999 Martin Mares + * Copyright (c) 1999 Martin Mares * - * Init/reset quirks for USB host controllers should be in the - * USB quirks file, where their drivers can access reuse it. + * Init/reset quirks for USB host controllers should be in the USB quirks + * file, where their drivers can use them. */ #include diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index 374a33443be9..a7b5c37a85ec 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -1,11 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/rom.c + * PCI ROM access routines * * (C) Copyright 2004 Jon Smirl * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes - * - * PCI ROM access routines */ #include #include diff --git a/drivers/pci/search.c b/drivers/pci/search.c index bc1e023f1353..2b5f720862d3 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * PCI searching functions. + * PCI searching functions * - * Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter, + * Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter, * David Mosberger-Tang - * Copyright (C) 1997 -- 2000 Martin Mares - * Copyright (C) 2003 -- 2004 Greg Kroah-Hartman + * Copyright (C) 1997 -- 2000 Martin Mares + * Copyright (C) 2003 -- 2004 Greg Kroah-Hartman */ #include diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 3cce29a069e6..072784f55ea5 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1,16 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/setup-bus.c + * Support routines for initializing a PCI subsystem * * Extruded from code written by * Dave Rusling (david.rusling@reo.mts.dec.com) * David Mosberger (davidm@cs.arizona.edu) * David Miller (davem@redhat.com) * - * Support routines for initializing a PCI subsystem. - */ - -/* * Nov 2000, Ivan Kokshaysky * PCI-PCI bridges cleanup, sorted resource allocation. * Feb 2002, Ivan Kokshaysky diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c index 5ad4ee7d7b1e..7129494754dd 100644 --- a/drivers/pci/setup-irq.c +++ b/drivers/pci/setup-irq.c @@ -1,13 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/setup-irq.c + * Support routines for initializing a PCI subsystem * * Extruded from code written by * Dave Rusling (david.rusling@reo.mts.dec.com) * David Mosberger (davidm@cs.arizona.edu) * David Miller (davem@redhat.com) - * - * Support routines for initializing a PCI subsystem. */ diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 369d48d6c6f1..1ef01d79b52e 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -1,18 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/setup-res.c + * Support routines for initializing a PCI subsystem * * Extruded from code written by * Dave Rusling (david.rusling@reo.mts.dec.com) * David Mosberger (davidm@cs.arizona.edu) * David Miller (davem@redhat.com) * - * Support routines for initializing a PCI subsystem. - */ - -/* fixed for multiple pci buses, 1999 Andrea Arcangeli */ - -/* + * Fixed for multiple PCI buses, 1999 Andrea Arcangeli + * * Nov 2000, Ivan Kokshaysky * Resource sorting */ diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index d10f556dc03e..bdea6d4061ae 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/slot.c * Copyright (C) 2006 Matthew Wilcox * Copyright (C) 2006-2009 Hewlett-Packard Development Company, L.P. * Alex Chiang diff --git a/drivers/pci/syscall.c b/drivers/pci/syscall.c index e725f99b5479..d96626c614f5 100644 --- a/drivers/pci/syscall.c +++ b/drivers/pci/syscall.c @@ -1,11 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* - * pci_syscall.c - * - * For architectures where we want to allow direct access - * to the PCI config stuff - it would probably be preferable - * on PCs too, but there people just do it by hand with the - * magic northbridge registers.. + * For architectures where we want to allow direct access to the PCI config + * stuff - it would probably be preferable on PCs too, but there people + * just do it by hand with the magic northbridge registers. */ #include diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 70fba57d6103..4b22885f4e9c 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * File: vpd.c - * Purpose: Provide PCI VPD support + * PCI VPD support * * Copyright (C) 2010 Broadcom Corporation. */ diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index 8785014f656e..eba6e33147a2 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Xen PCI Frontend. + * Xen PCI Frontend * - * Author: Ryan Wilson + * Author: Ryan Wilson */ #include #include -- cgit v1.2.3 From bf4447fd1cb6158b60bd60a79998e1d029d31e68 Mon Sep 17 00:00:00 2001 From: KarimAllah Ahmed Date: Sat, 3 Mar 2018 05:33:10 +0100 Subject: PCI/IOV: Skip BAR sizing for VFs Per PCIe r4.0, sec 9.3.4.1.11, the BAR registers in VF config space are all RO Zero, so skip sizing them. This is an optimization when enabling SR-IOV on a device with many VFs. Suggested-by: Bjorn Helgaas Signed-off-by: KarimAllah Ahmed Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index a1cddca37793..9f80b904bf76 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -329,6 +329,10 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) if (dev->non_compliant_bars) return; + /* Per PCIe r4.0, sec 9.3.4.1.11, the VF BARs are all RO Zero */ + if (dev->is_virtfn) + return; + for (pos = 0; pos < howmany; pos++) { struct resource *res = &dev->resource[pos]; reg = PCI_BASE_ADDRESS_0 + (pos << 2); -- cgit v1.2.3 From e4aa4ae77aaa80e69b9d3335541be4ba9bd02766 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 7 Mar 2018 09:42:35 -0600 Subject: PCI: kirin: Remove unnecessary asm/compiler.h include compiler.h is unnecessary and doesn't exist on some arches, so remove it. Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi --- drivers/pci/dwc/pcie-kirin.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/dwc/pcie-kirin.c b/drivers/pci/dwc/pcie-kirin.c index 13d839bd6160..dcc8cedf6e17 100644 --- a/drivers/pci/dwc/pcie-kirin.c +++ b/drivers/pci/dwc/pcie-kirin.c @@ -8,7 +8,6 @@ * Author: Xiaowei Song */ -#include #include #include #include -- cgit v1.2.3 From 3758a191116f110a503a0c6cb7ce89bc81841baa Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 15 Feb 2018 15:07:39 -0600 Subject: sparc/PCI: Stop reserving System ROM and Video ROM in PCI space Previously, pci_register_legacy_regions() reserved PCI address space under every PCI host bridge for the System ROM and the Video ROM, but these regions are not part of PCI address space. Previously, pci_register_legacy_regions() reserved the following areas of PCI address space under every PCI host bridge: [bus 0xa0000-0xbffff] Video RAM area (VGA frame buffer) [bus 0xc0000-0xc7fff] Video ROM [bus 0xf0000-0xfffff] System ROM It does need to reserve the [bus 0xa0000-0xbffff] region (at least if there's a possibility of a VGA device below the bridge) because VGA devices can respond to that even if they don't describe it with a BAR. But the Video ROM and System ROM areas don't seem necessary because they are not areas that legacy PCI devices respond to. They appear to be copied from x86, where they describe areas of system memory that depend on BIOS conventions. On x86, BIOS copies the option ROM of the primary VGA device to RAM at 0xc0000, and the 0xf0000-0xfffff region is reserved for the motherboard BIOS. Neither of these things applies to sparc. Stop reserving the System ROM and Video ROM regions in PCI space. Signed-off-by: Bjorn Helgaas Acked-by: David S. Miller --- arch/sparc/kernel/pci_common.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/arch/sparc/kernel/pci_common.c b/arch/sparc/kernel/pci_common.c index 14fa74f90fdb..38d46bcc8634 100644 --- a/arch/sparc/kernel/pci_common.c +++ b/arch/sparc/kernel/pci_common.c @@ -344,26 +344,6 @@ static void pci_register_legacy_regions(struct resource *io_res, p->end = p->start + 0x1ffffUL; p->flags = IORESOURCE_BUSY; request_resource(mem_res, p); - - p = kzalloc(sizeof(*p), GFP_KERNEL); - if (!p) - return; - - p->name = "System ROM"; - p->start = mem_res->start + 0xf0000UL; - p->end = p->start + 0xffffUL; - p->flags = IORESOURCE_BUSY; - request_resource(mem_res, p); - - p = kzalloc(sizeof(*p), GFP_KERNEL); - if (!p) - return; - - p->name = "Video ROM"; - p->start = mem_res->start + 0xc0000UL; - p->end = p->start + 0x7fffUL; - p->flags = IORESOURCE_BUSY; - request_resource(mem_res, p); } static void pci_register_iommu_region(struct pci_pbm_info *pbm) -- cgit v1.2.3 From a1b363a53f0c231627d2c06a2691c17f610eab98 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 7 Mar 2018 09:42:36 -0600 Subject: PCI: iproc: Remove dependency on ARM specific struct pci_sys_data The iproc driver is using ARM's struct pci_sys_data simply to store a private data pointer. This is completely unnecessary, so store the private data directly in bus->sysdata as is done on arm64. Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Scott Branden --- drivers/pci/host/pcie-iproc-bcma.c | 3 +-- drivers/pci/host/pcie-iproc.c | 19 ++----------------- drivers/pci/host/pcie-iproc.h | 4 ---- 3 files changed, 3 insertions(+), 23 deletions(-) diff --git a/drivers/pci/host/pcie-iproc-bcma.c b/drivers/pci/host/pcie-iproc-bcma.c index 603c83429cb3..aa55b064f64d 100644 --- a/drivers/pci/host/pcie-iproc-bcma.c +++ b/drivers/pci/host/pcie-iproc-bcma.c @@ -25,8 +25,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8012, bcma_pcie2_fixup_class); static int iproc_pcie_bcma_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { - struct pci_sys_data *sys = dev->sysdata; - struct iproc_pcie *pcie = sys->private_data; + struct iproc_pcie *pcie = dev->sysdata; struct bcma_device *bdev = container_of(pcie->dev, struct bcma_device, dev); return bcma_core_irq(bdev, 5); diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c index cbb095481cdc..3c76c5fa4f32 100644 --- a/drivers/pci/host/pcie-iproc.c +++ b/drivers/pci/host/pcie-iproc.c @@ -377,14 +377,7 @@ static const u16 iproc_pcie_reg_paxc_v2[] = { static inline struct iproc_pcie *iproc_data(struct pci_bus *bus) { - struct iproc_pcie *pcie; -#ifdef CONFIG_ARM - struct pci_sys_data *sys = bus->sysdata; - - pcie = sys->private_data; -#else - pcie = bus->sysdata; -#endif + struct iproc_pcie *pcie = bus->sysdata; return pcie; } @@ -1331,7 +1324,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) { struct device *dev; int ret; - void *sysdata; struct pci_bus *child; struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); @@ -1376,13 +1368,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) goto err_power_off_phy; } -#ifdef CONFIG_ARM - pcie->sysdata.private_data = pcie; - sysdata = &pcie->sysdata; -#else - sysdata = pcie; -#endif - ret = iproc_pcie_check_link(pcie); if (ret) { dev_err(dev, "no PCIe EP device detected\n"); @@ -1399,7 +1384,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) host->busnr = 0; host->dev.parent = dev; host->ops = &iproc_pcie_ops; - host->sysdata = sysdata; + host->sysdata = pcie; host->map_irq = pcie->map_irq; host->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h index d55f56a186cd..814b600b383a 100644 --- a/drivers/pci/host/pcie-iproc.h +++ b/drivers/pci/host/pcie-iproc.h @@ -54,7 +54,6 @@ struct iproc_msi; * @reg_offsets: register offsets * @base: PCIe host controller I/O register base * @base_addr: PCIe host controller register base physical address - * @sysdata: Per PCI controller data (ARM-specific) * @root_bus: pointer to root bus * @phy: optional PHY device that controls the Serdes * @map_irq: function callback to map interrupts @@ -80,9 +79,6 @@ struct iproc_pcie { u16 *reg_offsets; void __iomem *base; phys_addr_t base_addr; -#ifdef CONFIG_ARM - struct pci_sys_data sysdata; -#endif struct resource mem; struct pci_bus *root_bus; struct phy *phy; -- cgit v1.2.3 From d2fd7344a9879c53afe63fabe068fa8349384750 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 7 Mar 2018 09:42:37 -0600 Subject: PCI: kirin: Fix missing dependency on PCI_MSI_IRQ_DOMAIN PCIE_DW_HOST depends on PCI_MSI_IRQ_DOMAIN and since kirin selects PCIE_DW_HOST, it must also depend on PCI_MSI_IRQ_DOMAIN. This was found by 0-day once building on all arches was enabled. Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi --- drivers/pci/dwc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/dwc/Kconfig b/drivers/pci/dwc/Kconfig index 0f666b1ce289..2f3f5c50aa48 100644 --- a/drivers/pci/dwc/Kconfig +++ b/drivers/pci/dwc/Kconfig @@ -176,6 +176,7 @@ config PCIE_ARTPEC6_EP config PCIE_KIRIN depends on OF && ARM64 bool "HiSilicon Kirin series SoCs PCIe controllers" + depends on PCI_MSI_IRQ_DOMAIN depends on PCI select PCIEPORTBUS select PCIE_DW_HOST -- cgit v1.2.3 From 1acfb9b7ee0b1881bb8e875b6757976e48293ec4 Mon Sep 17 00:00:00 2001 From: Jay Fang Date: Mon, 12 Mar 2018 17:13:32 +0800 Subject: PCI: Add decoding for 16 GT/s link speed PCIe 4.0 defines the 16.0 GT/s link speed. Links can run at that speed without any Linux changes, but previously their sysfs "max_link_speed" and "current_link_speed" files contained "Unknown speed", not the expected "16.0 GT/s". Add decoding for the new 16 GT/s link speed. Signed-off-by: Jay Fang [bhelgaas: add PCI_EXP_LNKCAP2_SLS_16_0GB] Signed-off-by: Bjorn Helgaas Reviewed-by: Dongdong Liu --- drivers/pci/pci-sysfs.c | 6 ++++++ drivers/pci/probe.c | 2 +- drivers/pci/slot.c | 1 + include/linux/pci.h | 1 + include/uapi/linux/pci_regs.h | 7 +++++-- 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index eb6bee8724cc..7dc5be545d18 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -168,6 +168,9 @@ static ssize_t max_link_speed_show(struct device *dev, return -EINVAL; switch (linkcap & PCI_EXP_LNKCAP_SLS) { + case PCI_EXP_LNKCAP_SLS_16_0GB: + speed = "16 GT/s"; + break; case PCI_EXP_LNKCAP_SLS_8_0GB: speed = "8 GT/s"; break; @@ -213,6 +216,9 @@ static ssize_t current_link_speed_show(struct device *dev, return -EINVAL; switch (linkstat & PCI_EXP_LNKSTA_CLS) { + case PCI_EXP_LNKSTA_CLS_16_0GB: + speed = "16 GT/s"; + break; case PCI_EXP_LNKSTA_CLS_8_0GB: speed = "8 GT/s"; break; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ef5377438a1e..86bf045f3d59 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -592,7 +592,7 @@ const unsigned char pcie_link_speed[] = { PCIE_SPEED_2_5GT, /* 1 */ PCIE_SPEED_5_0GT, /* 2 */ PCIE_SPEED_8_0GT, /* 3 */ - PCI_SPEED_UNKNOWN, /* 4 */ + PCIE_SPEED_16_0GT, /* 4 */ PCI_SPEED_UNKNOWN, /* 5 */ PCI_SPEED_UNKNOWN, /* 6 */ PCI_SPEED_UNKNOWN, /* 7 */ diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index d10f556dc03e..191893e19d5c 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -76,6 +76,7 @@ static const char *pci_bus_speed_strings[] = { "2.5 GT/s PCIe", /* 0x14 */ "5.0 GT/s PCIe", /* 0x15 */ "8.0 GT/s PCIe", /* 0x16 */ + "16.0 GT/s PCIe", /* 0x17 */ }; static ssize_t bus_speed_read(enum pci_bus_speed speed, char *buf) diff --git a/include/linux/pci.h b/include/linux/pci.h index 024a1beda008..8043a5937ad0 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -256,6 +256,7 @@ enum pci_bus_speed { PCIE_SPEED_2_5GT = 0x14, PCIE_SPEED_5_0GT = 0x15, PCIE_SPEED_8_0GT = 0x16, + PCIE_SPEED_16_0GT = 0x17, PCI_SPEED_UNKNOWN = 0xff, }; diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 0c79eac5e9b8..103ba797a8f3 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -520,6 +520,7 @@ #define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */ #define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */ #define PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */ +#define PCI_EXP_LNKCAP_SLS_16_0GB 0x00000004 /* LNKCAP2 SLS Vector bit 3 */ #define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ #define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ #define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ @@ -547,6 +548,7 @@ #define PCI_EXP_LNKSTA_CLS_2_5GB 0x0001 /* Current Link Speed 2.5GT/s */ #define PCI_EXP_LNKSTA_CLS_5_0GB 0x0002 /* Current Link Speed 5.0GT/s */ #define PCI_EXP_LNKSTA_CLS_8_0GB 0x0003 /* Current Link Speed 8.0GT/s */ +#define PCI_EXP_LNKSTA_CLS_16_0GB 0x0004 /* Current Link Speed 16.0GT/s */ #define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */ #define PCI_EXP_LNKSTA_NLW_X1 0x0010 /* Current Link Width x1 */ #define PCI_EXP_LNKSTA_NLW_X2 0x0020 /* Current Link Width x2 */ @@ -648,8 +650,9 @@ #define PCI_CAP_EXP_RC_ENDPOINT_SIZEOF_V2 44 /* v2 endpoints without link end here */ #define PCI_EXP_LNKCAP2 44 /* Link Capabilities 2 */ #define PCI_EXP_LNKCAP2_SLS_2_5GB 0x00000002 /* Supported Speed 2.5GT/s */ -#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5.0GT/s */ -#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8.0GT/s */ +#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5GT/s */ +#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8GT/s */ +#define PCI_EXP_LNKCAP2_SLS_16_0GB 0x00000010 /* Supported Speed 16GT/s */ #define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */ #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ -- cgit v1.2.3 From 031e3601869c815582ca1d49d1ff73de58e446b0 Mon Sep 17 00:00:00 2001 From: Zhichang Yuan Date: Thu, 15 Mar 2018 02:15:50 +0800 Subject: lib: Add generic PIO mapping method 41f8bba7f555 ("of/pci: Add pci_register_io_range() and pci_pio_to_address()") added support for PCI I/O space mapped into CPU physical memory space. With that support, the I/O ranges configured for PCI/PCIe hosts on some architectures can be mapped to logical PIO and converted easily between CPU address and the corresponding logical PIO. Based on this, PCI I/O port space can be accessed via in/out accessors that use memory read/write. But on some platforms, there are bus hosts that access I/O port space with host-local I/O port addresses rather than memory addresses. Add a more generic I/O mapping method to support those devices. With this patch, both the CPU addresses and the host-local port can be mapped into the logical PIO space with different logical/fake PIOs. After this, all the I/O accesses to either PCI MMIO devices or host-local I/O peripherals can be unified into the existing I/O accessors defined in asm-generic/io.h and be redirected to the right device-specific hooks based on the input logical PIO. Tested-by: dann frazier Signed-off-by: Zhichang Yuan Signed-off-by: Gabriele Paoloni Signed-off-by: John Garry [bhelgaas: remove -EFAULT return from logic_pio_register_range() per https://lkml.kernel.org/r/20180403143909.GA21171@ulmo, fix NULL pointer checking per https://lkml.kernel.org/r/20180403211505.GA29612@embeddedor.com] Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko --- include/asm-generic/io.h | 2 + include/linux/logic_pio.h | 123 ++++++++++++++++++++ lib/Kconfig | 16 +++ lib/Makefile | 2 + lib/logic_pio.c | 280 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 423 insertions(+) create mode 100644 include/linux/logic_pio.h create mode 100644 lib/logic_pio.c diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h index b4531e3b2120..b7996a79d64b 100644 --- a/include/asm-generic/io.h +++ b/include/asm-generic/io.h @@ -351,6 +351,8 @@ static inline void writesq(volatile void __iomem *addr, const void *buffer, #define IO_SPACE_LIMIT 0xffff #endif +#include + /* * {in,out}{b,w,l}() access little endian I/O. {in,out}{b,w,l}_p() can be * implemented on hardware that needs an additional delay for I/O accesses to diff --git a/include/linux/logic_pio.h b/include/linux/logic_pio.h new file mode 100644 index 000000000000..cbd9d8495690 --- /dev/null +++ b/include/linux/logic_pio.h @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved. + * Author: Gabriele Paoloni + * Author: Zhichang Yuan + */ + +#ifndef __LINUX_LOGIC_PIO_H +#define __LINUX_LOGIC_PIO_H + +#include + +enum { + LOGIC_PIO_INDIRECT, /* Indirect IO flag */ + LOGIC_PIO_CPU_MMIO, /* Memory-mapped IO flag */ +}; + +struct logic_pio_hwaddr { + struct list_head list; + struct fwnode_handle *fwnode; + resource_size_t hw_start; + resource_size_t io_start; + resource_size_t size; /* range size populated */ + unsigned long flags; + + void *hostdata; + const struct logic_pio_host_ops *ops; +}; + +struct logic_pio_host_ops { + u32 (*in)(void *hostdata, unsigned long addr, size_t dwidth); + void (*out)(void *hostdata, unsigned long addr, u32 val, + size_t dwidth); + u32 (*ins)(void *hostdata, unsigned long addr, void *buffer, + size_t dwidth, unsigned int count); + void (*outs)(void *hostdata, unsigned long addr, const void *buffer, + size_t dwidth, unsigned int count); +}; + +#ifdef CONFIG_INDIRECT_PIO +u8 logic_inb(unsigned long addr); +void logic_outb(u8 value, unsigned long addr); +void logic_outw(u16 value, unsigned long addr); +void logic_outl(u32 value, unsigned long addr); +u16 logic_inw(unsigned long addr); +u32 logic_inl(unsigned long addr); +void logic_outb(u8 value, unsigned long addr); +void logic_outw(u16 value, unsigned long addr); +void logic_outl(u32 value, unsigned long addr); +void logic_insb(unsigned long addr, void *buffer, unsigned int count); +void logic_insl(unsigned long addr, void *buffer, unsigned int count); +void logic_insw(unsigned long addr, void *buffer, unsigned int count); +void logic_outsb(unsigned long addr, const void *buffer, unsigned int count); +void logic_outsw(unsigned long addr, const void *buffer, unsigned int count); +void logic_outsl(unsigned long addr, const void *buffer, unsigned int count); + +#ifndef inb +#define inb logic_inb +#endif + +#ifndef inw +#define inw logic_inw +#endif + +#ifndef inl +#define inl logic_inl +#endif + +#ifndef outb +#define outb logic_outb +#endif + +#ifndef outw +#define outw logic_outw +#endif + +#ifndef outl +#define outl logic_outl +#endif + +#ifndef insb +#define insb logic_insb +#endif + +#ifndef insw +#define insw logic_insw +#endif + +#ifndef insl +#define insl logic_insl +#endif + +#ifndef outsb +#define outsb logic_outsb +#endif + +#ifndef outsw +#define outsw logic_outsw +#endif + +#ifndef outsl +#define outsl logic_outsl +#endif + +/* + * We reserve 0x4000 bytes for Indirect IO as so far this library is only + * used by the HiSilicon LPC Host. If needed, we can reserve a wider IO + * area by redefining the macro below. + */ +#define PIO_INDIRECT_SIZE 0x4000 +#define MMIO_UPPER_LIMIT (IO_SPACE_LIMIT - PIO_INDIRECT_SIZE) +#else +#define MMIO_UPPER_LIMIT IO_SPACE_LIMIT +#endif /* CONFIG_INDIRECT_PIO */ + +struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode); +unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode, + resource_size_t hw_addr, resource_size_t size); +int logic_pio_register_range(struct logic_pio_hwaddr *newrange); +resource_size_t logic_pio_to_hwaddr(unsigned long pio); +unsigned long logic_pio_trans_cpuaddr(resource_size_t hw_addr); + +#endif /* __LINUX_LOGIC_PIO_H */ diff --git a/lib/Kconfig b/lib/Kconfig index e96089499371..5fe577673b98 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -55,6 +55,22 @@ config ARCH_USE_CMPXCHG_LOCKREF config ARCH_HAS_FAST_MULTIPLIER bool +config INDIRECT_PIO + bool "Access I/O in non-MMIO mode" + depends on ARM64 + help + On some platforms where no separate I/O space exists, there are I/O + hosts which can not be accessed in MMIO mode. Using the logical PIO + mechanism, the host-local I/O resource can be mapped into system + logic PIO space shared with MMIO hosts, such as PCI/PCIe, then the + system can access the I/O devices with the mapped-logic PIO through + I/O accessors. + + This way has relatively little I/O performance cost. Please make + sure your devices really need this configure item enabled. + + When in doubt, say N. + config CRC_CCITT tristate "CRC-CCITT functions" help diff --git a/lib/Makefile b/lib/Makefile index a90d4fcd748f..4a9eacda3c8b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -81,6 +81,8 @@ obj-$(CONFIG_HAS_IOMEM) += iomap_copy.o devres.o obj-$(CONFIG_CHECK_SIGNATURE) += check_signature.o obj-$(CONFIG_DEBUG_LOCKING_API_SELFTESTS) += locking-selftest.o +obj-y += logic_pio.o + obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o obj-$(CONFIG_BTREE) += btree.o diff --git a/lib/logic_pio.c b/lib/logic_pio.c new file mode 100644 index 000000000000..feea48fd1a0d --- /dev/null +++ b/lib/logic_pio.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved. + * Author: Gabriele Paoloni + * Author: Zhichang Yuan + */ + +#define pr_fmt(fmt) "LOGIC PIO: " fmt + +#include +#include +#include +#include +#include +#include +#include + +/* The unique hardware address list */ +static LIST_HEAD(io_range_list); +static DEFINE_MUTEX(io_range_mutex); + +/* Consider a kernel general helper for this */ +#define in_range(b, first, len) ((b) >= (first) && (b) < (first) + (len)) + +/** + * logic_pio_register_range - register logical PIO range for a host + * @new_range: pointer to the IO range to be registered. + * + * Returns 0 on success, the error code in case of failure. + * + * Register a new IO range node in the IO range list. + */ +int logic_pio_register_range(struct logic_pio_hwaddr *new_range) +{ + struct logic_pio_hwaddr *range; + resource_size_t start; + resource_size_t end; + resource_size_t mmio_sz = 0; + resource_size_t iio_sz = MMIO_UPPER_LIMIT; + int ret = 0; + + if (!new_range || !new_range->fwnode || !new_range->size) + return -EINVAL; + + start = new_range->hw_start; + end = new_range->hw_start + new_range->size; + + mutex_lock(&io_range_mutex); + list_for_each_entry_rcu(range, &io_range_list, list) { + if (range->fwnode == new_range->fwnode) { + /* range already there */ + goto end_register; + } + if (range->flags == LOGIC_PIO_CPU_MMIO && + new_range->flags == LOGIC_PIO_CPU_MMIO) { + /* for MMIO ranges we need to check for overlap */ + if (start >= range->hw_start + range->size || + end < range->hw_start) { + mmio_sz += range->size; + } else { + ret = -EFAULT; + goto end_register; + } + } else if (range->flags == LOGIC_PIO_INDIRECT && + new_range->flags == LOGIC_PIO_INDIRECT) { + iio_sz += range->size; + } + } + + /* range not registered yet, check for available space */ + if (new_range->flags == LOGIC_PIO_CPU_MMIO) { + if (mmio_sz + new_range->size - 1 > MMIO_UPPER_LIMIT) { + /* if it's too big check if 64K space can be reserved */ + if (mmio_sz + SZ_64K - 1 > MMIO_UPPER_LIMIT) { + ret = -E2BIG; + goto end_register; + } + new_range->size = SZ_64K; + pr_warn("Requested IO range too big, new size set to 64K\n"); + } + new_range->io_start = mmio_sz; + } else if (new_range->flags == LOGIC_PIO_INDIRECT) { + if (iio_sz + new_range->size - 1 > IO_SPACE_LIMIT) { + ret = -E2BIG; + goto end_register; + } + new_range->io_start = iio_sz; + } else { + /* invalid flag */ + ret = -EINVAL; + goto end_register; + } + + list_add_tail_rcu(&new_range->list, &io_range_list); + +end_register: + mutex_unlock(&io_range_mutex); + return ret; +} + +/** + * find_io_range_by_fwnode - find logical PIO range for given FW node + * @fwnode: FW node handle associated with logical PIO range + * + * Returns pointer to node on success, NULL otherwise. + * + * Traverse the io_range_list to find the registered node for @fwnode. + */ +struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode) +{ + struct logic_pio_hwaddr *range; + + list_for_each_entry_rcu(range, &io_range_list, list) { + if (range->fwnode == fwnode) + return range; + } + return NULL; +} + +/* Return a registered range given an input PIO token */ +static struct logic_pio_hwaddr *find_io_range(unsigned long pio) +{ + struct logic_pio_hwaddr *range; + + list_for_each_entry_rcu(range, &io_range_list, list) { + if (in_range(pio, range->io_start, range->size)) + return range; + } + pr_err("PIO entry token %lx invalid\n", pio); + return NULL; +} + +/** + * logic_pio_to_hwaddr - translate logical PIO to HW address + * @pio: logical PIO value + * + * Returns HW address if valid, ~0 otherwise. + * + * Translate the input logical PIO to the corresponding hardware address. + * The input PIO should be unique in the whole logical PIO space. + */ +resource_size_t logic_pio_to_hwaddr(unsigned long pio) +{ + struct logic_pio_hwaddr *range; + + range = find_io_range(pio); + if (range) + return range->hw_start + pio - range->io_start; + + return (resource_size_t)~0; +} + +/** + * logic_pio_trans_hwaddr - translate HW address to logical PIO + * @fwnode: FW node reference for the host + * @addr: Host-relative HW address + * @size: size to translate + * + * Returns Logical PIO value if successful, ~0UL otherwise + */ +unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode, + resource_size_t addr, resource_size_t size) +{ + struct logic_pio_hwaddr *range; + + range = find_io_range_by_fwnode(fwnode); + if (!range || range->flags == LOGIC_PIO_CPU_MMIO) { + pr_err("IO range not found or invalid\n"); + return ~0UL; + } + if (range->size < size) { + pr_err("resource size %pa cannot fit in IO range size %pa\n", + &size, &range->size); + return ~0UL; + } + return addr - range->hw_start + range->io_start; +} + +unsigned long logic_pio_trans_cpuaddr(resource_size_t addr) +{ + struct logic_pio_hwaddr *range; + + list_for_each_entry_rcu(range, &io_range_list, list) { + if (range->flags != LOGIC_PIO_CPU_MMIO) + continue; + if (in_range(addr, range->hw_start, range->size)) + return addr - range->hw_start + range->io_start; + } + pr_err("addr %llx not registered in io_range_list\n", + (unsigned long long) addr); + return ~0UL; +} + +#if defined(CONFIG_INDIRECT_PIO) && defined(PCI_IOBASE) +#define BUILD_LOGIC_IO(bw, type) \ +type logic_in##bw(unsigned long addr) \ +{ \ + type ret = (type)~0; \ + \ + if (addr < MMIO_UPPER_LIMIT) { \ + ret = read##bw(PCI_IOBASE + addr); \ + } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ + struct logic_pio_hwaddr *entry = find_io_range(addr); \ + \ + if (entry && entry->ops) \ + ret = entry->ops->in(entry->hostdata, \ + addr, sizeof(type)); \ + else \ + WARN_ON_ONCE(1); \ + } \ + return ret; \ +} \ + \ +void logic_out##bw(type value, unsigned long addr) \ +{ \ + if (addr < MMIO_UPPER_LIMIT) { \ + write##bw(value, PCI_IOBASE + addr); \ + } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ + struct logic_pio_hwaddr *entry = find_io_range(addr); \ + \ + if (entry && entry->ops) \ + entry->ops->out(entry->hostdata, \ + addr, value, sizeof(type)); \ + else \ + WARN_ON_ONCE(1); \ + } \ +} \ + \ +void logic_ins##bw(unsigned long addr, void *buffer, \ + unsigned int count) \ +{ \ + if (addr < MMIO_UPPER_LIMIT) { \ + reads##bw(PCI_IOBASE + addr, buffer, count); \ + } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ + struct logic_pio_hwaddr *entry = find_io_range(addr); \ + \ + if (entry && entry->ops) \ + entry->ops->ins(entry->hostdata, \ + addr, buffer, sizeof(type), count); \ + else \ + WARN_ON_ONCE(1); \ + } \ + \ +} \ + \ +void logic_outs##bw(unsigned long addr, const void *buffer, \ + unsigned int count) \ +{ \ + if (addr < MMIO_UPPER_LIMIT) { \ + writes##bw(PCI_IOBASE + addr, buffer, count); \ + } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ + struct logic_pio_hwaddr *entry = find_io_range(addr); \ + \ + if (entry && entry->ops) \ + entry->ops->outs(entry->hostdata, \ + addr, buffer, sizeof(type), count); \ + else \ + WARN_ON_ONCE(1); \ + } \ +} + +BUILD_LOGIC_IO(b, u8) +EXPORT_SYMBOL(logic_inb); +EXPORT_SYMBOL(logic_insb); +EXPORT_SYMBOL(logic_outb); +EXPORT_SYMBOL(logic_outsb); + +BUILD_LOGIC_IO(w, u16) +EXPORT_SYMBOL(logic_inw); +EXPORT_SYMBOL(logic_insw); +EXPORT_SYMBOL(logic_outw); +EXPORT_SYMBOL(logic_outsw); + +BUILD_LOGIC_IO(l, u32) +EXPORT_SYMBOL(logic_inl); +EXPORT_SYMBOL(logic_insl); +EXPORT_SYMBOL(logic_outl); +EXPORT_SYMBOL(logic_outsl); + +#endif /* CONFIG_INDIRECT_PIO && PCI_IOBASE */ -- cgit v1.2.3 From e734016dd33ac35a4608ea5117cb28d6b45f5bd5 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Tue, 20 Mar 2018 17:12:12 +0000 Subject: PCI: kirin: Make struct kirin_pcie_driver static This was generated from 0-day builder. Signed-off-by: Fengguang Wu [robh: add commit msg] Signed-off-by: Rob Herring [lorenzo.pieralisi@arm.com: reworked the commit log] Signed-off-by: Lorenzo Pieralisi --- drivers/pci/dwc/pcie-kirin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/dwc/pcie-kirin.c b/drivers/pci/dwc/pcie-kirin.c index dcc8cedf6e17..a6b88c7f6e3e 100644 --- a/drivers/pci/dwc/pcie-kirin.c +++ b/drivers/pci/dwc/pcie-kirin.c @@ -504,7 +504,7 @@ static const struct of_device_id kirin_pcie_match[] = { {}, }; -struct platform_driver kirin_pcie_driver = { +static struct platform_driver kirin_pcie_driver = { .probe = kirin_pcie_probe, .driver = { .name = "kirin-pcie", -- cgit v1.2.3 From 492d98e4f8b37ab802274c33386da36e801e9f64 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Tue, 20 Mar 2018 17:17:23 +0000 Subject: PCI: faraday: Make struct faraday_pci_variant static This was generated from 0-day builder. Signed-off-by: Fengguang Wu [lorenzo.pieralisi@arm.com: reworked/split patch] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Linus Walleij --- drivers/pci/host/pci-ftpci100.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/host/pci-ftpci100.c b/drivers/pci/host/pci-ftpci100.c index b9617d1c1d48..5008fd87956a 100644 --- a/drivers/pci/host/pci-ftpci100.c +++ b/drivers/pci/host/pci-ftpci100.c @@ -586,11 +586,11 @@ static int faraday_pci_probe(struct platform_device *pdev) * We encode bridge variants here, we have at least two so it doesn't * hurt to have infrastructure to encompass future variants as well. */ -const struct faraday_pci_variant faraday_regular = { +static const struct faraday_pci_variant faraday_regular = { .cascaded_irq = true, }; -const struct faraday_pci_variant faraday_dual = { +static const struct faraday_pci_variant faraday_dual = { .cascaded_irq = false, }; -- cgit v1.2.3 From d17086728ca115cdb7efa40f9f4e0092648fee41 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Wed, 7 Mar 2018 09:42:39 -0600 Subject: PCI: rcar: Remove unnecessary semicolon Remove unneeded semicolon. Generated by: scripts/coccinelle/misc/semicolon.cocci Signed-off-by: Fengguang Wu Signed-off-by: Julia Lawall Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi --- drivers/pci/host/pcie-rcar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index b4c4aad2cf66..6ab28f29ac6a 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -435,7 +435,7 @@ static void rcar_pcie_force_speedup(struct rcar_pcie *pcie) } msleep(1); - }; + } dev_err(dev, "Speed change timed out\n"); -- cgit v1.2.3 From 6c994c504fa21843c667edece593dbfa4634b046 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Wed, 7 Mar 2018 09:42:40 -0600 Subject: PCI: v3-semi: Remove unnecessary semicolon drivers/pci/host/pci-v3-semi.c:676:2-3: Unneeded semicolon Remove unneeded semicolon. Generated by: scripts/coccinelle/misc/semicolon.cocci Signed-off-by: Fengguang Wu Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Reviewed-by: Linus Walleij --- drivers/pci/host/pci-v3-semi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/host/pci-v3-semi.c b/drivers/pci/host/pci-v3-semi.c index 7fef64869d19..0a4dea796663 100644 --- a/drivers/pci/host/pci-v3-semi.c +++ b/drivers/pci/host/pci-v3-semi.c @@ -673,7 +673,7 @@ static int v3_get_dma_range_config(struct v3_pci *v3, dev_err(v3->dev, "illegal dma memory chunk size\n"); return -EINVAL; break; - }; + } val |= V3_PCI_MAP_M_REG_EN | V3_PCI_MAP_M_ENABLE; *pci_map = val; -- cgit v1.2.3 From 745029187a5465972fa2daf0fa43f1d2edb48de9 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Thu, 22 Mar 2018 16:33:17 +0000 Subject: PCI: pcie-xilinx-nwl: Fix mask value to disable MSIs Compiling the xilinx-nwl driver with sparse checks result in the following warning: drivers/pci/host/pcie-xilinx-nwl.c:633:38: sparse: cast truncates bits from constant value (ffffffff00000000 becomes 0) Fix it by explicitly writing 0 to mask interrupts instead of relying on a bogus cast applied to the mask bitwise complement. Reported-by: Fengguang Wu Signed-off-by: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Rob Herring Cc: Michal Simek --- drivers/pci/host/pcie-xilinx-nwl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/host/pcie-xilinx-nwl.c b/drivers/pci/host/pcie-xilinx-nwl.c index 0acaf483d031..4839ae578711 100644 --- a/drivers/pci/host/pcie-xilinx-nwl.c +++ b/drivers/pci/host/pcie-xilinx-nwl.c @@ -630,7 +630,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie) * For high range MSI interrupts: disable, clear any pending, * and enable */ - nwl_bridge_writel(pcie, (u32)~MSGF_MSI_SR_HI_MASK, MSGF_MSI_MASK_HI); + nwl_bridge_writel(pcie, 0, MSGF_MSI_MASK_HI); nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MSI_STATUS_HI) & MSGF_MSI_SR_HI_MASK, MSGF_MSI_STATUS_HI); @@ -641,7 +641,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie) * For low range MSI interrupts: disable, clear any pending, * and enable */ - nwl_bridge_writel(pcie, (u32)~MSGF_MSI_SR_LO_MASK, MSGF_MSI_MASK_LO); + nwl_bridge_writel(pcie, 0, MSGF_MSI_MASK_LO); nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MSI_STATUS_LO) & MSGF_MSI_SR_LO_MASK, MSGF_MSI_STATUS_LO); -- cgit v1.2.3 From 13d3047c81505cc0fb9bdae7810676e70523c8bf Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 12 Feb 2018 13:55:23 +0300 Subject: ACPI / hotplug / PCI: Check presence of slot itself in get_slot_status() Mike Lothian reported that plugging in a USB-C device does not work properly in his Dell Alienware system. This system has an Intel Alpine Ridge Thunderbolt controller providing USB-C functionality. In these systems the USB controller (xHCI) is hotplugged whenever a device is connected to the port using ACPI-based hotplug. The ACPI description of the root port in question is as follows: Device (RP01) { Name (_ADR, 0x001C0000) Device (PXSX) { Name (_ADR, 0x02) Method (_RMV, 0, NotSerialized) { // ... } } Here _ADR 0x02 means device 0, function 2 on the bus under root port (RP01) but that seems to be incorrect because device 0 is the upstream port of the Alpine Ridge PCIe switch and it has no functions other than 0 (the bridge itself). When we get ACPI Notify() to the root port resulting from connecting a USB-C device, Linux tries to read PCI_VENDOR_ID from device 0, function 2 which of course always returns 0xffffffff because there is no such function and we never find the device. In Windows this works fine. Now, since we get ACPI Notify() to the root port and not to the PXSX device we should actually start our scan from there as well and not from the non-existent PXSX device. Fix this by checking presence of the slot itself (function 0) if we fail to do that otherwise. While there use pci_bus_read_dev_vendor_id() in get_slot_status(), which is the recommended way to read Device and Vendor IDs of devices on PCI buses. Link: https://bugzilla.kernel.org/show_bug.cgi?id=198557 Reported-by: Mike Lothian Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Cc: Greg Kroah-Hartman Cc: stable@vger.kernel.org --- drivers/pci/hotplug/acpiphp_glue.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index e2198a2feeca..b45b375c0e6c 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -541,6 +541,7 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot) { unsigned long long sta = 0; struct acpiphp_func *func; + u32 dvid; list_for_each_entry(func, &slot->funcs, sibling) { if (func->flags & FUNC_HAS_STA) { @@ -551,19 +552,27 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot) if (ACPI_SUCCESS(status) && sta) break; } else { - u32 dvid; - - pci_bus_read_config_dword(slot->bus, - PCI_DEVFN(slot->device, - func->function), - PCI_VENDOR_ID, &dvid); - if (dvid != 0xffffffff) { + if (pci_bus_read_dev_vendor_id(slot->bus, + PCI_DEVFN(slot->device, func->function), + &dvid, 0)) { sta = ACPI_STA_ALL; break; } } } + if (!sta) { + /* + * Check for the slot itself since it may be that the + * ACPI slot is a device below PCIe upstream port so in + * that case it may not even be reachable yet. + */ + if (pci_bus_read_dev_vendor_id(slot->bus, + PCI_DEVFN(slot->device, 0), &dvid, 0)) { + sta = ACPI_STA_ALL; + } + } + return (unsigned int)sta; } -- cgit v1.2.3 From 6cf57be0f78e289aaf236f8bc55c40ea6c422c75 Mon Sep 17 00:00:00 2001 From: Tal Gilboa Date: Fri, 30 Mar 2018 07:44:05 -0500 Subject: PCI: Add pcie_get_speed_cap() to find max supported link speed Add pcie_get_speed_cap() to find the max link speed supported by a device. Change max_link_speed_show() to use pcie_get_speed_cap(). Signed-off-by: Tal Gilboa [bhelgaas: return speed directly instead of error and *speed, don't export outside drivers/pci] Signed-off-by: Bjorn Helgaas Reviewed-by: Tariq Toukan --- drivers/pci/pci-sysfs.c | 28 ++-------------------------- drivers/pci/pci.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 10 ++++++++++ 3 files changed, 56 insertions(+), 26 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 7dc5be545d18..c2ea05fbbf1d 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -158,33 +158,9 @@ static DEVICE_ATTR_RO(resource); static ssize_t max_link_speed_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct pci_dev *pci_dev = to_pci_dev(dev); - u32 linkcap; - int err; - const char *speed; - - err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &linkcap); - if (err) - return -EINVAL; - - switch (linkcap & PCI_EXP_LNKCAP_SLS) { - case PCI_EXP_LNKCAP_SLS_16_0GB: - speed = "16 GT/s"; - break; - case PCI_EXP_LNKCAP_SLS_8_0GB: - speed = "8 GT/s"; - break; - case PCI_EXP_LNKCAP_SLS_5_0GB: - speed = "5 GT/s"; - break; - case PCI_EXP_LNKCAP_SLS_2_5GB: - speed = "2.5 GT/s"; - break; - default: - speed = "Unknown speed"; - } + struct pci_dev *pdev = to_pci_dev(dev); - return sprintf(buf, "%s\n", speed); + return sprintf(buf, "%s\n", PCIE_SPEED2STR(pcie_get_speed_cap(pdev))); } static DEVICE_ATTR_RO(max_link_speed); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index f6a4dd10d9b0..b29d3436ee9f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5146,6 +5146,50 @@ int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, } EXPORT_SYMBOL(pcie_get_minimum_link); +/** + * pcie_get_speed_cap - query for the PCI device's link speed capability + * @dev: PCI device to query + * + * Query the PCI device speed capability. Return the maximum link speed + * supported by the device. + */ +enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev) +{ + u32 lnkcap2, lnkcap; + + /* + * PCIe r4.0 sec 7.5.3.18 recommends using the Supported Link + * Speeds Vector in Link Capabilities 2 when supported, falling + * back to Max Link Speed in Link Capabilities otherwise. + */ + pcie_capability_read_dword(dev, PCI_EXP_LNKCAP2, &lnkcap2); + if (lnkcap2) { /* PCIe r3.0-compliant */ + if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_16_0GB) + return PCIE_SPEED_16_0GT; + else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) + return PCIE_SPEED_8_0GT; + else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB) + return PCIE_SPEED_5_0GT; + else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB) + return PCIE_SPEED_2_5GT; + return PCI_SPEED_UNKNOWN; + } + + pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); + if (lnkcap) { + if (lnkcap & PCI_EXP_LNKCAP_SLS_16_0GB) + return PCIE_SPEED_16_0GT; + else if (lnkcap & PCI_EXP_LNKCAP_SLS_8_0GB) + return PCIE_SPEED_8_0GT; + else if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB) + return PCIE_SPEED_5_0GT; + else if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB) + return PCIE_SPEED_2_5GT; + } + + return PCI_SPEED_UNKNOWN; +} + /** * pci_select_bars - Make BAR mask from the type of resource * @dev: the PCI device for which BAR mask is made diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index fcd81911b127..1186d8be6055 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -253,6 +253,16 @@ bool pci_bus_clip_resource(struct pci_dev *dev, int idx); void pci_reassigndev_resource_alignment(struct pci_dev *dev); void pci_disable_bridge_window(struct pci_dev *dev); +/* PCIe link information */ +#define PCIE_SPEED2STR(speed) \ + ((speed) == PCIE_SPEED_16_0GT ? "16 GT/s" : \ + (speed) == PCIE_SPEED_8_0GT ? "8 GT/s" : \ + (speed) == PCIE_SPEED_5_0GT ? "5 GT/s" : \ + (speed) == PCIE_SPEED_2_5GT ? "2.5 GT/s" : \ + "Unknown speed") + +enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev); + /* Single Root I/O Virtualization */ struct pci_sriov { int pos; /* Capability position */ -- cgit v1.2.3 From c70b65fb7f121da7d01f62588ce6abb4741f513f Mon Sep 17 00:00:00 2001 From: Tal Gilboa Date: Fri, 30 Mar 2018 08:24:36 -0500 Subject: PCI: Add pcie_get_width_cap() to find max supported link width Add pcie_get_width_cap() to find the max link width supported by a device. Change max_link_width_show() to use pcie_get_width_cap(). Signed-off-by: Tal Gilboa [bhelgaas: return width directly instead of error and *width, don't export outside drivers/pci] Signed-off-by: Bjorn Helgaas Reviewed-by: Tariq Toukan --- drivers/pci/pci-sysfs.c | 10 ++-------- drivers/pci/pci.c | 18 ++++++++++++++++++ drivers/pci/pci.h | 1 + 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index c2ea05fbbf1d..63d0952684fb 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -167,15 +167,9 @@ static DEVICE_ATTR_RO(max_link_speed); static ssize_t max_link_width_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct pci_dev *pci_dev = to_pci_dev(dev); - u32 linkcap; - int err; - - err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &linkcap); - if (err) - return -EINVAL; + struct pci_dev *pdev = to_pci_dev(dev); - return sprintf(buf, "%u\n", (linkcap & PCI_EXP_LNKCAP_MLW) >> 4); + return sprintf(buf, "%u\n", pcie_get_width_cap(pdev)); } static DEVICE_ATTR_RO(max_link_width); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b29d3436ee9f..43075be79388 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5190,6 +5190,24 @@ enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev) return PCI_SPEED_UNKNOWN; } +/** + * pcie_get_width_cap - query for the PCI device's link width capability + * @dev: PCI device to query + * + * Query the PCI device width capability. Return the maximum link width + * supported by the device. + */ +enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev) +{ + u32 lnkcap; + + pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); + if (lnkcap) + return (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4; + + return PCIE_LNK_WIDTH_UNKNOWN; +} + /** * pci_select_bars - Make BAR mask from the type of resource * @dev: the PCI device for which BAR mask is made diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 1186d8be6055..66738f1050c0 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -262,6 +262,7 @@ void pci_disable_bridge_window(struct pci_dev *dev); "Unknown speed") enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev); +enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev); /* Single Root I/O Virtualization */ struct pci_sriov { -- cgit v1.2.3 From b852f63aa6cee3f4846383377c414ae9c4fbc166 Mon Sep 17 00:00:00 2001 From: Tal Gilboa Date: Fri, 30 Mar 2018 08:32:03 -0500 Subject: PCI: Add pcie_bandwidth_capable() to compute max supported link bandwidth Add pcie_bandwidth_capable() to compute the max link bandwidth supported by a device, based on the max link speed and width, adjusted by the encoding overhead. The maximum bandwidth of the link is computed as: max_link_width * max_link_speed * (1 - encoding_overhead) 2.5 and 5.0 GT/s links use 8b/10b encoding, which reduces the raw bandwidth available by 20%; 8.0 GT/s and faster links use 128b/130b encoding, which reduces it by about 1.5%. The result is in Mb/s, i.e., megabits/second, of raw bandwidth. Signed-off-by: Tal Gilboa [bhelgaas: add 16 GT/s, adjust for pcie_get_speed_cap() and pcie_get_width_cap() signatures, don't export outside drivers/pci] Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 22 ++++++++++++++++++++++ drivers/pci/pci.h | 10 ++++++++++ 2 files changed, 32 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 43075be79388..ff1e72060952 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5208,6 +5208,28 @@ enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev) return PCIE_LNK_WIDTH_UNKNOWN; } +/** + * pcie_bandwidth_capable - calculate a PCI device's link bandwidth capability + * @dev: PCI device + * @speed: storage for link speed + * @width: storage for link width + * + * Calculate a PCI device's link bandwidth by querying for its link speed + * and width, multiplying them, and applying encoding overhead. The result + * is in Mb/s, i.e., megabits/second of raw bandwidth. + */ +u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed, + enum pcie_link_width *width) +{ + *speed = pcie_get_speed_cap(dev); + *width = pcie_get_width_cap(dev); + + if (*speed == PCI_SPEED_UNKNOWN || *width == PCIE_LNK_WIDTH_UNKNOWN) + return 0; + + return *width * PCIE_SPEED2MBS_ENC(*speed); +} + /** * pci_select_bars - Make BAR mask from the type of resource * @dev: the PCI device for which BAR mask is made diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 66738f1050c0..ce9adec6c66e 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -261,8 +261,18 @@ void pci_disable_bridge_window(struct pci_dev *dev); (speed) == PCIE_SPEED_2_5GT ? "2.5 GT/s" : \ "Unknown speed") +/* PCIe speed to Mb/s reduced by encoding overhead */ +#define PCIE_SPEED2MBS_ENC(speed) \ + ((speed) == PCIE_SPEED_16_0GT ? 16000*128/130 : \ + (speed) == PCIE_SPEED_8_0GT ? 8000*128/130 : \ + (speed) == PCIE_SPEED_5_0GT ? 5000*8/10 : \ + (speed) == PCIE_SPEED_2_5GT ? 2500*8/10 : \ + 0) + enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev); enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev); +u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed, + enum pcie_link_width *width); /* Single Root I/O Virtualization */ struct pci_sriov { -- cgit v1.2.3 From 3620c71484f7a19b2588e577ea732f55719f5b1f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:06:55 -0600 Subject: PCI/PM: Clear PCIe PME Status bit for Root Complex Event Collectors Per PCIe r4.0, sec 6.1.6, Root Complex Event Collectors can generate PME interrupts on behalf of Root Complex Integrated Endpoints. Linux does not currently enable PME interrupts from RC Event Collectors, but fe31e69740ed ("PCI/PCIe: Clear Root PME Status bits early during system resume") suggests PME interrupts may be enabled by the platform for ACPI- based runtime wakeup. Clear the PCIe PME Status bit for Root Complex Event Collectors during resume, just like we already do for Root Ports. If the BIOS enables PME interrupts for an event collector and neglects to clear the status bit on resume, this change should fix the same bug as fe31e69740ed (PMEs not working after waking from a sleep state), but for Root Complex Integrated Endpoints. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pci-driver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index c49af2b679bc..646da0d2d7a8 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -722,7 +722,8 @@ static void pcie_pme_root_status_cleanup(struct pci_dev *pci_dev) * Clear those bits now just in case (shouldn't hurt). */ if (pci_is_pcie(pci_dev) && - pci_pcie_type(pci_dev) == PCI_EXP_TYPE_ROOT_PORT) + (pci_pcie_type(pci_dev) == PCI_EXP_TYPE_ROOT_PORT || + pci_pcie_type(pci_dev) == PCI_EXP_TYPE_RC_EC)) pcie_clear_root_pme_status(pci_dev); } -- cgit v1.2.3 From 79a011194b23302dc43f265ed4237054877768ff Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:06:56 -0600 Subject: PCI/portdrv: Disable port driver in compat mode The "pcie_ports=compat" kernel parameter sets pcie_ports_disabled, which is intended to disable the PCIe port driver. But even when it was disabled, we registered pcie_portdriver so we could work around a BIOS PME issue (see fe31e69740ed ("PCI/PCIe: Clear Root PME Status bits early during system resume")). Registering the driver meant that the pcie_portdrv_probe() path called pci_enable_device(), pci_save_state(), pm_runtime_set_autosuspend_delay(), pm_runtime_use_autosuspend(), etc., even when the driver was disabled. We've since moved the BIOS PME workaround from the port driver to the core, so stop registering the PCIe port driver in compat mode. This means "pcie_ports=compat" will now be basically the same as turning off CONFIG_PCIEPORTBUS completely. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pcie/portdrv_core.c | 3 --- drivers/pci/pcie/portdrv_pci.c | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 4268b2fc2c7a..9a41751db332 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -211,9 +211,6 @@ static int get_port_device_capability(struct pci_dev *dev) int services = 0; int cap_mask = 0; - if (pcie_ports_disabled) - return 0; - cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP | PCIE_PORT_SERVICE_VC; if (pci_aer_available()) diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index ec9e936c2a5b..5d9d5305ebef 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -261,7 +261,7 @@ static int __init pcie_portdrv_init(void) int retval; if (pcie_ports_disabled) - return pci_register_driver(&pcie_portdriver); + return -EACCES; dmi_check_system(pcie_portdrv_dmi_table); -- cgit v1.2.3 From c6c889d932bb49d95273711a790d16f814cb213b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:06:56 -0600 Subject: PCI/portdrv: Remove pcie_port_bus_type link order dependency The pcie_port_bus_type must be registered before drivers that depend on it can be registered. Those drivers include: pcied_init() # PCIe native hotplug driver aer_service_init() # AER driver dpc_service_init() # DPC driver pcie_pme_service_init() # PME driver Previously we registered pcie_port_bus_type from pcie_portdrv_init(), a device_initcall. The callers of pcie_port_service_register() (above) are also device_initcalls. This is fragile because the device_initcall ordering depends on link order, which is not explicit. Register pcie_port_bus_type from pci_driver_init() along with pci_bus_type. This removes the link order dependency between portdrv and the pciehp, AER, DPC, and PCIe PME drivers. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Reviewed-by: Christoph Hellwig --- drivers/pci/pci-driver.c | 44 ++++++++++++++++++++++++++++++++- drivers/pci/pcie/Makefile | 2 +- drivers/pci/pcie/portdrv_bus.c | 55 ------------------------------------------ drivers/pci/pcie/portdrv_pci.c | 13 +--------- 4 files changed, 45 insertions(+), 69 deletions(-) delete mode 100644 drivers/pci/pcie/portdrv_bus.c diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 646da0d2d7a8..21eb2f7ad95d 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -19,6 +19,7 @@ #include #include #include "pci.h" +#include "pcie/portdrv.h" struct pci_dynid { struct list_head node; @@ -1552,8 +1553,49 @@ struct bus_type pci_bus_type = { }; EXPORT_SYMBOL(pci_bus_type); +#ifdef CONFIG_PCIEPORTBUS +static int pcie_port_bus_match(struct device *dev, struct device_driver *drv) +{ + struct pcie_device *pciedev; + struct pcie_port_service_driver *driver; + + if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type) + return 0; + + pciedev = to_pcie_device(dev); + driver = to_service_driver(drv); + + if (driver->service != pciedev->service) + return 0; + + if (driver->port_type != PCIE_ANY_PORT && + driver->port_type != pci_pcie_type(pciedev->port)) + return 0; + + return 1; +} + +struct bus_type pcie_port_bus_type = { + .name = "pci_express", + .match = pcie_port_bus_match, +}; +EXPORT_SYMBOL_GPL(pcie_port_bus_type); +#endif + static int __init pci_driver_init(void) { - return bus_register(&pci_bus_type); + int ret; + + ret = bus_register(&pci_bus_type); + if (ret) + return ret; + +#ifdef CONFIG_PCIEPORTBUS + ret = bus_register(&pcie_port_bus_type); + if (ret) + return ret; +#endif + + return 0; } postcore_initcall(pci_driver_init); diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile index 223e4c34c29a..e01c10c97b95 100644 --- a/drivers/pci/pcie/Makefile +++ b/drivers/pci/pcie/Makefile @@ -6,7 +6,7 @@ # Build PCI Express ASPM if needed obj-$(CONFIG_PCIEASPM) += aspm.o -pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o +pcieportdrv-y := portdrv_core.o portdrv_pci.o pcieportdrv-$(CONFIG_ACPI) += portdrv_acpi.o obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c deleted file mode 100644 index 4969ccf6b214..000000000000 --- a/drivers/pci/pcie/portdrv_bus.c +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * File: portdrv_bus.c - * Purpose: PCI Express Port Bus Driver's Bus Overloading Functions - * - * Copyright (C) 2004 Intel - * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) - */ - -#include -#include -#include -#include -#include - -#include "portdrv.h" - -static int pcie_port_bus_match(struct device *dev, struct device_driver *drv); - -struct bus_type pcie_port_bus_type = { - .name = "pci_express", - .match = pcie_port_bus_match, -}; -EXPORT_SYMBOL_GPL(pcie_port_bus_type); - -static int pcie_port_bus_match(struct device *dev, struct device_driver *drv) -{ - struct pcie_device *pciedev; - struct pcie_port_service_driver *driver; - - if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type) - return 0; - - pciedev = to_pcie_device(dev); - driver = to_service_driver(drv); - - if (driver->service != pciedev->service) - return 0; - - if ((driver->port_type != PCIE_ANY_PORT) && - (driver->port_type != pci_pcie_type(pciedev->port))) - return 0; - - return 1; -} - -int pcie_port_bus_register(void) -{ - return bus_register(&pcie_port_bus_type); -} - -void pcie_port_bus_unregister(void) -{ - bus_unregister(&pcie_port_bus_type); -} diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 5d9d5305ebef..127321e17184 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -258,22 +258,11 @@ static const struct dmi_system_id pcie_portdrv_dmi_table[] __initconst = { static int __init pcie_portdrv_init(void) { - int retval; - if (pcie_ports_disabled) return -EACCES; dmi_check_system(pcie_portdrv_dmi_table); - retval = pcie_port_bus_register(); - if (retval) { - printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval); - goto out; - } - retval = pci_register_driver(&pcie_portdriver); - if (retval) - pcie_port_bus_unregister(); - out: - return retval; + return pci_register_driver(&pcie_portdriver); } device_initcall(pcie_portdrv_init); -- cgit v1.2.3 From 168f3ae595d6a6cee8321633f29273a7dd4fc83e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:21:24 -0600 Subject: PCI/portdrv: Remove unused PCIE_PORT_SERVICE_VC No driver registers for PCIE_PORT_SERVICE_VC, so remove it. This removes the VC "service" files from /sys/bus/pci_express/devices, e.g., 0000:07:00.0:pcie108, 0000:08:04.0:pcie208 (all the files that contained "8" as the last digit of the "pcieXXX" part). The port driver created these files for PCIe port devices that have a VC Capability. Since this reduces PCIE_PORT_DEVICE_MAXSERVICES and moves DPC down into the spot where VC used to be, the DPC sysfs files will now be named "pcieXX8". I don't think there's anything useful userspace can do with those files, so I hope nobody cares about these filenames. There is no VC driver that calls pcie_port_service_register(), so there never was a /sys/bus/pci_express/drivers/vc directory. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Reviewed-by: Christoph Hellwig --- drivers/pci/pcie/portdrv.h | 6 ++---- drivers/pci/pcie/portdrv_acpi.c | 2 +- drivers/pci/pcie/portdrv_core.c | 14 ++++---------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 7086086e45d0..7bfd75f9197b 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -19,12 +19,10 @@ #define PCIE_PORT_SERVICE_AER (1 << PCIE_PORT_SERVICE_AER_SHIFT) #define PCIE_PORT_SERVICE_HP_SHIFT 2 /* Native Hotplug */ #define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT) -#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */ -#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT) -#define PCIE_PORT_SERVICE_DPC_SHIFT 4 /* Downstream Port Containment */ +#define PCIE_PORT_SERVICE_DPC_SHIFT 3 /* Downstream Port Containment */ #define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT) -#define PCIE_PORT_DEVICE_MAXSERVICES 5 +#define PCIE_PORT_DEVICE_MAXSERVICES 4 /* Port Type */ #define PCIE_ANY_PORT (~0) diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c index 53f60053bd47..9d12650dc2ae 100644 --- a/drivers/pci/pcie/portdrv_acpi.c +++ b/drivers/pci/pcie/portdrv_acpi.c @@ -47,7 +47,7 @@ void pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask) flags = root->osc_control_set; - *srv_mask = PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_DPC; + *srv_mask = PCIE_PORT_SERVICE_DPC; if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL) *srv_mask |= PCIE_PORT_SERVICE_HP; if (flags & OSC_PCI_EXPRESS_PME_CONTROL) diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 9a41751db332..bf851da97947 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -188,10 +188,8 @@ legacy_irq: if (ret < 0) return -ENODEV; - for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { - if (i != PCIE_PORT_SERVICE_VC_SHIFT) - irqs[i] = pci_irq_vector(dev, 0); - } + for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) + irqs[i] = pci_irq_vector(dev, 0); return 0; } @@ -211,8 +209,7 @@ static int get_port_device_capability(struct pci_dev *dev) int services = 0; int cap_mask = 0; - cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP - | PCIE_PORT_SERVICE_VC; + cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP; if (pci_aer_available()) cap_mask |= PCIE_PORT_SERVICE_AER | PCIE_PORT_SERVICE_DPC; @@ -239,9 +236,6 @@ static int get_port_device_capability(struct pci_dev *dev) */ pci_disable_pcie_error_reporting(dev); } - /* VC support */ - if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC)) - services |= PCIE_PORT_SERVICE_VC; /* Root ports are capable of generating PME too */ if ((cap_mask & PCIE_PORT_SERVICE_PME) && pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) { @@ -331,7 +325,7 @@ int pcie_port_device_register(struct pci_dev *dev) */ status = pcie_init_service_irqs(dev, irqs, capabilities); if (status) { - capabilities &= PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_HP; + capabilities &= PCIE_PORT_SERVICE_HP; if (!capabilities) goto error_disable; } -- cgit v1.2.3 From 02bfeb484230dfd073148a17253aeb1717ce769c Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:21:25 -0600 Subject: PCI/portdrv: Simplify PCIe feature permission checking Some PCIe features (AER, DPC, hotplug, PME) can be managed by either the platform firmware or the OS, so the host bridge driver may have to request permission from the platform before using them. On ACPI systems, this is done by negotiate_os_control() in acpi_pci_root_add(). The PCIe port driver later uses pcie_port_platform_notify() and pcie_port_acpi_setup() to figure out whether it can use these features. But all we need is a single bit for each service, so these interfaces are needlessly complicated. Simplify this by adding bits in the struct pci_host_bridge to show when the OS has permission to use each feature: + unsigned int native_aer:1; /* OS may use PCIe AER */ + unsigned int native_hotplug:1; /* OS may use PCIe hotplug */ + unsigned int native_pme:1; /* OS may use PCIe PME */ These are set when we create a host bridge, and the host bridge driver can clear the bits corresponding to any feature the platform doesn't want us to use. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/acpi/pci_root.c | 13 +++++++++++-- drivers/pci/pcie/Makefile | 1 - drivers/pci/pcie/portdrv.h | 11 ----------- drivers/pci/pcie/portdrv_core.c | 42 +++++++++++++++++++++++++---------------- drivers/pci/probe.c | 10 ++++++++++ include/linux/pci.h | 3 +++ 6 files changed, 50 insertions(+), 30 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 6fc204a52493..63b2cb775324 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -871,6 +871,7 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, struct acpi_device *device = root->device; int node = acpi_get_node(device->handle); struct pci_bus *bus; + struct pci_host_bridge *host_bridge; info->root = root; info->bridge = device; @@ -895,9 +896,17 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, if (!bus) goto out_release_info; + host_bridge = to_pci_host_bridge(bus->bridge); + if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)) + host_bridge->native_hotplug = 0; + if (!(root->osc_control_set & OSC_PCI_EXPRESS_AER_CONTROL)) + host_bridge->native_aer = 0; + if (!(root->osc_control_set & OSC_PCI_EXPRESS_PME_CONTROL)) + host_bridge->native_pme = 0; + pci_scan_child_bus(bus); - pci_set_host_bridge_release(to_pci_host_bridge(bus->bridge), - acpi_pci_root_release_info, info); + pci_set_host_bridge_release(host_bridge, acpi_pci_root_release_info, + info); if (node != NUMA_NO_NODE) dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", node); return bus; diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile index e01c10c97b95..11fb633b866c 100644 --- a/drivers/pci/pcie/Makefile +++ b/drivers/pci/pcie/Makefile @@ -7,7 +7,6 @@ obj-$(CONFIG_PCIEASPM) += aspm.o pcieportdrv-y := portdrv_core.o portdrv_pci.o -pcieportdrv-$(CONFIG_ACPI) += portdrv_acpi.o obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 7bfd75f9197b..ed84e767085f 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -123,15 +123,4 @@ static inline bool pcie_pme_no_msi(void) { return false; } static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {} #endif /* !CONFIG_PCIE_PME */ -#ifdef CONFIG_ACPI -void pcie_port_acpi_setup(struct pci_dev *port, int *mask); - -static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask) -{ - pcie_port_acpi_setup(port, mask); -} -#else /* !CONFIG_ACPI */ -static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask){} -#endif /* !CONFIG_ACPI */ - #endif /* _PORTDRV_H_ */ diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index bf851da97947..5c25761cd05e 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -206,19 +206,20 @@ legacy_irq: */ static int get_port_device_capability(struct pci_dev *dev) { + struct pci_host_bridge *host = pci_find_host_bridge(dev->bus); + bool native; int services = 0; - int cap_mask = 0; - cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP; - if (pci_aer_available()) - cap_mask |= PCIE_PORT_SERVICE_AER | PCIE_PORT_SERVICE_DPC; - - if (pcie_ports_auto) - pcie_port_platform_notify(dev, &cap_mask); + /* + * If the user specified "pcie_ports=native", use the PCIe services + * regardless of whether the platform has given us permission. On + * ACPI systems, this means we ignore _OSC. + */ + native = !pcie_ports_auto; - /* Hot-Plug Capable */ - if ((cap_mask & PCIE_PORT_SERVICE_HP) && dev->is_hotplug_bridge) { + if (dev->is_hotplug_bridge && (native || host->native_hotplug)) { services |= PCIE_PORT_SERVICE_HP; + /* * Disable hot-plug interrupts in case they have been enabled * by the BIOS and the hot-plug service driver is not loaded. @@ -226,20 +227,27 @@ static int get_port_device_capability(struct pci_dev *dev) pcie_capability_clear_word(dev, PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE); } - /* AER capable */ - if ((cap_mask & PCIE_PORT_SERVICE_AER) - && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) { + + if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR) && + pci_aer_available() && (native || host->native_aer)) { services |= PCIE_PORT_SERVICE_AER; + /* * Disable AER on this port in case it's been enabled by the * BIOS (the AER service driver will enable it when necessary). */ pci_disable_pcie_error_reporting(dev); } - /* Root ports are capable of generating PME too */ - if ((cap_mask & PCIE_PORT_SERVICE_PME) - && pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) { + + /* + * Root ports are capable of generating PME too. Root Complex + * Event Collectors can also generate PMEs, but we don't handle + * those yet. + */ + if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT && + (native || host->native_pme)) { services |= PCIE_PORT_SERVICE_PME; + /* * Disable PME interrupt on this port in case it's been enabled * by the BIOS (the PME service driver will enable it when @@ -247,7 +255,9 @@ static int get_port_device_capability(struct pci_dev *dev) */ pcie_pme_interrupt_enable(dev, false); } - if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC)) + + if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) && + pci_aer_available()) services |= PCIE_PORT_SERVICE_DPC; return services; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ef5377438a1e..a00de697a970 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -540,6 +540,16 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv) INIT_LIST_HEAD(&bridge->windows); bridge->dev.release = pci_release_host_bridge_dev; + /* + * We assume we can manage these PCIe features. Some systems may + * reserve these for use by the platform itself, e.g., an ACPI BIOS + * may implement its own AER handling and use _OSC to prevent the + * OS from interfering. + */ + bridge->native_aer = 1; + bridge->native_hotplug = 1; + bridge->native_pme = 1; + return bridge; } EXPORT_SYMBOL(pci_alloc_host_bridge); diff --git a/include/linux/pci.h b/include/linux/pci.h index 024a1beda008..a04b7abc6b7a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -469,6 +469,9 @@ struct pci_host_bridge { struct msi_controller *msi; unsigned int ignore_reset_delay:1; /* For entire hierarchy */ unsigned int no_ext_tags:1; /* No Extended Tags */ + unsigned int native_aer:1; /* OS may use PCIe AER */ + unsigned int native_hotplug:1; /* OS may use PCIe hotplug */ + unsigned int native_pme:1; /* OS may use PCIe PME */ /* Resource alignment requirements */ resource_size_t (*align_resource)(struct pci_dev *dev, const struct resource *res, -- cgit v1.2.3 From 1b64cb87cf0013cdd04b7e1665072a00f92b830f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:21:26 -0600 Subject: PCI/portdrv: Remove unnecessary include of portdrv_pci.c doesn't use anything from . Remove the include of it. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pcie/portdrv_pci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 127321e17184..1997d9f2743e 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -17,7 +17,6 @@ #include #include #include -#include #include "../pci.h" #include "portdrv.h" -- cgit v1.2.3 From 1e447c57ae367c030e7b424f55a91470ae7604ca Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:21:27 -0600 Subject: PCI/portdrv: Remove "pcie_hp=nomsi" kernel parameter 7570a333d8b0 ("PCI: Add pcie_hp=nomsi to disable MSI/MSI-X for pciehp driver") added the "pcie_hp=nomsi" kernel parameter to work around this error on shutdown: irq 16: nobody cared (try booting with the "irqpoll" option) Pid: 1081, comm: reboot Not tainted 3.2.0 #1 ... Disabling IRQ #16 This happened on an unspecified system (possibly involving the Integrated Device Technology, Inc. Device 807f bridge) where "an un-wanted interrupt is generated when PCI driver switches from MSI/MSI-X to INTx while shutting down the device." The implication was that the device was buggy, but it is normal for a device to use INTx after MSI/MSI-X have been disabled. The only problem was that the driver was still attached and it wasn't prepared for INTx interrupts. Prarit Bhargava fixed this issue with fda78d7a0ead ("PCI/MSI: Stop disabling MSI/MSI-X in pci_device_shutdown()"). There is no automated way to set this parameter, so it's not very useful for distributions or end users. It's really only useful for debugging, and we have "pci=nomsi" for that purpose. Revert 7570a333d8b0 to remove the "pcie_hp=nomsi" parameter. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki CC: MUNEDA Takahiro CC: Kenji Kaneshige CC: Prarit Bhargava --- Documentation/admin-guide/kernel-parameters.txt | 4 ---- drivers/pci/pcie/portdrv.h | 12 ------------ drivers/pci/pcie/portdrv_core.c | 20 +++----------------- 3 files changed, 3 insertions(+), 33 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 1d1d53f85ddd..761749562165 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3130,10 +3130,6 @@ force Enable ASPM even on devices that claim not to support it. WARNING: Forcing ASPM on may cause system lockups. - pcie_hp= [PCIE] PCI Express Hotplug driver options: - nomsi Do not use MSI for PCI Express Native Hotplug (this - makes all PCIe ports use INTx for hotplug services). - pcie_ports= [PCIE] PCIe ports handling: auto Ask the BIOS whether or not to use native PCIe services associated with PCIe ports (PME, hot-plug, AER). Use diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index ed84e767085f..86368f9341d7 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -91,18 +91,6 @@ void pcie_port_bus_unregister(void); struct pci_dev; -#ifdef CONFIG_HOTPLUG_PCI_PCIE -extern bool pciehp_msi_disabled; - -static inline bool pciehp_no_msi(void) -{ - return pciehp_msi_disabled; -} - -#else /* !CONFIG_HOTPLUG_PCI_PCIE */ -static inline bool pciehp_no_msi(void) { return false; } -#endif /* !CONFIG_HOTPLUG_PCI_PCIE */ - #ifdef CONFIG_PCIE_PME extern bool pcie_pme_msi_disabled; diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 5c25761cd05e..6ed67cbf6148 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -20,17 +20,6 @@ #include "../pci.h" #include "portdrv.h" -bool pciehp_msi_disabled; - -static int __init pciehp_setup(char *str) -{ - if (!strncmp(str, "nomsi", 5)) - pciehp_msi_disabled = true; - - return 1; -} -__setup("pcie_hp=", pciehp_setup); - /** * release_pcie_device - free PCI Express port service device structure * @dev: Port service device to release @@ -168,16 +157,13 @@ static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask) irqs[i] = -1; /* - * If we support PME or hotplug, but we can't use MSI/MSI-X for - * them, we have to fall back to INTx or other interrupts, e.g., a - * system shared interrupt. + * If we support PME but can't use MSI/MSI-X for it, we have to + * fall back to INTx or other interrupts, e.g., a system shared + * interrupt. */ if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) goto legacy_irq; - if ((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi()) - goto legacy_irq; - /* Try to use MSI-X or MSI if supported */ if (pcie_port_enable_irq_vec(dev, irqs, mask) == 0) return 0; -- cgit v1.2.3 From 4c0fd7648d880d98add62552cffdf993bde65cf8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:21:28 -0600 Subject: PCI/portdrv: Remove unnecessary "pcie_ports=auto" parameter The "pcie_ports=auto" parameter set pcie_ports_disabled and pcie_ports_auto to their compiled-in defaults, so specifying the parameter is the same as not using it at all. Remove the "pcie_ports=auto" parameter and update the documentation. Signed-off-by: Bjorn Helgaas --- Documentation/admin-guide/kernel-parameters.txt | 15 +++++++-------- drivers/pci/pcie/portdrv_pci.c | 3 --- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 761749562165..26565794a573 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3130,14 +3130,13 @@ force Enable ASPM even on devices that claim not to support it. WARNING: Forcing ASPM on may cause system lockups. - pcie_ports= [PCIE] PCIe ports handling: - auto Ask the BIOS whether or not to use native PCIe services - associated with PCIe ports (PME, hot-plug, AER). Use - them only if that is allowed by the BIOS. - native Use native PCIe services associated with PCIe ports - unconditionally. - compat Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe - ports driver. + pcie_ports= [PCIE] PCIe port services handling: + native Use native PCIe services (PME, AER, DPC, PCIe hotplug) + even if the platform doesn't give the OS permission to + use them. This may cause conflicts if the platform + also tries to use these services. + compat Disable native PCIe services (PME, AER, DPC, PCIe + hotplug). pcie_port_pm= [PCIE] PCIe port power management handling: off Disable power management of all PCIe ports diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 1997d9f2743e..8b62192342ac 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -37,9 +37,6 @@ static int __init pcie_port_setup(char *str) } else if (!strncmp(str, "native", 6)) { pcie_ports_disabled = false; pcie_ports_auto = false; - } else if (!strncmp(str, "auto", 4)) { - pcie_ports_disabled = false; - pcie_ports_auto = true; } return 1; -- cgit v1.2.3 From 842b447f0074b93e9f7db60039fdc72ec14bef9a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:21:29 -0600 Subject: PCI/portdrv: Encapsulate pcie_ports_auto inside the port driver "pcie_ports_auto" is only used inside the PCIe port driver itself, so move it from include/linux/pci.h to portdrv.h so it's not visible to the whole kernel. Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv.h | 2 ++ include/linux/pci.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 86368f9341d7..62e28b5afa51 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -12,6 +12,8 @@ #include +extern bool pcie_ports_auto; + /* Service Type */ #define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */ #define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT) diff --git a/include/linux/pci.h b/include/linux/pci.h index a04b7abc6b7a..dc70a3ce8dc5 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1449,10 +1449,8 @@ static inline int pci_irqd_intx_xlate(struct irq_domain *d, #ifdef CONFIG_PCIEPORTBUS extern bool pcie_ports_disabled; -extern bool pcie_ports_auto; #else #define pcie_ports_disabled true -#define pcie_ports_auto false #endif #ifdef CONFIG_PCIEASPM -- cgit v1.2.3 From d850882b726f6db01b0792151e72e69b234aa461 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:21:30 -0600 Subject: PCI/portdrv: Rename and reverse sense of pcie_ports_auto The platform may restrict the OS's use of PCIe services, e.g., via the ACPI _OSC method. The user may use "pcie_ports=native" to force the port driver to use PCIe services even if the platform asked us not to. The "pcie_ports=native" parameter determines the setting of pcie_ports_auto. Rename this to pcie_ports_native and reverse the sense to simplify the code. Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv.h | 2 +- drivers/pci/pcie/portdrv_core.c | 15 ++++----------- drivers/pci/pcie/portdrv_pci.c | 15 +++++++-------- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 62e28b5afa51..3e0058a5500f 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -12,7 +12,7 @@ #include -extern bool pcie_ports_auto; +extern bool pcie_ports_native; /* Service Type */ #define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */ diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 6ed67cbf6148..6890aea4a550 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -193,17 +193,10 @@ legacy_irq: static int get_port_device_capability(struct pci_dev *dev) { struct pci_host_bridge *host = pci_find_host_bridge(dev->bus); - bool native; int services = 0; - /* - * If the user specified "pcie_ports=native", use the PCIe services - * regardless of whether the platform has given us permission. On - * ACPI systems, this means we ignore _OSC. - */ - native = !pcie_ports_auto; - - if (dev->is_hotplug_bridge && (native || host->native_hotplug)) { + if (dev->is_hotplug_bridge && + (pcie_ports_native || host->native_hotplug)) { services |= PCIE_PORT_SERVICE_HP; /* @@ -215,7 +208,7 @@ static int get_port_device_capability(struct pci_dev *dev) } if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR) && - pci_aer_available() && (native || host->native_aer)) { + pci_aer_available() && (pcie_ports_native || host->native_aer)) { services |= PCIE_PORT_SERVICE_AER; /* @@ -231,7 +224,7 @@ static int get_port_device_capability(struct pci_dev *dev) * those yet. */ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT && - (native || host->native_pme)) { + (pcie_ports_native || host->native_pme)) { services |= PCIE_PORT_SERVICE_PME; /* diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 8b62192342ac..8e4260d25941 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -25,19 +25,18 @@ bool pcie_ports_disabled; /* - * If this switch is set, ACPI _OSC will be used to determine whether or not to - * enable PCIe port native services. + * If the user specified "pcie_ports=native", use the PCIe services regardless + * of whether the platform has given us permission. On ACPI systems, this + * means we ignore _OSC. */ -bool pcie_ports_auto = true; +bool pcie_ports_native; static int __init pcie_port_setup(char *str) { - if (!strncmp(str, "compat", 6)) { + if (!strncmp(str, "compat", 6)) pcie_ports_disabled = true; - } else if (!strncmp(str, "native", 6)) { - pcie_ports_disabled = false; - pcie_ports_auto = false; - } + else if (!strncmp(str, "native", 6)) + pcie_ports_native = true; return 1; } -- cgit v1.2.3 From f0553ba08a19031726f120448eedc3aff1599c23 Mon Sep 17 00:00:00 2001 From: Frederick Lawler Date: Thu, 22 Mar 2018 16:20:55 -0500 Subject: PCI/AER: Use cached AER Capability offset Replace pci_find_ext_capability(..., PCI_EXT_CAP_ID_ERR) calls with pci_dev->aer_cap. pci_dev->aer_cap is initialized in pci_init_capabilities(), which happens before any of these users of the AER Capability. Signed-off-by: Frederick Lawler Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer/aer_inject.c | 4 ++-- drivers/pci/pcie/aer/ecrc.c | 4 ++-- drivers/pci/pcie/portdrv_core.c | 14 ++++++++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index 25e1feb962c5..a49090935303 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -344,7 +344,7 @@ static int aer_inject(struct aer_error_inj *einj) goto out_put; } - pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos_cap_err = dev->aer_cap; if (!pos_cap_err) { pci_err(dev, "aer_inject: Device doesn't support AER\n"); ret = -EPROTONOSUPPORT; @@ -355,7 +355,7 @@ static int aer_inject(struct aer_error_inj *einj) pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK, &uncor_mask); - rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR); + rp_pos_cap_err = rpdev->aer_cap; if (!rp_pos_cap_err) { pci_err(rpdev, "aer_inject: Root port doesn't support AER\n"); ret = -EPROTONOSUPPORT; diff --git a/drivers/pci/pcie/aer/ecrc.c b/drivers/pci/pcie/aer/ecrc.c index 26d3cac9e635..afb5f761f5d4 100644 --- a/drivers/pci/pcie/aer/ecrc.c +++ b/drivers/pci/pcie/aer/ecrc.c @@ -40,7 +40,7 @@ static int enable_ecrc_checking(struct pci_dev *dev) if (!pci_is_pcie(dev)) return -ENODEV; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; if (!pos) return -ENODEV; @@ -68,7 +68,7 @@ static int disable_ecrc_checking(struct pci_dev *dev) if (!pci_is_pcie(dev)) return -ENODEV; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; if (!pos) return -ENODEV; diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 6890aea4a550..099ef7ac615b 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -40,7 +40,7 @@ static void release_pcie_device(struct device *dev) static int pcie_message_numbers(struct pci_dev *dev, int mask, u32 *pme, u32 *aer, u32 *dpc) { - u32 nvec = 0, pos, reg32; + u32 nvec = 0, pos; u16 reg16; /* @@ -56,8 +56,11 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask, nvec = *pme + 1; } +#ifdef CONFIG_PCIEAER if (mask & PCIE_PORT_SERVICE_AER) { - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + u32 reg32; + + pos = dev->aer_cap; if (pos) { pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, ®32); @@ -65,6 +68,7 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask, nvec = max(nvec, *aer + 1); } } +#endif if (mask & PCIE_PORT_SERVICE_DPC) { pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC); @@ -207,8 +211,9 @@ static int get_port_device_capability(struct pci_dev *dev) PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE); } - if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR) && - pci_aer_available() && (pcie_ports_native || host->native_aer)) { +#ifdef CONFIG_PCIEAER + if (dev->aer_cap && pci_aer_available() && + (pcie_ports_native || host->native_aer)) { services |= PCIE_PORT_SERVICE_AER; /* @@ -217,6 +222,7 @@ static int get_port_device_capability(struct pci_dev *dev) */ pci_disable_pcie_error_reporting(dev); } +#endif /* * Root ports are capable of generating PME too. Root Complex -- cgit v1.2.3 From 4e5fad429bd179a41fa8b222463397e8cc806cd1 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 27 Mar 2018 13:48:35 +0300 Subject: PCI/DPC: Do not enable DPC if AER control is not allowed by the BIOS Commit eed85ff4c0da ("PCI/DPC: Enable DPC only if AER is available") made DPC control dependent whether AER is enabled in the OS. However, it does not take into account situations where BIOS has not given OS control of AER: acpi PNP0A08:00: _OSC: OS supports [ExtendedConfig ASPM ClockPM Segments MSI] acpi PNP0A08:00: _OSC: platform does not support [AER] acpi PNP0A08:00: _OSC: OS now controls [PCIeHotplug PME PCIeCapability] I think here it is better not to enable DPC even if the capability is available because then it would be against what "Determination of DPC Control" note in PCIe 4.0 sec 6.1.10 recommends. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_acpi.c | 4 ++-- drivers/pci/pcie/portdrv_core.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c index 9d12650dc2ae..8ab5d434b9c6 100644 --- a/drivers/pci/pcie/portdrv_acpi.c +++ b/drivers/pci/pcie/portdrv_acpi.c @@ -47,11 +47,11 @@ void pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask) flags = root->osc_control_set; - *srv_mask = PCIE_PORT_SERVICE_DPC; + *srv_mask = 0; if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL) *srv_mask |= PCIE_PORT_SERVICE_HP; if (flags & OSC_PCI_EXPRESS_PME_CONTROL) *srv_mask |= PCIE_PORT_SERVICE_PME; if (flags & OSC_PCI_EXPRESS_AER_CONTROL) - *srv_mask |= PCIE_PORT_SERVICE_AER; + *srv_mask |= PCIE_PORT_SERVICE_AER | PCIE_PORT_SERVICE_DPC; } diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 099ef7ac615b..4ba4d05a5e4c 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -242,7 +242,7 @@ static int get_port_device_capability(struct pci_dev *dev) } if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) && - pci_aer_available()) + pci_aer_available() && services & PCIE_PORT_SERVICE_AER) services |= PCIE_PORT_SERVICE_DPC; return services; -- cgit v1.2.3 From ad32eb2df801548a4b55802384fbbfbc04d76bfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Sun, 18 Mar 2018 13:58:06 +0100 Subject: PCI: Always define the of_node helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simply move these inline functions outside the ifdef instead of duplicating them as stubs in the !OF case. The struct device of_node field does not depend on OF. This also fixes the missing stubbed pci_bus_to_OF_node(). Signed-off-by: Bjørn Mork Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/include/linux/pci.h b/include/linux/pci.h index 024a1beda008..d0396da9160e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2182,24 +2182,11 @@ int pci_parse_request_of_pci_ranges(struct device *dev, /* Arch may override this (weak) */ struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus); -static inline struct device_node * -pci_device_to_OF_node(const struct pci_dev *pdev) -{ - return pdev ? pdev->dev.of_node : NULL; -} - -static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) -{ - return bus ? bus->dev.of_node : NULL; -} - #else /* CONFIG_OF */ 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) { } -static inline struct device_node * -pci_device_to_OF_node(const struct pci_dev *pdev) { return NULL; } static inline struct irq_domain * pci_host_bridge_of_msi_domain(struct pci_bus *bus) { return NULL; } static inline int pci_parse_request_of_pci_ranges(struct device *dev, @@ -2210,6 +2197,17 @@ static inline int pci_parse_request_of_pci_ranges(struct device *dev, } #endif /* CONFIG_OF */ +static inline struct device_node * +pci_device_to_OF_node(const struct pci_dev *pdev) +{ + return pdev ? pdev->dev.of_node : NULL; +} + +static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) +{ + return bus ? bus->dev.of_node : NULL; +} + #ifdef CONFIG_ACPI struct irq_domain *pci_host_bridge_acpi_msi_domain(struct pci_bus *bus); -- cgit v1.2.3 From cf0921bea66c55600a48009597caa5fcb1419748 Mon Sep 17 00:00:00 2001 From: KarimAllah Ahmed Date: Mon, 19 Mar 2018 21:06:00 +0100 Subject: PCI/IOV: Use VF0 cached config registers for other VFs Cache some config data from VF0 and use it for all other VFs instead of reading it from the config space of each VF. We assume these items are the same across all associated VFs: Revision ID Class Code Subsystem Vendor ID Subsystem ID This is an optimization when enabling SR-IOV on a device with many VFs. Signed-off-by: KarimAllah Ahmed [bhelgaas: changelog, simplify comments, remove unused "device", test CONFIG_PCI_IOV instead of CONFIG_PCI_ATS, rename functions] Signed-off-by: Bjorn Helgaas --- drivers/pci/iov.c | 42 +++++++++++++++++++++++++++++++++++------- drivers/pci/pci.h | 4 ++++ drivers/pci/probe.c | 47 ++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 81 insertions(+), 12 deletions(-) diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 677924ae0350..30bf8f706ed9 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -114,6 +114,29 @@ resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno) return dev->sriov->barsz[resno - PCI_IOV_RESOURCES]; } +static void pci_read_vf_config_common(struct pci_dev *virtfn) +{ + struct pci_dev *physfn = virtfn->physfn; + + /* + * Some config registers are the same across all associated VFs. + * Read them once from VF0 so we can skip reading them from the + * other VFs. + * + * PCIe r4.0, sec 9.3.4.1, technically doesn't require all VFs to + * have the same Revision ID and Subsystem ID, but we assume they + * do. + */ + pci_read_config_dword(virtfn, PCI_CLASS_REVISION, + &physfn->sriov->class); + pci_read_config_byte(virtfn, PCI_HEADER_TYPE, + &physfn->sriov->hdr_type); + pci_read_config_word(virtfn, PCI_SUBSYSTEM_VENDOR_ID, + &physfn->sriov->subsystem_vendor); + pci_read_config_word(virtfn, PCI_SUBSYSTEM_ID, + &physfn->sriov->subsystem_device); +} + int pci_iov_add_virtfn(struct pci_dev *dev, int id) { int i; @@ -136,13 +159,17 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id) virtfn->devfn = pci_iov_virtfn_devfn(dev, id); virtfn->vendor = dev->vendor; virtfn->device = iov->vf_device; + virtfn->is_virtfn = 1; + virtfn->physfn = pci_dev_get(dev); + + if (id == 0) + pci_read_vf_config_common(virtfn); + rc = pci_setup_device(virtfn); if (rc) - goto failed0; + goto failed1; virtfn->dev.parent = dev->dev.parent; - virtfn->physfn = pci_dev_get(dev); - virtfn->is_virtfn = 1; virtfn->multifunction = 0; for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { @@ -163,10 +190,10 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id) sprintf(buf, "virtfn%u", id); rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf); if (rc) - goto failed1; + goto failed2; rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn"); if (rc) - goto failed2; + goto failed3; kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE); @@ -174,11 +201,12 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id) return 0; -failed2: +failed3: sysfs_remove_link(&dev->dev.kobj, buf); +failed2: + pci_stop_and_remove_bus_device(virtfn); failed1: pci_dev_put(dev); - pci_stop_and_remove_bus_device(virtfn); failed0: virtfn_remove_bus(dev->bus, bus); failed: diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index fcd81911b127..bdb4ba2d5f95 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -271,6 +271,10 @@ struct pci_sriov { u16 driver_max_VFs; /* Max num VFs driver supports */ struct pci_dev *dev; /* Lowest numbered PF */ struct pci_dev *self; /* This PF */ + u32 class; /* VF device */ + u8 hdr_type; /* VF header type */ + u16 subsystem_vendor; /* VF subsystem vendor */ + u16 subsystem_device; /* VF subsystem device */ resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */ bool drivers_autoprobe; /* Auto probing of VFs by driver */ }; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 9f80b904bf76..708094f720fd 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1400,6 +1400,43 @@ int pci_cfg_space_size(struct pci_dev *dev) return PCI_CFG_SPACE_SIZE; } +static u32 pci_class(struct pci_dev *dev) +{ + u32 class; + +#ifdef CONFIG_PCI_IOV + if (dev->is_virtfn) + return dev->physfn->sriov->class; +#endif + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); + return class; +} + +static void pci_subsystem_ids(struct pci_dev *dev, u16 *vendor, u16 *device) +{ +#ifdef CONFIG_PCI_IOV + if (dev->is_virtfn) { + *vendor = dev->physfn->sriov->subsystem_vendor; + *device = dev->physfn->sriov->subsystem_device; + return; + } +#endif + pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, vendor); + pci_read_config_word(dev, PCI_SUBSYSTEM_ID, device); +} + +static u8 pci_hdr_type(struct pci_dev *dev) +{ + u8 hdr_type; + +#ifdef CONFIG_PCI_IOV + if (dev->is_virtfn) + return dev->physfn->sriov->hdr_type; +#endif + pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type); + return hdr_type; +} + #define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED) static void pci_msi_setup_pci_dev(struct pci_dev *dev) @@ -1465,8 +1502,7 @@ int pci_setup_device(struct pci_dev *dev) struct pci_bus_region region; struct resource *res; - if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type)) - return -EIO; + hdr_type = pci_hdr_type(dev); dev->sysdata = dev->bus->sysdata; dev->dev.parent = dev->bus->bridge; @@ -1488,7 +1524,8 @@ int pci_setup_device(struct pci_dev *dev) dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); + class = pci_class(dev); + dev->revision = class & 0xff; dev->class = class >> 8; /* upper 3 bytes */ @@ -1528,8 +1565,8 @@ int pci_setup_device(struct pci_dev *dev) goto bad; pci_read_irq(dev); pci_read_bases(dev, 6, PCI_ROM_ADDRESS); - pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor); - pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device); + + pci_subsystem_ids(dev, &dev->subsystem_vendor, &dev->subsystem_device); /* * Do the ugly legacy mode stuff here rather than broken chip -- cgit v1.2.3 From 619e6f340cec7c5d1449a2951dae5af0990bd0f5 Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Fri, 30 Mar 2018 17:39:31 -0500 Subject: PCI/IOV: Add missing prototypes for powerpc pcibios interfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add missing prototypes for: resource_size_t pcibios_default_alignment(void); int pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs); int pcibios_sriov_disable(struct pci_dev *pdev); This fixes the following warnings treated as errors when using W=1: arch/powerpc/kernel/pci-common.c:236:17: error: no previous prototype for ‘pcibios_default_alignment’ [-Werror=missing-prototypes] arch/powerpc/kernel/pci-common.c:253:5: error: no previous prototype for ‘pcibios_sriov_enable’ [-Werror=missing-prototypes] arch/powerpc/kernel/pci-common.c:261:5: error: no previous prototype for ‘pcibios_sriov_disable’ [-Werror=missing-prototypes] Also, commit 978d2d683123 ("PCI: Add pcibios_iov_resource_alignment() interface") added a new function but the prototype was located in the main header instead of the CONFIG_PCI_IOV specific section. Move this function next to the newly added ones. Signed-off-by: Mathieu Malaterre Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/linux/pci.h b/include/linux/pci.h index 562875d34b98..df17288fc1f6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1295,7 +1295,6 @@ unsigned char pci_bus_max_busnr(struct pci_bus *bus); void pci_setup_bridge(struct pci_bus *bus); resource_size_t pcibios_window_alignment(struct pci_bus *bus, unsigned long type); -resource_size_t pcibios_iov_resource_alignment(struct pci_dev *dev, int resno); #define PCI_VGA_STATE_CHANGE_BRIDGE (1 << 0) #define PCI_VGA_STATE_CHANGE_DECODES (1 << 1) @@ -1923,6 +1922,7 @@ void pcibios_release_device(struct pci_dev *dev); void pcibios_penalize_isa_irq(int irq, int active); int pcibios_alloc_irq(struct pci_dev *dev); void pcibios_free_irq(struct pci_dev *dev); +resource_size_t pcibios_default_alignment(void); #ifdef CONFIG_HIBERNATE_CALLBACKS extern struct dev_pm_ops pcibios_pm_ops; @@ -1955,6 +1955,11 @@ int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs); int pci_sriov_get_totalvfs(struct pci_dev *dev); resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno); void pci_vf_drivers_autoprobe(struct pci_dev *dev, bool probe); + +/* Arch may override these (weak) */ +int pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs); +int pcibios_sriov_disable(struct pci_dev *pdev); +resource_size_t pcibios_iov_resource_alignment(struct pci_dev *dev, int resno); #else static inline int pci_iov_virtfn_bus(struct pci_dev *dev, int id) { -- cgit v1.2.3 From e02602bd76257e0368e4c3d4ce11a7ac86df72d2 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 31 Mar 2018 17:48:57 -0500 Subject: PCI/DPC: Rename from pcie-dpc.c to dpc.c Rename pcie-dpc.c to dpc.c. The path "drivers/pci/pcie/pcie-dpc.c" has more occurrences of "pci" than necessary. Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/Makefile | 2 +- drivers/pci/pcie/dpc.c | 307 ++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pcie/pcie-dpc.c | 307 -------------------------------------------- 3 files changed, 308 insertions(+), 308 deletions(-) create mode 100644 drivers/pci/pcie/dpc.c delete mode 100644 drivers/pci/pcie/pcie-dpc.c diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile index 11fb633b866c..30557a567ee7 100644 --- a/drivers/pci/pcie/Makefile +++ b/drivers/pci/pcie/Makefile @@ -15,5 +15,5 @@ obj-$(CONFIG_PCIEAER) += aer/ obj-$(CONFIG_PCIE_PME) += pme.o -obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o +obj-$(CONFIG_PCIE_DPC) += dpc.o obj-$(CONFIG_PCIE_PTM) += ptm.o diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c new file mode 100644 index 000000000000..8c57d607e603 --- /dev/null +++ b/drivers/pci/pcie/dpc.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCI Express Downstream Port Containment services driver + * Author: Keith Busch + * + * Copyright (C) 2016 Intel Corp. + */ + +#include +#include +#include +#include + +#include "portdrv.h" +#include "../pci.h" +#include "aer/aerdrv.h" + +struct dpc_dev { + struct pcie_device *dev; + struct work_struct work; + u16 cap_pos; + bool rp_extensions; + u32 rp_pio_status; + u8 rp_log_size; +}; + +static const char * const rp_pio_error_string[] = { + "Configuration Request received UR Completion", /* Bit Position 0 */ + "Configuration Request received CA Completion", /* Bit Position 1 */ + "Configuration Request Completion Timeout", /* Bit Position 2 */ + NULL, + NULL, + NULL, + NULL, + NULL, + "I/O Request received UR Completion", /* Bit Position 8 */ + "I/O Request received CA Completion", /* Bit Position 9 */ + "I/O Request Completion Timeout", /* Bit Position 10 */ + NULL, + NULL, + NULL, + NULL, + NULL, + "Memory Request received UR Completion", /* Bit Position 16 */ + "Memory Request received CA Completion", /* Bit Position 17 */ + "Memory Request Completion Timeout", /* Bit Position 18 */ +}; + +static int dpc_wait_rp_inactive(struct dpc_dev *dpc) +{ + unsigned long timeout = jiffies + HZ; + struct pci_dev *pdev = dpc->dev->port; + struct device *dev = &dpc->dev->device; + u16 cap = dpc->cap_pos, status; + + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); + while (status & PCI_EXP_DPC_RP_BUSY && + !time_after(jiffies, timeout)) { + msleep(10); + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); + } + if (status & PCI_EXP_DPC_RP_BUSY) { + dev_warn(dev, "DPC root port still busy\n"); + return -EBUSY; + } + return 0; +} + +static void dpc_wait_link_inactive(struct dpc_dev *dpc) +{ + unsigned long timeout = jiffies + HZ; + struct pci_dev *pdev = dpc->dev->port; + struct device *dev = &dpc->dev->device; + u16 lnk_status; + + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); + while (lnk_status & PCI_EXP_LNKSTA_DLLLA && + !time_after(jiffies, timeout)) { + msleep(10); + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); + } + if (lnk_status & PCI_EXP_LNKSTA_DLLLA) + dev_warn(dev, "Link state not disabled for DPC event\n"); +} + +static void dpc_work(struct work_struct *work) +{ + struct dpc_dev *dpc = container_of(work, struct dpc_dev, work); + struct pci_dev *dev, *temp, *pdev = dpc->dev->port; + struct pci_bus *parent = pdev->subordinate; + u16 cap = dpc->cap_pos, ctl; + + pci_lock_rescan_remove(); + list_for_each_entry_safe_reverse(dev, temp, &parent->devices, + bus_list) { + pci_dev_get(dev); + pci_dev_set_disconnected(dev, NULL); + if (pci_has_subordinate(dev)) + pci_walk_bus(dev->subordinate, + pci_dev_set_disconnected, NULL); + pci_stop_and_remove_bus_device(dev); + pci_dev_put(dev); + } + pci_unlock_rescan_remove(); + + dpc_wait_link_inactive(dpc); + if (dpc->rp_extensions && dpc_wait_rp_inactive(dpc)) + return; + if (dpc->rp_extensions && dpc->rp_pio_status) { + pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, + dpc->rp_pio_status); + dpc->rp_pio_status = 0; + } + + pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS, + PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT); + + pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl); + pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL, + ctl | PCI_EXP_DPC_CTL_INT_EN); +} + +static void dpc_process_rp_pio_error(struct dpc_dev *dpc) +{ + struct device *dev = &dpc->dev->device; + struct pci_dev *pdev = dpc->dev->port; + u16 cap = dpc->cap_pos, dpc_status, first_error; + u32 status, mask, sev, syserr, exc, dw0, dw1, dw2, dw3, log, prefix; + int i; + + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, &status); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_MASK, &mask); + dev_err(dev, "rp_pio_status: %#010x, rp_pio_mask: %#010x\n", + status, mask); + + dpc->rp_pio_status = status; + + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SEVERITY, &sev); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SYSERROR, &syserr); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_EXCEPTION, &exc); + dev_err(dev, "RP PIO severity=%#010x, syserror=%#010x, exception=%#010x\n", + sev, syserr, exc); + + /* Get First Error Pointer */ + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &dpc_status); + first_error = (dpc_status & 0x1f00) >> 8; + + status &= ~mask; + for (i = 0; i < ARRAY_SIZE(rp_pio_error_string); i++) { + if (status & (1 << i)) + dev_err(dev, "[%2d] %s%s\n", i, rp_pio_error_string[i], + first_error == i ? " (First)" : ""); + } + + if (dpc->rp_log_size < 4) + return; + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG, + &dw0); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 4, + &dw1); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 8, + &dw2); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 12, + &dw3); + dev_err(dev, "TLP Header: %#010x %#010x %#010x %#010x\n", + dw0, dw1, dw2, dw3); + + if (dpc->rp_log_size < 5) + return; + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG, &log); + dev_err(dev, "RP PIO ImpSpec Log %#010x\n", log); + + for (i = 0; i < dpc->rp_log_size - 5; i++) { + pci_read_config_dword(pdev, + cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG, &prefix); + dev_err(dev, "TLP Prefix Header: dw%d, %#010x\n", i, prefix); + } +} + +static irqreturn_t dpc_irq(int irq, void *context) +{ + struct dpc_dev *dpc = (struct dpc_dev *)context; + struct pci_dev *pdev = dpc->dev->port; + struct device *dev = &dpc->dev->device; + u16 cap = dpc->cap_pos, ctl, status, source, reason, ext_reason; + + pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl); + + if (!(ctl & PCI_EXP_DPC_CTL_INT_EN) || ctl == (u16)(~0)) + return IRQ_NONE; + + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); + + if (!(status & PCI_EXP_DPC_STATUS_INTERRUPT)) + return IRQ_NONE; + + if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) { + pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS, + PCI_EXP_DPC_STATUS_INTERRUPT); + return IRQ_HANDLED; + } + + pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL, + ctl & ~PCI_EXP_DPC_CTL_INT_EN); + + pci_read_config_word(pdev, cap + PCI_EXP_DPC_SOURCE_ID, + &source); + + dev_info(dev, "DPC containment event, status:%#06x source:%#06x\n", + status, source); + + reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1; + ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5; + + dev_warn(dev, "DPC %s detected, remove downstream devices\n", + (reason == 0) ? "unmasked uncorrectable error" : + (reason == 1) ? "ERR_NONFATAL" : + (reason == 2) ? "ERR_FATAL" : + (ext_reason == 0) ? "RP PIO error" : + (ext_reason == 1) ? "software trigger" : + "reserved error"); + /* show RP PIO error detail information */ + if (dpc->rp_extensions && reason == 3 && ext_reason == 0) + dpc_process_rp_pio_error(dpc); + + schedule_work(&dpc->work); + + return IRQ_HANDLED; +} + +#define FLAG(x, y) (((x) & (y)) ? '+' : '-') +static int dpc_probe(struct pcie_device *dev) +{ + struct dpc_dev *dpc; + struct pci_dev *pdev = dev->port; + struct device *device = &dev->device; + int status; + u16 ctl, cap; + + if (pcie_aer_get_firmware_first(pdev)) + return -ENOTSUPP; + + dpc = devm_kzalloc(device, sizeof(*dpc), GFP_KERNEL); + if (!dpc) + return -ENOMEM; + + dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC); + dpc->dev = dev; + INIT_WORK(&dpc->work, dpc_work); + set_service_data(dev, dpc); + + status = devm_request_irq(device, dev->irq, dpc_irq, IRQF_SHARED, + "pcie-dpc", dpc); + if (status) { + dev_warn(device, "request IRQ%d failed: %d\n", dev->irq, + status); + return status; + } + + pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap); + pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl); + + dpc->rp_extensions = (cap & PCI_EXP_DPC_CAP_RP_EXT); + if (dpc->rp_extensions) { + dpc->rp_log_size = (cap & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8; + if (dpc->rp_log_size < 4 || dpc->rp_log_size > 9) { + dev_err(device, "RP PIO log size %u is invalid\n", + dpc->rp_log_size); + dpc->rp_log_size = 0; + } + } + + ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN; + pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl); + + dev_info(device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n", + cap & PCI_EXP_DPC_IRQ, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT), + FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP), + FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size, + FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE)); + return status; +} + +static void dpc_remove(struct pcie_device *dev) +{ + struct dpc_dev *dpc = get_service_data(dev); + struct pci_dev *pdev = dev->port; + u16 ctl; + + pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl); + ctl &= ~(PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN); + pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl); +} + +static struct pcie_port_service_driver dpcdriver = { + .name = "dpc", + .port_type = PCIE_ANY_PORT, + .service = PCIE_PORT_SERVICE_DPC, + .probe = dpc_probe, + .remove = dpc_remove, +}; + +static int __init dpc_service_init(void) +{ + return pcie_port_service_register(&dpcdriver); +} +device_initcall(dpc_service_init); diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/pcie-dpc.c deleted file mode 100644 index 8c57d607e603..000000000000 --- a/drivers/pci/pcie/pcie-dpc.c +++ /dev/null @@ -1,307 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCI Express Downstream Port Containment services driver - * Author: Keith Busch - * - * Copyright (C) 2016 Intel Corp. - */ - -#include -#include -#include -#include - -#include "portdrv.h" -#include "../pci.h" -#include "aer/aerdrv.h" - -struct dpc_dev { - struct pcie_device *dev; - struct work_struct work; - u16 cap_pos; - bool rp_extensions; - u32 rp_pio_status; - u8 rp_log_size; -}; - -static const char * const rp_pio_error_string[] = { - "Configuration Request received UR Completion", /* Bit Position 0 */ - "Configuration Request received CA Completion", /* Bit Position 1 */ - "Configuration Request Completion Timeout", /* Bit Position 2 */ - NULL, - NULL, - NULL, - NULL, - NULL, - "I/O Request received UR Completion", /* Bit Position 8 */ - "I/O Request received CA Completion", /* Bit Position 9 */ - "I/O Request Completion Timeout", /* Bit Position 10 */ - NULL, - NULL, - NULL, - NULL, - NULL, - "Memory Request received UR Completion", /* Bit Position 16 */ - "Memory Request received CA Completion", /* Bit Position 17 */ - "Memory Request Completion Timeout", /* Bit Position 18 */ -}; - -static int dpc_wait_rp_inactive(struct dpc_dev *dpc) -{ - unsigned long timeout = jiffies + HZ; - struct pci_dev *pdev = dpc->dev->port; - struct device *dev = &dpc->dev->device; - u16 cap = dpc->cap_pos, status; - - pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); - while (status & PCI_EXP_DPC_RP_BUSY && - !time_after(jiffies, timeout)) { - msleep(10); - pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); - } - if (status & PCI_EXP_DPC_RP_BUSY) { - dev_warn(dev, "DPC root port still busy\n"); - return -EBUSY; - } - return 0; -} - -static void dpc_wait_link_inactive(struct dpc_dev *dpc) -{ - unsigned long timeout = jiffies + HZ; - struct pci_dev *pdev = dpc->dev->port; - struct device *dev = &dpc->dev->device; - u16 lnk_status; - - pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); - while (lnk_status & PCI_EXP_LNKSTA_DLLLA && - !time_after(jiffies, timeout)) { - msleep(10); - pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); - } - if (lnk_status & PCI_EXP_LNKSTA_DLLLA) - dev_warn(dev, "Link state not disabled for DPC event\n"); -} - -static void dpc_work(struct work_struct *work) -{ - struct dpc_dev *dpc = container_of(work, struct dpc_dev, work); - struct pci_dev *dev, *temp, *pdev = dpc->dev->port; - struct pci_bus *parent = pdev->subordinate; - u16 cap = dpc->cap_pos, ctl; - - pci_lock_rescan_remove(); - list_for_each_entry_safe_reverse(dev, temp, &parent->devices, - bus_list) { - pci_dev_get(dev); - pci_dev_set_disconnected(dev, NULL); - if (pci_has_subordinate(dev)) - pci_walk_bus(dev->subordinate, - pci_dev_set_disconnected, NULL); - pci_stop_and_remove_bus_device(dev); - pci_dev_put(dev); - } - pci_unlock_rescan_remove(); - - dpc_wait_link_inactive(dpc); - if (dpc->rp_extensions && dpc_wait_rp_inactive(dpc)) - return; - if (dpc->rp_extensions && dpc->rp_pio_status) { - pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, - dpc->rp_pio_status); - dpc->rp_pio_status = 0; - } - - pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS, - PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT); - - pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl); - pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL, - ctl | PCI_EXP_DPC_CTL_INT_EN); -} - -static void dpc_process_rp_pio_error(struct dpc_dev *dpc) -{ - struct device *dev = &dpc->dev->device; - struct pci_dev *pdev = dpc->dev->port; - u16 cap = dpc->cap_pos, dpc_status, first_error; - u32 status, mask, sev, syserr, exc, dw0, dw1, dw2, dw3, log, prefix; - int i; - - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, &status); - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_MASK, &mask); - dev_err(dev, "rp_pio_status: %#010x, rp_pio_mask: %#010x\n", - status, mask); - - dpc->rp_pio_status = status; - - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SEVERITY, &sev); - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SYSERROR, &syserr); - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_EXCEPTION, &exc); - dev_err(dev, "RP PIO severity=%#010x, syserror=%#010x, exception=%#010x\n", - sev, syserr, exc); - - /* Get First Error Pointer */ - pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &dpc_status); - first_error = (dpc_status & 0x1f00) >> 8; - - status &= ~mask; - for (i = 0; i < ARRAY_SIZE(rp_pio_error_string); i++) { - if (status & (1 << i)) - dev_err(dev, "[%2d] %s%s\n", i, rp_pio_error_string[i], - first_error == i ? " (First)" : ""); - } - - if (dpc->rp_log_size < 4) - return; - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG, - &dw0); - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 4, - &dw1); - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 8, - &dw2); - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 12, - &dw3); - dev_err(dev, "TLP Header: %#010x %#010x %#010x %#010x\n", - dw0, dw1, dw2, dw3); - - if (dpc->rp_log_size < 5) - return; - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG, &log); - dev_err(dev, "RP PIO ImpSpec Log %#010x\n", log); - - for (i = 0; i < dpc->rp_log_size - 5; i++) { - pci_read_config_dword(pdev, - cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG, &prefix); - dev_err(dev, "TLP Prefix Header: dw%d, %#010x\n", i, prefix); - } -} - -static irqreturn_t dpc_irq(int irq, void *context) -{ - struct dpc_dev *dpc = (struct dpc_dev *)context; - struct pci_dev *pdev = dpc->dev->port; - struct device *dev = &dpc->dev->device; - u16 cap = dpc->cap_pos, ctl, status, source, reason, ext_reason; - - pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl); - - if (!(ctl & PCI_EXP_DPC_CTL_INT_EN) || ctl == (u16)(~0)) - return IRQ_NONE; - - pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); - - if (!(status & PCI_EXP_DPC_STATUS_INTERRUPT)) - return IRQ_NONE; - - if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) { - pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS, - PCI_EXP_DPC_STATUS_INTERRUPT); - return IRQ_HANDLED; - } - - pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL, - ctl & ~PCI_EXP_DPC_CTL_INT_EN); - - pci_read_config_word(pdev, cap + PCI_EXP_DPC_SOURCE_ID, - &source); - - dev_info(dev, "DPC containment event, status:%#06x source:%#06x\n", - status, source); - - reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1; - ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5; - - dev_warn(dev, "DPC %s detected, remove downstream devices\n", - (reason == 0) ? "unmasked uncorrectable error" : - (reason == 1) ? "ERR_NONFATAL" : - (reason == 2) ? "ERR_FATAL" : - (ext_reason == 0) ? "RP PIO error" : - (ext_reason == 1) ? "software trigger" : - "reserved error"); - /* show RP PIO error detail information */ - if (dpc->rp_extensions && reason == 3 && ext_reason == 0) - dpc_process_rp_pio_error(dpc); - - schedule_work(&dpc->work); - - return IRQ_HANDLED; -} - -#define FLAG(x, y) (((x) & (y)) ? '+' : '-') -static int dpc_probe(struct pcie_device *dev) -{ - struct dpc_dev *dpc; - struct pci_dev *pdev = dev->port; - struct device *device = &dev->device; - int status; - u16 ctl, cap; - - if (pcie_aer_get_firmware_first(pdev)) - return -ENOTSUPP; - - dpc = devm_kzalloc(device, sizeof(*dpc), GFP_KERNEL); - if (!dpc) - return -ENOMEM; - - dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC); - dpc->dev = dev; - INIT_WORK(&dpc->work, dpc_work); - set_service_data(dev, dpc); - - status = devm_request_irq(device, dev->irq, dpc_irq, IRQF_SHARED, - "pcie-dpc", dpc); - if (status) { - dev_warn(device, "request IRQ%d failed: %d\n", dev->irq, - status); - return status; - } - - pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap); - pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl); - - dpc->rp_extensions = (cap & PCI_EXP_DPC_CAP_RP_EXT); - if (dpc->rp_extensions) { - dpc->rp_log_size = (cap & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8; - if (dpc->rp_log_size < 4 || dpc->rp_log_size > 9) { - dev_err(device, "RP PIO log size %u is invalid\n", - dpc->rp_log_size); - dpc->rp_log_size = 0; - } - } - - ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN; - pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl); - - dev_info(device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n", - cap & PCI_EXP_DPC_IRQ, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT), - FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP), - FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size, - FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE)); - return status; -} - -static void dpc_remove(struct pcie_device *dev) -{ - struct dpc_dev *dpc = get_service_data(dev); - struct pci_dev *pdev = dev->port; - u16 ctl; - - pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl); - ctl &= ~(PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN); - pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl); -} - -static struct pcie_port_service_driver dpcdriver = { - .name = "dpc", - .port_type = PCIE_ANY_PORT, - .service = PCIE_PORT_SERVICE_DPC, - .probe = dpc_probe, - .remove = dpc_remove, -}; - -static int __init dpc_service_init(void) -{ - return pcie_port_service_register(&dpcdriver); -} -device_initcall(dpc_service_init); -- cgit v1.2.3 From bf597574b6bee5ac741c58da763ad2f10df19065 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:06 +0200 Subject: PCI: endpoint: BAR width should not depend on sizeof dma_addr_t If a BAR supports 64-bit width or not depends on the hardware, and should thus not depend on sizeof(dma_addr_t). If a certain hardware doesn't support 64-bit BARs, its epc->ops->set_bar() implementation should return -EINVAL when PCI_BASE_ADDRESS_MEM_TYPE_64 is set. We can't change pci_epc_set_bar() to only set PCI_BASE_ADDRESS_MEM_TYPE_64 based on size, since if the user, for some reason, wants to configure a BAR with a 64-bit width, even though the BAR size is less than 4 GB, he should be able to do that. However, since pci-epf-test is simply a test and not an API, we can set PCI_BASE_ADDRESS_MEM_TYPE_64 in pci-epf-test itself only based on size. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/functions/pci-epf-test.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 64d8a17f8094..f6c0c59b1bc8 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -70,7 +70,7 @@ struct pci_epf_test_data { bool linkup_notifier; }; -static int bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 }; +static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 }; static int pci_epf_test_copy(struct pci_epf_test *epf_test) { @@ -367,12 +367,14 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) struct pci_epf_test *epf_test = epf_get_drvdata(epf); enum pci_barno test_reg_bar = epf_test->test_reg_bar; - flags = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32; - if (sizeof(dma_addr_t) == 0x8) - flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; - for (bar = BAR_0; bar <= BAR_5; bar++) { epf_bar = &epf->bar[bar]; + + flags = PCI_BASE_ADDRESS_SPACE_MEMORY; + flags |= upper_32_bits(epf_bar->size) ? + PCI_BASE_ADDRESS_MEM_TYPE_64 : + PCI_BASE_ADDRESS_MEM_TYPE_32; + ret = pci_epc_set_bar(epc, epf->func_no, bar, epf_bar->phys_addr, epf_bar->size, flags); -- cgit v1.2.3 From bc4a48976f57bc88319bfa32690bcc4b6cef4a29 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:07 +0200 Subject: PCI: endpoint: Simplify epc->ops->set_bar()/pci_epc_set_bar() Add barno and flags to struct epf_bar. That way we can simplify epc->ops->set_bar()/pci_epc_set_bar() by passing a struct *epf_bar instead of a whole lot of arguments. This is needed so that epc->ops->set_bar() implementations can modify BAR flags. Will be utilized in a succeeding patch. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Reviewed-by: Gustavo Pimentel Acked-by: Kishon Vijay Abraham I --- drivers/pci/cadence/pcie-cadence-ep.c | 9 ++++++--- drivers/pci/dwc/pcie-designware-ep.c | 8 +++++--- drivers/pci/endpoint/functions/pci-epf-test.c | 8 ++------ drivers/pci/endpoint/pci-epc-core.c | 10 ++++------ drivers/pci/endpoint/pci-epf-core.c | 4 ++++ include/linux/pci-epc.h | 6 ++---- include/linux/pci-epf.h | 2 ++ 7 files changed, 25 insertions(+), 22 deletions(-) diff --git a/drivers/pci/cadence/pcie-cadence-ep.c b/drivers/pci/cadence/pcie-cadence-ep.c index 3c3a97743453..cef36cd6b710 100644 --- a/drivers/pci/cadence/pcie-cadence-ep.c +++ b/drivers/pci/cadence/pcie-cadence-ep.c @@ -77,16 +77,19 @@ static int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 fn, return 0; } -static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags) +static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, + struct pci_epf_bar *epf_bar) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; + dma_addr_t bar_phys = epf_bar->phys_addr; + enum pci_barno bar = epf_bar->barno; + int flags = epf_bar->flags; u32 addr0, addr1, reg, cfg, b, aperture, ctrl; u64 sz; /* BAR size is 2^(aperture + 7) */ - sz = max_t(size_t, size, CDNS_PCIE_EP_MIN_APERTURE); + sz = max_t(size_t, epf_bar->size, CDNS_PCIE_EP_MIN_APERTURE); /* * roundup_pow_of_two() returns an unsigned long, which is not suited * for 64bit values. diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c index 3a6feeff5f5b..b3a5533fe0b9 100644 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -117,12 +117,14 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, } static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, - enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags) + struct pci_epf_bar *epf_bar) { int ret; struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar = epf_bar->barno; + size_t size = epf_bar->size; + int flags = epf_bar->flags; enum dw_pcie_as_type as_type; u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); @@ -131,7 +133,7 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, else as_type = DW_PCIE_AS_IO; - ret = dw_pcie_ep_inbound_atu(ep, bar, bar_phys, as_type); + ret = dw_pcie_ep_inbound_atu(ep, bar, epf_bar->phys_addr, as_type); if (ret) return ret; diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index f6c0c59b1bc8..91274779e59f 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -358,7 +358,6 @@ static void pci_epf_test_unbind(struct pci_epf *epf) static int pci_epf_test_set_bar(struct pci_epf *epf) { - int flags; int bar; int ret; struct pci_epf_bar *epf_bar; @@ -370,14 +369,11 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) for (bar = BAR_0; bar <= BAR_5; bar++) { epf_bar = &epf->bar[bar]; - flags = PCI_BASE_ADDRESS_SPACE_MEMORY; - flags |= upper_32_bits(epf_bar->size) ? + epf_bar->flags |= upper_32_bits(epf_bar->size) ? PCI_BASE_ADDRESS_MEM_TYPE_64 : PCI_BASE_ADDRESS_MEM_TYPE_32; - ret = pci_epc_set_bar(epc, epf->func_no, bar, - epf_bar->phys_addr, - epf_bar->size, flags); + ret = pci_epc_set_bar(epc, epf->func_no, epf_bar); if (ret) { pci_epf_free_space(epf, epf_test->reg[bar], bar); dev_err(dev, "failed to set BAR%d\n", bar); diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index e245bba0ab53..784e33d6f229 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -300,14 +300,12 @@ EXPORT_SYMBOL_GPL(pci_epc_clear_bar); * pci_epc_set_bar() - configure BAR in order for host to assign PCI addr space * @epc: the EPC device on which BAR has to be configured * @func_no: the endpoint function number in the EPC device - * @bar: the BAR number that has to be configured - * @size: the size of the addr space - * @flags: specify memory allocation/io allocation/32bit address/64 bit address + * @epf_bar: the struct epf_bar that contains the BAR information * * Invoke to configure the BAR of the endpoint device. */ -int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags) +int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, + struct pci_epf_bar *epf_bar) { int ret; unsigned long irq_flags; @@ -319,7 +317,7 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, enum pci_barno bar, return 0; spin_lock_irqsave(&epc->lock, irq_flags); - ret = epc->ops->set_bar(epc, func_no, bar, bar_phys, size, flags); + ret = epc->ops->set_bar(epc, func_no, epf_bar); spin_unlock_irqrestore(&epc->lock, irq_flags); return ret; diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 59ed29e550e9..465b5f058b6d 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -98,6 +98,8 @@ void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar) epf->bar[bar].phys_addr = 0; epf->bar[bar].size = 0; + epf->bar[bar].barno = 0; + epf->bar[bar].flags = 0; } EXPORT_SYMBOL_GPL(pci_epf_free_space); @@ -126,6 +128,8 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar) epf->bar[bar].phys_addr = phys_addr; epf->bar[bar].size = size; + epf->bar[bar].barno = bar; + epf->bar[bar].flags = PCI_BASE_ADDRESS_SPACE_MEMORY; return space; } diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index a1a5e5df0f66..75bae8aabbf9 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -39,8 +39,7 @@ struct pci_epc_ops { int (*write_header)(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr); int (*set_bar)(struct pci_epc *epc, u8 func_no, - enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags); + struct pci_epf_bar *epf_bar); void (*clear_bar)(struct pci_epc *epc, u8 func_no, enum pci_barno bar); int (*map_addr)(struct pci_epc *epc, u8 func_no, @@ -127,8 +126,7 @@ void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf); int pci_epc_write_header(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr); int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, - enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags); + struct pci_epf_bar *epf_bar); void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, int bar); int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, phys_addr_t phys_addr, diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index e897bf076701..f7d6f4883f8b 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -97,6 +97,8 @@ struct pci_epf_driver { struct pci_epf_bar { dma_addr_t phys_addr; size_t size; + enum pci_barno barno; + int flags; }; /** -- cgit v1.2.3 From f16b1f6fdf631213428ff115ae49ea70c9ac4f2d Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:08 +0200 Subject: PCI: endpoint: Setting BAR_5 to 64-bits wide is invalid Since a 64-bit BAR consists of a BAR pair, and since there is no BAR after BAR_5, BAR_5 cannot be 64-bits wide. This sanity check is done in pci_epc_set_bar(), so that we don't need to do this sanity check in all epc->ops->set_bar() implementations. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/pci-epc-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 784e33d6f229..109d75f0b7d2 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -310,7 +310,9 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, int ret; unsigned long irq_flags; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || + (epf_bar->barno == BAR_5 && + epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) return -EINVAL; if (!epc->ops->set_bar) -- cgit v1.2.3 From 3567a4edd6352239586470da1bd5834989425085 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:09 +0200 Subject: PCI: endpoint: Setting 64-bit/prefetch bit is invalid when IO is set If flag PCI_BASE_ADDRESS_SPACE_IO is set, also having any PCI_BASE_ADDRESS_MEM_* bit set is invalid. This sanity check is done in pci_epc_set_bar(), so that we don't need to do this sanity check in all epc->ops->set_bar() implementations. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/pci-epc-core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 109d75f0b7d2..40eea20d21f9 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -309,10 +309,13 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, { int ret; unsigned long irq_flags; + int flags = epf_bar->flags; if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || (epf_bar->barno == BAR_5 && - epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) + flags & PCI_BASE_ADDRESS_MEM_TYPE_64) || + (flags & PCI_BASE_ADDRESS_SPACE_IO && + flags & PCI_BASE_ADDRESS_IO_MASK)) return -EINVAL; if (!epc->ops->set_bar) -- cgit v1.2.3 From f25b5fae29d4e5fe6ae17d3f898a959d72519b6a Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:10 +0200 Subject: PCI: endpoint: Setting a BAR size > 4 GB is invalid if 64-bit flag is not set Setting a BAR size > 4 GB is invalid if PCI_BASE_ADDRESS_MEM_TYPE_64 flag is not set. This sanity check is done in pci_epc_set_bar(), so that we don't need to do this sanity check in all epc->ops->set_bar() implementations. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/pci-epc-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 40eea20d21f9..8637822605ff 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -315,7 +315,9 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, (epf_bar->barno == BAR_5 && flags & PCI_BASE_ADDRESS_MEM_TYPE_64) || (flags & PCI_BASE_ADDRESS_SPACE_IO && - flags & PCI_BASE_ADDRESS_IO_MASK)) + flags & PCI_BASE_ADDRESS_IO_MASK) || + (upper_32_bits(epf_bar->size) && + !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64))) return -EINVAL; if (!epc->ops->set_bar) -- cgit v1.2.3 From d28810ba7891a1df2cb00116c6c66167970a193d Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:11 +0200 Subject: PCI: designware-ep: Make dw_pcie_ep_set_bar() handle 64-bit BARs properly Since a 64-bit BAR consists of a BAR pair, we need to write to both BARs in the BAR pair to setup the BAR properly. Link: https://lkml.kernel.org/r/20180328115018.31921-7-niklas.cassel@axis.com Signed-off-by: Niklas Cassel [lorenzo.pieralisi@arm.com: updated code according to review] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Gustavo Pimentel --- drivers/pci/dwc/pcie-designware-ep.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c index b3a5533fe0b9..70c8c1eedb42 100644 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -138,8 +138,15 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, return ret; dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writel_dbi2(pci, reg, size - 1); + + dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1)); dw_pcie_writel_dbi(pci, reg, flags); + + if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { + dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1)); + dw_pcie_writel_dbi(pci, reg + 4, 0); + } + dw_pcie_dbi_ro_wr_dis(pci); return 0; -- cgit v1.2.3 From a2ea8ac4ec72da44e2cf508e6431db0487b26893 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:12 +0200 Subject: PCI: cadence: Set PCI_BASE_ADDRESS_MEM_TYPE_64 if a 64-bit BAR was set-up cdns_pcie_ep_set_bar() does some round-up of the BAR size, which means that a 64-bit BAR can be set-up, even when the flag PCI_BASE_ADDRESS_MEM_TYPE_64 isn't set. If a 64-bit BAR was set-up, set the flag PCI_BASE_ADDRESS_MEM_TYPE_64, so that the calling function can know what BAR width that was actually set-up. I'm not sure why cdns_pcie_ep_set_bar() doesn't obey the flag PCI_BASE_ADDRESS_MEM_TYPE_64, but I leave this for the MAINTAINER to fix, since there might be a reason why this flag is ignored. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Alan Douglas --- drivers/pci/cadence/pcie-cadence-ep.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/cadence/pcie-cadence-ep.c b/drivers/pci/cadence/pcie-cadence-ep.c index cef36cd6b710..2905e098678c 100644 --- a/drivers/pci/cadence/pcie-cadence-ep.c +++ b/drivers/pci/cadence/pcie-cadence-ep.c @@ -106,6 +106,9 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, if (is_64bits && (bar & 1)) return -EINVAL; + if (is_64bits && !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) + epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; + if (is_64bits && is_prefetch) ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS; else if (is_prefetch) -- cgit v1.2.3 From fca83058753456528bef62579ae2b50799d7a473 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:13 +0200 Subject: PCI: endpoint: Handle 64-bit BARs properly If a 64-bit BAR was set-up, we need to skip a BAR, since a 64-bit BAR consists of a BAR pair. We need to check what BAR width the epc->ops->set_bar() specific implementation actually did set-up, since some drivers, like the Cadence EP controller, sometimes sets up a 64-bit BAR, even though a 32-bit BAR was requested. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/functions/pci-epf-test.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 91274779e59f..d46e3ebabb8e 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -380,6 +380,13 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) if (bar == test_reg_bar) return ret; } + /* + * pci_epc_set_bar() sets PCI_BASE_ADDRESS_MEM_TYPE_64 + * if the specific implementation required a 64-bit BAR, + * even if we only requested a 32-bit BAR. + */ + if (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64) + bar++; } return 0; -- cgit v1.2.3 From 77d08dbdae2e70a446c61f5db763deed5947acf3 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:14 +0200 Subject: PCI: endpoint: Make epc->ops->clear_bar()/pci_epc_clear_bar() take struct *epf_bar Make epc->ops->clear_bar()/pci_epc_clear_bar() take struct *epf_bar. This is needed so that epc->ops->clear_bar() can clear the BAR pair, if the BAR is 64-bits wide. This also makes it possible for pci_epc_clear_bar() to sanity check the flags. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Reviewed-by: Gustavo Pimentel --- drivers/pci/cadence/pcie-cadence-ep.c | 3 ++- drivers/pci/dwc/pcie-designware-ep.c | 13 ++++++++++--- drivers/pci/endpoint/functions/pci-epf-test.c | 5 ++++- drivers/pci/endpoint/pci-epc-core.c | 7 ++++--- include/linux/pci-epc.h | 5 +++-- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/drivers/pci/cadence/pcie-cadence-ep.c b/drivers/pci/cadence/pcie-cadence-ep.c index 2905e098678c..3d8283e450a9 100644 --- a/drivers/pci/cadence/pcie-cadence-ep.c +++ b/drivers/pci/cadence/pcie-cadence-ep.c @@ -145,10 +145,11 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, } static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, - enum pci_barno bar) + struct pci_epf_bar *epf_bar) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; + enum pci_barno bar = epf_bar->barno; u32 reg, cfg, b, ctrl; if (bar < BAR_4) { diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c index 70c8c1eedb42..c9bdb5f139b4 100644 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -19,7 +19,8 @@ void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) pci_epc_linkup(epc); } -void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) +static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar, + int flags) { u32 reg; @@ -30,6 +31,11 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) dw_pcie_dbi_ro_wr_dis(pci); } +void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) +{ + __dw_pcie_ep_reset_bar(pci, bar, 0); +} + static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr) { @@ -104,13 +110,14 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr, } static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, - enum pci_barno bar) + struct pci_epf_bar *epf_bar) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar = epf_bar->barno; u32 atu_index = ep->bar_to_atu[bar]; - dw_pcie_ep_reset_bar(pci, bar); + __dw_pcie_ep_reset_bar(pci, bar, epf_bar->flags); dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND); clear_bit(atu_index, ep->ib_window_map); diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index d46e3ebabb8e..7cef85124325 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -344,14 +344,17 @@ static void pci_epf_test_unbind(struct pci_epf *epf) { struct pci_epf_test *epf_test = epf_get_drvdata(epf); struct pci_epc *epc = epf->epc; + struct pci_epf_bar *epf_bar; int bar; cancel_delayed_work(&epf_test->cmd_handler); pci_epc_stop(epc); for (bar = BAR_0; bar <= BAR_5; bar++) { + epf_bar = &epf->bar[bar]; + if (epf_test->reg[bar]) { pci_epf_free_space(epf, epf_test->reg[bar], bar); - pci_epc_clear_bar(epc, epf->func_no, bar); + pci_epc_clear_bar(epc, epf->func_no, epf_bar); } } } diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 8637822605ff..eccc942043cb 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -276,11 +276,12 @@ EXPORT_SYMBOL_GPL(pci_epc_map_addr); * pci_epc_clear_bar() - reset the BAR * @epc: the EPC device for which the BAR has to be cleared * @func_no: the endpoint function number in the EPC device - * @bar: the BAR number that has to be reset + * @epf_bar: the struct epf_bar that contains the BAR information * * Invoke to reset the BAR of the endpoint device. */ -void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, int bar) +void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, + struct pci_epf_bar *epf_bar) { unsigned long flags; @@ -291,7 +292,7 @@ void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, int bar) return; spin_lock_irqsave(&epc->lock, flags); - epc->ops->clear_bar(epc, func_no, bar); + epc->ops->clear_bar(epc, func_no, epf_bar); spin_unlock_irqrestore(&epc->lock, flags); } EXPORT_SYMBOL_GPL(pci_epc_clear_bar); diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 75bae8aabbf9..af657ca58b70 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -41,7 +41,7 @@ struct pci_epc_ops { int (*set_bar)(struct pci_epc *epc, u8 func_no, struct pci_epf_bar *epf_bar); void (*clear_bar)(struct pci_epc *epc, u8 func_no, - enum pci_barno bar); + struct pci_epf_bar *epf_bar); int (*map_addr)(struct pci_epc *epc, u8 func_no, phys_addr_t addr, u64 pci_addr, size_t size); void (*unmap_addr)(struct pci_epc *epc, u8 func_no, @@ -127,7 +127,8 @@ int pci_epc_write_header(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr); int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, struct pci_epf_bar *epf_bar); -void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, int bar); +void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, + struct pci_epf_bar *epf_bar); int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, phys_addr_t phys_addr, u64 pci_addr, size_t size); -- cgit v1.2.3 From 6474a4e5546e1e3fbba5081dd201ebed62939278 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:15 +0200 Subject: PCI: endpoint: Make sure that BAR_5 does not have 64-bit flag set when clearing Since a 64-bit BAR consists of a BAR pair, and since there is no BAR after BAR_5, BAR_5 cannot be 64-bits wide. This sanity check is done in pci_epc_clear_bar(), so that we don't need to do this sanity check in all epc->ops->clear_bar() implementations. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/pci-epc-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index eccc942043cb..b0ee42739c3c 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -285,7 +285,9 @@ void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, { unsigned long flags; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || + (epf_bar->barno == BAR_5 && + epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) return; if (!epc->ops->clear_bar) -- cgit v1.2.3 From 96a3be43261b919a1785d080b501fae26ce97bc2 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:16 +0200 Subject: PCI: designware-ep: Make dw_pcie_ep_reset_bar() handle 64-bit BARs properly Since a 64-bit BAR consists of a BAR pair, we need to write to both BARs in the BAR pair to clear the BAR properly. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Reviewed-by: Gustavo Pimentel Acked-by: Kishon Vijay Abraham I --- drivers/pci/dwc/pcie-designware-ep.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c index c9bdb5f139b4..9164c9084b4e 100644 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -28,6 +28,10 @@ static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar, dw_pcie_dbi_ro_wr_en(pci); dw_pcie_writel_dbi2(pci, reg, 0x0); dw_pcie_writel_dbi(pci, reg, 0x0); + if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { + dw_pcie_writel_dbi2(pci, reg + 4, 0x0); + dw_pcie_writel_dbi(pci, reg + 4, 0x0); + } dw_pcie_dbi_ro_wr_dis(pci); } -- cgit v1.2.3 From 16b17cad0990f13cdcdfd9694cc7c41c7c76b0cf Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:17 +0200 Subject: misc: pci_endpoint_test: Handle 64-bit BARs properly A 64-bit BAR consists of a BAR pair, where the second BAR has the upper bits, so we cannot simply call pci_ioremap_bar() on every single BAR index. The second BAR in a BAR pair will not have the IORESOURCE_MEM resource flag set. Only call ioremap on BARs that have the IORESOURCE_MEM resource flag set. pci 0000:01:00.0: BAR 4: assigned [mem 0xc0300000-0xc031ffff 64bit] pci 0000:01:00.0: BAR 2: assigned [mem 0xc0320000-0xc03203ff 64bit] pci 0000:01:00.0: BAR 0: assigned [mem 0xc0320400-0xc03204ff 64bit] pci-endpoint-test 0000:01:00.0: can't ioremap BAR 1: [??? 0x00000000 flags 0x0] pci-endpoint-test 0000:01:00.0: failed to read BAR1 pci-endpoint-test 0000:01:00.0: can't ioremap BAR 3: [??? 0x00000000 flags 0x0] pci-endpoint-test 0000:01:00.0: failed to read BAR3 pci-endpoint-test 0000:01:00.0: can't ioremap BAR 5: [??? 0x00000000 flags 0x0] pci-endpoint-test 0000:01:00.0: failed to read BAR5 Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/misc/pci_endpoint_test.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 320276f42653..fe8897e64635 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -534,12 +534,14 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, } for (bar = BAR_0; bar <= BAR_5; bar++) { - base = pci_ioremap_bar(pdev, bar); - if (!base) { - dev_err(dev, "failed to read BAR%d\n", bar); - WARN_ON(bar == test_reg_bar); + if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) { + base = pci_ioremap_bar(pdev, bar); + if (!base) { + dev_err(dev, "failed to read BAR%d\n", bar); + WARN_ON(bar == test_reg_bar); + } + test->bar[bar] = base; } - test->bar[bar] = base; } test->base = test->bar[test_reg_bar]; -- cgit v1.2.3 From 6db79a88c67e4679d9c1e4a3f05c6385e21f6e9a Mon Sep 17 00:00:00 2001 From: Tal Gilboa Date: Fri, 30 Mar 2018 08:37:44 -0500 Subject: PCI: Add pcie_bandwidth_available() to compute bandwidth available to device Add pcie_bandwidth_available() to compute the bandwidth available to a device. This may be limited by the device itself or by a slower upstream link leading to the device. The available bandwidth at each link along the path is computed as: link_width * link_speed * (1 - encoding_overhead) 2.5 and 5.0 GT/s links use 8b/10b encoding, which reduces the raw bandwidth available by 20%; 8.0 GT/s and faster links use 128b/130b encoding, which reduces it by about 1.5%. The result is in Mb/s, i.e., megabits/second, of raw bandwidth. Also return the device with the slowest link and the speed and width of that link. Signed-off-by: Tal Gilboa [bhelgaas: changelog, leave pcie_get_minimum_link() alone for now, return bw directly, use pci_upstream_bridge(), check "next_bw <= bw" to find uppermost limiting device, return speed/width of the limiting device] Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 3 +++ 2 files changed, 61 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ff1e72060952..91138cbeb853 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5146,6 +5146,64 @@ int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, } EXPORT_SYMBOL(pcie_get_minimum_link); +/** + * pcie_bandwidth_available - determine minimum link settings of a PCIe + * device and its bandwidth limitation + * @dev: PCI device to query + * @limiting_dev: storage for device causing the bandwidth limitation + * @speed: storage for speed of limiting device + * @width: storage for width of limiting device + * + * Walk up the PCI device chain and find the point where the minimum + * bandwidth is available. Return the bandwidth available there and (if + * limiting_dev, speed, and width pointers are supplied) information about + * that point. The bandwidth returned is in Mb/s, i.e., megabits/second of + * raw bandwidth. + */ +u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev, + enum pci_bus_speed *speed, + enum pcie_link_width *width) +{ + u16 lnksta; + enum pci_bus_speed next_speed; + enum pcie_link_width next_width; + u32 bw, next_bw; + + if (speed) + *speed = PCI_SPEED_UNKNOWN; + if (width) + *width = PCIE_LNK_WIDTH_UNKNOWN; + + bw = 0; + + while (dev) { + pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + + next_speed = pcie_link_speed[lnksta & PCI_EXP_LNKSTA_CLS]; + next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> + PCI_EXP_LNKSTA_NLW_SHIFT; + + next_bw = next_width * PCIE_SPEED2MBS_ENC(next_speed); + + /* Check if current device limits the total bandwidth */ + if (!bw || next_bw <= bw) { + bw = next_bw; + + if (limiting_dev) + *limiting_dev = dev; + if (speed) + *speed = next_speed; + if (width) + *width = next_width; + } + + dev = pci_upstream_bridge(dev); + } + + return bw; +} +EXPORT_SYMBOL(pcie_bandwidth_available); + /** * pcie_get_speed_cap - query for the PCI device's link speed capability * @dev: PCI device to query diff --git a/include/linux/pci.h b/include/linux/pci.h index 8043a5937ad0..f2bf2b7a66c7 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1083,6 +1083,9 @@ int pcie_get_mps(struct pci_dev *dev); int pcie_set_mps(struct pci_dev *dev, int mps); int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, enum pcie_link_width *width); +u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev, + enum pci_bus_speed *speed, + enum pcie_link_width *width); void pcie_flr(struct pci_dev *dev); int __pci_reset_function_locked(struct pci_dev *dev); int pci_reset_function(struct pci_dev *dev); -- cgit v1.2.3 From 9e506a7b51474241f0c900e53e85512780275c05 Mon Sep 17 00:00:00 2001 From: Tal Gilboa Date: Fri, 30 Mar 2018 08:56:47 -0500 Subject: PCI: Add pcie_print_link_status() to log link speed and whether it's limited Add pcie_print_link_status(). This logs the current settings of the link (speed, width, and total available bandwidth). If the device is capable of more bandwidth but is limited by a slower upstream link, we include information about the link that limits the device's performance. The user may be able to move the device to a different slot for better performance. This provides a unified method for all PCI devices to report status and issues, instead of each device reporting in a different way, using different code. Signed-off-by: Tal Gilboa [bhelgaas: changelog, reword log messages, print device capabilities when not limited, print bandwidth in Gb/s] Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 32 ++++++++++++++++++++++++++++++++ include/linux/pci.h | 1 + 2 files changed, 33 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 91138cbeb853..e7a3917ed389 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5288,6 +5288,38 @@ u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed, return *width * PCIE_SPEED2MBS_ENC(*speed); } +/** + * pcie_print_link_status - Report the PCI device's link speed and width + * @dev: PCI device to query + * + * Report the available bandwidth at the device. If this is less than the + * device is capable of, report the device's maximum possible bandwidth and + * the upstream link that limits its performance to less than that. + */ +void pcie_print_link_status(struct pci_dev *dev) +{ + enum pcie_link_width width, width_cap; + enum pci_bus_speed speed, speed_cap; + struct pci_dev *limiting_dev = NULL; + u32 bw_avail, bw_cap; + + bw_cap = pcie_bandwidth_capable(dev, &speed_cap, &width_cap); + bw_avail = pcie_bandwidth_available(dev, &limiting_dev, &speed, &width); + + if (bw_avail >= bw_cap) + pci_info(dev, "%u.%03u Gb/s available bandwidth (%s x%d link)\n", + bw_cap / 1000, bw_cap % 1000, + PCIE_SPEED2STR(speed_cap), width_cap); + else + pci_info(dev, "%u.%03u Gb/s available bandwidth, limited by %s x%d link at %s (capable of %u.%03u Gb/s with %s x%d link)\n", + bw_avail / 1000, bw_avail % 1000, + PCIE_SPEED2STR(speed), width, + limiting_dev ? pci_name(limiting_dev) : "", + bw_cap / 1000, bw_cap % 1000, + PCIE_SPEED2STR(speed_cap), width_cap); +} +EXPORT_SYMBOL(pcie_print_link_status); + /** * pci_select_bars - Make BAR mask from the type of resource * @dev: the PCI device for which BAR mask is made diff --git a/include/linux/pci.h b/include/linux/pci.h index f2bf2b7a66c7..38f7957121ef 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1086,6 +1086,7 @@ int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev, enum pci_bus_speed *speed, enum pcie_link_width *width); +void pcie_print_link_status(struct pci_dev *dev); void pcie_flr(struct pci_dev *dev); int __pci_reset_function_locked(struct pci_dev *dev); int pci_reset_function(struct pci_dev *dev); -- cgit v1.2.3 From 190b509c8de2af7c35b8fdcef4e54163a865d680 Mon Sep 17 00:00:00 2001 From: Tal Gilboa Date: Fri, 30 Mar 2018 09:01:05 -0500 Subject: net/mlx4_core: Report PCIe link properties with pcie_print_link_status() Use pcie_print_link_status() to report PCIe link speed and possible limitations instead of implementing this in the driver itself. Signed-off-by: Tal Gilboa Signed-off-by: Tariq Toukan [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas --- drivers/net/ethernet/mellanox/mlx4/main.c | 81 +------------------------------ 1 file changed, 1 insertion(+), 80 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 4d84cab77105..30cacac54e69 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -623,85 +623,6 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) return 0; } -static int mlx4_get_pcie_dev_link_caps(struct mlx4_dev *dev, - enum pci_bus_speed *speed, - enum pcie_link_width *width) -{ - u32 lnkcap1, lnkcap2; - int err1, err2; - -#define PCIE_MLW_CAP_SHIFT 4 /* start of MLW mask in link capabilities */ - - *speed = PCI_SPEED_UNKNOWN; - *width = PCIE_LNK_WIDTH_UNKNOWN; - - err1 = pcie_capability_read_dword(dev->persist->pdev, PCI_EXP_LNKCAP, - &lnkcap1); - err2 = pcie_capability_read_dword(dev->persist->pdev, PCI_EXP_LNKCAP2, - &lnkcap2); - if (!err2 && lnkcap2) { /* PCIe r3.0-compliant */ - if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) - *speed = PCIE_SPEED_8_0GT; - else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB) - *speed = PCIE_SPEED_5_0GT; - else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB) - *speed = PCIE_SPEED_2_5GT; - } - if (!err1) { - *width = (lnkcap1 & PCI_EXP_LNKCAP_MLW) >> PCIE_MLW_CAP_SHIFT; - if (!lnkcap2) { /* pre-r3.0 */ - if (lnkcap1 & PCI_EXP_LNKCAP_SLS_5_0GB) - *speed = PCIE_SPEED_5_0GT; - else if (lnkcap1 & PCI_EXP_LNKCAP_SLS_2_5GB) - *speed = PCIE_SPEED_2_5GT; - } - } - - if (*speed == PCI_SPEED_UNKNOWN || *width == PCIE_LNK_WIDTH_UNKNOWN) { - return err1 ? err1 : - err2 ? err2 : -EINVAL; - } - return 0; -} - -static void mlx4_check_pcie_caps(struct mlx4_dev *dev) -{ - enum pcie_link_width width, width_cap; - enum pci_bus_speed speed, speed_cap; - int err; - -#define PCIE_SPEED_STR(speed) \ - (speed == PCIE_SPEED_8_0GT ? "8.0GT/s" : \ - speed == PCIE_SPEED_5_0GT ? "5.0GT/s" : \ - speed == PCIE_SPEED_2_5GT ? "2.5GT/s" : \ - "Unknown") - - err = mlx4_get_pcie_dev_link_caps(dev, &speed_cap, &width_cap); - if (err) { - mlx4_warn(dev, - "Unable to determine PCIe device BW capabilities\n"); - return; - } - - err = pcie_get_minimum_link(dev->persist->pdev, &speed, &width); - if (err || speed == PCI_SPEED_UNKNOWN || - width == PCIE_LNK_WIDTH_UNKNOWN) { - mlx4_warn(dev, - "Unable to determine PCI device chain minimum BW\n"); - return; - } - - if (width != width_cap || speed != speed_cap) - mlx4_warn(dev, - "PCIe BW is different than device's capability\n"); - - mlx4_info(dev, "PCIe link speed is %s, device supports %s\n", - PCIE_SPEED_STR(speed), PCIE_SPEED_STR(speed_cap)); - mlx4_info(dev, "PCIe link width is x%d, device supports x%d\n", - width, width_cap); - return; -} - /*The function checks if there are live vf, return the num of them*/ static int mlx4_how_many_lives_vf(struct mlx4_dev *dev) { @@ -3475,7 +3396,7 @@ slave_start: * express device capabilities are under-satisfied by the bus. */ if (!mlx4_is_slave(dev)) - mlx4_check_pcie_caps(dev); + pcie_print_link_status(dev->persist->pdev); /* In master functions, the communication channel must be initialized * after obtaining its address from fw */ -- cgit v1.2.3 From 00c6bcb0d60e3f05a533d324ca17b21b6af87fea Mon Sep 17 00:00:00 2001 From: Tal Gilboa Date: Fri, 30 Mar 2018 09:01:34 -0500 Subject: net/mlx5: Report PCIe link properties with pcie_print_link_status() Use pcie_print_link_status() to report PCIe link speed and possible limitations. Signed-off-by: Tal Gilboa [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 2ef641c91c26..622f02d34aae 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1043,6 +1043,10 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv, dev_info(&pdev->dev, "firmware version: %d.%d.%d\n", fw_rev_maj(dev), fw_rev_min(dev), fw_rev_sub(dev)); + /* Only PFs hold the relevant PCIe information for this query */ + if (mlx5_core_is_pf(dev)) + pcie_print_link_status(dev->pdev); + /* on load removing any previous indication of internal error, device is * up */ -- cgit v1.2.3 From 33523a361307c097a8a23f3549cb485db5fba6c5 Mon Sep 17 00:00:00 2001 From: Tal Gilboa Date: Fri, 30 Mar 2018 09:01:51 -0500 Subject: net/mlx5e: Use pcie_bandwidth_available() to compute bandwidth Use the new pci_bandwidth_available() function to calculate maximum available bandwidth through the PCI chain instead of computing it ourselves with mlx5e_get_pci_bw(). This is used to detect when the device is capable of more bandwidth than is available in the current slot. The driver may adjust compression settings accordingly. Note that pci_bandwidth_available() accounts for PCIe encoding overhead, so it is more accurate than mlx5e_get_pci_bw() was. Signed-off-by: Tal Gilboa [bhelgaas: remove mlx5e_get_pci_bw() wrapper altogether] Signed-off-by: Bjorn Helgaas Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 32 +---------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 47bab842c5ee..93291ec4a3d1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3864,36 +3864,6 @@ void mlx5e_build_default_indir_rqt(u32 *indirection_rqt, int len, indirection_rqt[i] = i % num_channels; } -static int mlx5e_get_pci_bw(struct mlx5_core_dev *mdev, u32 *pci_bw) -{ - enum pcie_link_width width; - enum pci_bus_speed speed; - int err = 0; - - err = pcie_get_minimum_link(mdev->pdev, &speed, &width); - if (err) - return err; - - if (speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN) - return -EINVAL; - - switch (speed) { - case PCIE_SPEED_2_5GT: - *pci_bw = 2500 * width; - break; - case PCIE_SPEED_5_0GT: - *pci_bw = 5000 * width; - break; - case PCIE_SPEED_8_0GT: - *pci_bw = 8000 * width; - break; - default: - return -EINVAL; - } - - return 0; -} - static bool cqe_compress_heuristic(u32 link_speed, u32 pci_bw) { return (link_speed && pci_bw && @@ -3979,7 +3949,7 @@ void mlx5e_build_nic_params(struct mlx5_core_dev *mdev, params->num_tc = 1; mlx5e_get_max_linkspeed(mdev, &link_speed); - mlx5e_get_pci_bw(mdev, &pci_bw); + pci_bw = pcie_bandwidth_available(mdev->pdev, NULL, NULL, NULL); mlx5_core_dbg(mdev, "Max link speed = %d, PCI BW = %d\n", link_speed, pci_bw); -- cgit v1.2.3 From 170648fda93729f05d0758c76b8cd9170408471b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 30 Mar 2018 14:22:44 -0500 Subject: fm10k: Report PCIe link properties with pcie_print_link_status() Previously the driver used pcie_get_minimum_link() to warn when the NIC is in a slot that can't supply as much bandwidth as the NIC could use. pcie_get_minimum_link() can be misleading because it finds the slowest link and the narrowest link (which may be different links) without considering the total bandwidth of each link. For a path with a 16 GT/s x1 link and a 2.5 GT/s x16 link, it returns 2.5 GT/s x1, which corresponds to 250 MB/s of bandwidth, not the true available bandwidth of about 1969 MB/s for a 16 GT/s x1 link. Use pcie_print_link_status() to report PCIe link speed and possible limitations instead of implementing this in the driver itself. This finds the slowest link in the path to the device by computing the total bandwidth of each link and compares that with the capabilities of the device. Note that the driver previously used dev_warn() to suggest using a different slot, but pcie_print_link_status() uses dev_info() because if the platform has no faster slot available, the user can't do anything about the warning and may not want to be bothered with it. Signed-off-by: Bjorn Helgaas Acked-by: Jacob Keller --- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 87 +--------------------------- 1 file changed, 1 insertion(+), 86 deletions(-) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index a434fecfdfeb..aa05fb534942 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -2120,91 +2120,6 @@ static int fm10k_sw_init(struct fm10k_intfc *interface, return 0; } -static void fm10k_slot_warn(struct fm10k_intfc *interface) -{ - enum pcie_link_width width = PCIE_LNK_WIDTH_UNKNOWN; - enum pci_bus_speed speed = PCI_SPEED_UNKNOWN; - struct fm10k_hw *hw = &interface->hw; - int max_gts = 0, expected_gts = 0; - - if (pcie_get_minimum_link(interface->pdev, &speed, &width) || - speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN) { - dev_warn(&interface->pdev->dev, - "Unable to determine PCI Express bandwidth.\n"); - return; - } - - switch (speed) { - case PCIE_SPEED_2_5GT: - /* 8b/10b encoding reduces max throughput by 20% */ - max_gts = 2 * width; - break; - case PCIE_SPEED_5_0GT: - /* 8b/10b encoding reduces max throughput by 20% */ - max_gts = 4 * width; - break; - case PCIE_SPEED_8_0GT: - /* 128b/130b encoding has less than 2% impact on throughput */ - max_gts = 8 * width; - break; - default: - dev_warn(&interface->pdev->dev, - "Unable to determine PCI Express bandwidth.\n"); - return; - } - - dev_info(&interface->pdev->dev, - "PCI Express bandwidth of %dGT/s available\n", - max_gts); - dev_info(&interface->pdev->dev, - "(Speed:%s, Width: x%d, Encoding Loss:%s, Payload:%s)\n", - (speed == PCIE_SPEED_8_0GT ? "8.0GT/s" : - speed == PCIE_SPEED_5_0GT ? "5.0GT/s" : - speed == PCIE_SPEED_2_5GT ? "2.5GT/s" : - "Unknown"), - hw->bus.width, - (speed == PCIE_SPEED_2_5GT ? "20%" : - speed == PCIE_SPEED_5_0GT ? "20%" : - speed == PCIE_SPEED_8_0GT ? "<2%" : - "Unknown"), - (hw->bus.payload == fm10k_bus_payload_128 ? "128B" : - hw->bus.payload == fm10k_bus_payload_256 ? "256B" : - hw->bus.payload == fm10k_bus_payload_512 ? "512B" : - "Unknown")); - - switch (hw->bus_caps.speed) { - case fm10k_bus_speed_2500: - /* 8b/10b encoding reduces max throughput by 20% */ - expected_gts = 2 * hw->bus_caps.width; - break; - case fm10k_bus_speed_5000: - /* 8b/10b encoding reduces max throughput by 20% */ - expected_gts = 4 * hw->bus_caps.width; - break; - case fm10k_bus_speed_8000: - /* 128b/130b encoding has less than 2% impact on throughput */ - expected_gts = 8 * hw->bus_caps.width; - break; - default: - dev_warn(&interface->pdev->dev, - "Unable to determine expected PCI Express bandwidth.\n"); - return; - } - - if (max_gts >= expected_gts) - return; - - dev_warn(&interface->pdev->dev, - "This device requires %dGT/s of bandwidth for optimal performance.\n", - expected_gts); - dev_warn(&interface->pdev->dev, - "A %sslot with x%d lanes is suggested.\n", - (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s " : - hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s " : - hw->bus_caps.speed == fm10k_bus_speed_8000 ? "8.0GT/s " : ""), - hw->bus_caps.width); -} - /** * fm10k_probe - Device Initialization Routine * @pdev: PCI device information struct @@ -2326,7 +2241,7 @@ static int fm10k_probe(struct pci_dev *pdev, const struct pci_device_id *ent) mod_timer(&interface->service_timer, (HZ * 2) + jiffies); /* print warning for non-optimal configurations */ - fm10k_slot_warn(interface); + pcie_print_link_status(interface->pdev); /* report MAC address for logging */ dev_info(&pdev->dev, "%pM\n", netdev->dev_addr); -- cgit v1.2.3 From 86df1073a57a5b87af3e0e6ed76e42a6cf950fd4 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Tue, 3 Apr 2018 16:55:27 +0100 Subject: MAINTAINERS: Add missing /drivers/pci/cadence directory entry Commit 37dddf14f1ae ("PCI: cadence: Add EndPoint Controller driver for Cadence PCIe controller") created the /drivers/pci/cadence directory to keep in a single place Cadence host and endpoint controller drivers. Since code in /drivers/pci/cadence falls within the PCI native host bridge and endpoint controllers mainteinance remit, that maintainer entry should have been updated too by adding the /drivers/pci/cadence directory to it but it actually was not. Update the MAINTAINERS entry accordingly, fixing the omission. Signed-off-by: Lorenzo Pieralisi Cc: Alan Douglas Cc: Bjorn Helgaas --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 3bdc260e36b7..969426969438 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10722,6 +10722,7 @@ L: linux-pci@vger.kernel.org Q: http://patchwork.ozlabs.org/project/linux-pci/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/pci.git/ S: Supported +F: drivers/pci/cadence/ F: drivers/pci/host/ F: drivers/pci/dwc/ -- cgit v1.2.3 From e2515476ab3ca228369be14ac4792787c91d1804 Mon Sep 17 00:00:00 2001 From: Gabriele Paoloni Date: Thu, 15 Mar 2018 02:15:51 +0800 Subject: PCI: Remove __weak tag from pci_register_io_range() pci_register_io_range() has only one definition, so there is no need for the __weak attribute. Remove it. Tested-by: dann frazier Signed-off-by: Gabriele Paoloni Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko --- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index f6a4dd10d9b0..4666a016356e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3455,7 +3455,7 @@ static DEFINE_SPINLOCK(io_range_lock); * Record the PCI IO range (expressed as CPU physical address + size). * Return a negative value if an error has occured, zero otherwise */ -int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size) +int pci_register_io_range(phys_addr_t addr, resource_size_t size) { int err = 0; -- cgit v1.2.3 From fcfaab30933bd151bd8cb4dd07b3f11d885bb611 Mon Sep 17 00:00:00 2001 From: Gabriele Paoloni Date: Thu, 15 Mar 2018 02:15:52 +0800 Subject: PCI: Add fwnode handler as input param of pci_register_io_range() In preparation for having the PCI MMIO helpers use the new generic I/O space management (logical PIO) we need to add the fwnode handler as an extra input parameter. Changes the signature of pci_register_io_range() and its callers as needed. Tested-by: dann frazier Signed-off-by: Gabriele Paoloni Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Acked-by: Rob Herring --- drivers/acpi/pci_root.c | 8 +++++--- drivers/of/address.c | 4 +++- drivers/pci/pci.c | 3 ++- include/linux/pci.h | 3 ++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 6fc204a52493..12134797b374 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -729,7 +729,8 @@ next: } } -static void acpi_pci_root_remap_iospace(struct resource_entry *entry) +static void acpi_pci_root_remap_iospace(struct fwnode_handle *fwnode, + struct resource_entry *entry) { #ifdef PCI_IOBASE struct resource *res = entry->res; @@ -738,7 +739,7 @@ static void acpi_pci_root_remap_iospace(struct resource_entry *entry) resource_size_t length = resource_size(res); unsigned long port; - if (pci_register_io_range(cpu_addr, length)) + if (pci_register_io_range(fwnode, cpu_addr, length)) goto err; port = pci_address_to_pio(cpu_addr); @@ -780,7 +781,8 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) else { resource_list_for_each_entry_safe(entry, tmp, list) { if (entry->res->flags & IORESOURCE_IO) - acpi_pci_root_remap_iospace(entry); + acpi_pci_root_remap_iospace(&device->fwnode, + entry); if (entry->res->flags & IORESOURCE_DISABLED) resource_list_destroy_entry(entry); diff --git a/drivers/of/address.c b/drivers/of/address.c index ce4d3d8b85de..cdf047b6d0a2 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -2,6 +2,7 @@ #define pr_fmt(fmt) "OF: " fmt #include +#include #include #include #include @@ -333,7 +334,8 @@ int of_pci_range_to_resource(struct of_pci_range *range, if (res->flags & IORESOURCE_IO) { unsigned long port; - err = pci_register_io_range(range->cpu_addr, range->size); + err = pci_register_io_range(&np->fwnode, range->cpu_addr, + range->size); if (err) goto invalid_range; port = pci_address_to_pio(range->cpu_addr); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 4666a016356e..07290a31370c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3455,7 +3455,8 @@ static DEFINE_SPINLOCK(io_range_lock); * Record the PCI IO range (expressed as CPU physical address + size). * Return a negative value if an error has occured, zero otherwise */ -int pci_register_io_range(phys_addr_t addr, resource_size_t size) +int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr, + resource_size_t size) { int err = 0; diff --git a/include/linux/pci.h b/include/linux/pci.h index 024a1beda008..be686fd87abb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1226,7 +1226,8 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus, void *alignf_data); -int pci_register_io_range(phys_addr_t addr, resource_size_t size); +int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr, + resource_size_t size); unsigned long pci_address_to_pio(phys_addr_t addr); phys_addr_t pci_pio_to_address(unsigned long pio); int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr); -- cgit v1.2.3 From 5745392e0c2b78e0d73203281d5c42cbd6993194 Mon Sep 17 00:00:00 2001 From: Zhichang Yuan Date: Thu, 15 Mar 2018 02:15:53 +0800 Subject: PCI: Apply the new generic I/O management on PCI IO hosts After introducing the new generic I/O space management (Logical PIO), the original PCI MMIO relevant helpers need to be updated based on the new interfaces defined in logical PIO. Adapt the corresponding code to match the changes introduced by logical PIO. Tested-by: dann frazier Signed-off-by: Zhichang Yuan Signed-off-by: Gabriele Paoloni Signed-off-by: Arnd Bergmann # earlier draft Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko --- drivers/pci/pci.c | 92 +++++++++--------------------------------------- include/asm-generic/io.h | 2 +- 2 files changed, 18 insertions(+), 76 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 07290a31370c..83a263fc9246 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -3440,17 +3441,6 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name) } EXPORT_SYMBOL(pci_request_regions_exclusive); -#ifdef PCI_IOBASE -struct io_range { - struct list_head list; - phys_addr_t start; - resource_size_t size; -}; - -static LIST_HEAD(io_range_list); -static DEFINE_SPINLOCK(io_range_lock); -#endif - /* * Record the PCI IO range (expressed as CPU physical address + size). * Return a negative value if an error has occured, zero otherwise @@ -3458,51 +3448,28 @@ static DEFINE_SPINLOCK(io_range_lock); int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr, resource_size_t size) { - int err = 0; - + int ret = 0; #ifdef PCI_IOBASE - struct io_range *range; - resource_size_t allocated_size = 0; - - /* check if the range hasn't been previously recorded */ - spin_lock(&io_range_lock); - list_for_each_entry(range, &io_range_list, list) { - if (addr >= range->start && addr + size <= range->start + size) { - /* range already registered, bail out */ - goto end_register; - } - allocated_size += range->size; - } + struct logic_pio_hwaddr *range; - /* range not registed yet, check for available space */ - if (allocated_size + size - 1 > IO_SPACE_LIMIT) { - /* if it's too big check if 64K space can be reserved */ - if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) { - err = -E2BIG; - goto end_register; - } - - size = SZ_64K; - pr_warn("Requested IO range too big, new size set to 64K\n"); - } + if (!size || addr + size < addr) + return -EINVAL; - /* add the range to the list */ range = kzalloc(sizeof(*range), GFP_ATOMIC); - if (!range) { - err = -ENOMEM; - goto end_register; - } + if (!range) + return -ENOMEM; - range->start = addr; + range->fwnode = fwnode; range->size = size; + range->hw_start = addr; + range->flags = LOGIC_PIO_CPU_MMIO; - list_add_tail(&range->list, &io_range_list); - -end_register: - spin_unlock(&io_range_lock); + ret = logic_pio_register_range(range); + if (ret) + kfree(range); #endif - return err; + return ret; } phys_addr_t pci_pio_to_address(unsigned long pio) @@ -3510,21 +3477,10 @@ phys_addr_t pci_pio_to_address(unsigned long pio) phys_addr_t address = (phys_addr_t)OF_BAD_ADDR; #ifdef PCI_IOBASE - struct io_range *range; - resource_size_t allocated_size = 0; - - if (pio > IO_SPACE_LIMIT) + if (pio >= MMIO_UPPER_LIMIT) return address; - spin_lock(&io_range_lock); - list_for_each_entry(range, &io_range_list, list) { - if (pio >= allocated_size && pio < allocated_size + range->size) { - address = range->start + pio - allocated_size; - break; - } - allocated_size += range->size; - } - spin_unlock(&io_range_lock); + address = logic_pio_to_hwaddr(pio); #endif return address; @@ -3533,21 +3489,7 @@ phys_addr_t pci_pio_to_address(unsigned long pio) unsigned long __weak pci_address_to_pio(phys_addr_t address) { #ifdef PCI_IOBASE - struct io_range *res; - resource_size_t offset = 0; - unsigned long addr = -1; - - spin_lock(&io_range_lock); - list_for_each_entry(res, &io_range_list, list) { - if (address >= res->start && address < res->start + res->size) { - addr = address - res->start + offset; - break; - } - offset += res->size; - } - spin_unlock(&io_range_lock); - - return addr; + return logic_pio_trans_cpuaddr(address); #else if (address > IO_SPACE_LIMIT) return (unsigned long)-1; diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h index b7996a79d64b..5a5993166654 100644 --- a/include/asm-generic/io.h +++ b/include/asm-generic/io.h @@ -901,7 +901,7 @@ static inline void iounmap(void __iomem *addr) #define ioport_map ioport_map static inline void __iomem *ioport_map(unsigned long port, unsigned int nr) { - return PCI_IOBASE + (port & IO_SPACE_LIMIT); + return PCI_IOBASE + (port & MMIO_UPPER_LIMIT); } #endif -- cgit v1.2.3 From 65af618d2c559f8eb19d80d03a23029651a59de4 Mon Sep 17 00:00:00 2001 From: Zhichang Yuan Date: Thu, 15 Mar 2018 02:15:54 +0800 Subject: of: Add missing I/O range exception for indirect-IO devices There are some special ISA/LPC devices that work on a specific I/O range where it is not correct to specify a 'ranges' property in the DTS parent node as CPU addresses translated from DTS node are only for memory space on some architectures, such as ARM64. Without the parent 'ranges' property, of_translate_address() returns an error. Here we add special handling for this case. During the OF address translation, some checking will be performed to identify whether the device node is registered as indirect-IO. If it is, the I/O translation will be done in a different way from that one of PCI MMIO. In this way, the I/O 'reg' property of the special ISA/LPC devices will be parsed correctly. Tested-by: dann frazier Signed-off-by: Zhichang Yuan Signed-off-by: Gabriele Paoloni Signed-off-by: Arnd Bergmann # earlier draft Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Acked-by: Rob Herring --- drivers/of/address.c | 92 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 16 deletions(-) diff --git a/drivers/of/address.c b/drivers/of/address.c index cdf047b6d0a2..53349912ac75 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -562,9 +563,14 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, * that translation is impossible (that is we are not dealing with a value * that can be mapped to a cpu physical address). This is not really specified * that way, but this is traditionally the way IBM at least do things + * + * Whenever the translation fails, the *host pointer will be set to the + * device that had registered logical PIO mapping, and the return code is + * relative to that node. */ static u64 __of_translate_address(struct device_node *dev, - const __be32 *in_addr, const char *rprop) + const __be32 *in_addr, const char *rprop, + struct device_node **host) { struct device_node *parent = NULL; struct of_bus *bus, *pbus; @@ -577,6 +583,7 @@ static u64 __of_translate_address(struct device_node *dev, /* Increase refcount at current level */ of_node_get(dev); + *host = NULL; /* Get parent & match bus type */ parent = of_get_parent(dev); if (parent == NULL) @@ -597,6 +604,8 @@ static u64 __of_translate_address(struct device_node *dev, /* Translate */ for (;;) { + struct logic_pio_hwaddr *iorange; + /* Switch to parent bus */ of_node_put(dev); dev = parent; @@ -609,6 +618,19 @@ static u64 __of_translate_address(struct device_node *dev, break; } + /* + * For indirectIO device which has no ranges property, get + * the address from reg directly. + */ + iorange = find_io_range_by_fwnode(&dev->fwnode); + if (iorange && (iorange->flags != LOGIC_PIO_CPU_MMIO)) { + result = of_read_number(addr + 1, na - 1); + pr_debug("indirectIO matched(%pOF) 0x%llx\n", + dev, result); + *host = of_node_get(dev); + break; + } + /* Get new parent bus and counts */ pbus = of_match_bus(parent); pbus->count_cells(dev, &pna, &pns); @@ -640,13 +662,32 @@ static u64 __of_translate_address(struct device_node *dev, u64 of_translate_address(struct device_node *dev, const __be32 *in_addr) { - return __of_translate_address(dev, in_addr, "ranges"); + struct device_node *host; + u64 ret; + + ret = __of_translate_address(dev, in_addr, "ranges", &host); + if (host) { + of_node_put(host); + return OF_BAD_ADDR; + } + + return ret; } EXPORT_SYMBOL(of_translate_address); u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr) { - return __of_translate_address(dev, in_addr, "dma-ranges"); + struct device_node *host; + u64 ret; + + ret = __of_translate_address(dev, in_addr, "dma-ranges", &host); + + if (host) { + of_node_put(host); + return OF_BAD_ADDR; + } + + return ret; } EXPORT_SYMBOL(of_translate_dma_address); @@ -688,29 +729,48 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, } EXPORT_SYMBOL(of_get_address); +static u64 of_translate_ioport(struct device_node *dev, const __be32 *in_addr, + u64 size) +{ + u64 taddr; + unsigned long port; + struct device_node *host; + + taddr = __of_translate_address(dev, in_addr, "ranges", &host); + if (host) { + /* host-specific port access */ + port = logic_pio_trans_hwaddr(&host->fwnode, taddr, size); + of_node_put(host); + } else { + /* memory-mapped I/O range */ + port = pci_address_to_pio(taddr); + } + + if (port == (unsigned long)-1) + return OF_BAD_ADDR; + + return port; +} + static int __of_address_to_resource(struct device_node *dev, const __be32 *addrp, u64 size, unsigned int flags, const char *name, struct resource *r) { u64 taddr; - if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) + if (flags & IORESOURCE_MEM) + taddr = of_translate_address(dev, addrp); + else if (flags & IORESOURCE_IO) + taddr = of_translate_ioport(dev, addrp, size); + else return -EINVAL; - taddr = of_translate_address(dev, addrp); + if (taddr == OF_BAD_ADDR) return -EINVAL; memset(r, 0, sizeof(struct resource)); - if (flags & IORESOURCE_IO) { - unsigned long port; - port = pci_address_to_pio(taddr); - if (port == (unsigned long)-1) - return -EINVAL; - r->start = port; - r->end = port + size - 1; - } else { - r->start = taddr; - r->end = taddr + size - 1; - } + + r->start = taddr; + r->end = taddr + size - 1; r->flags = flags; r->name = name ? name : dev->full_name; -- cgit v1.2.3 From adf38bb0b5956ab5469acb1ca981a9287c7ad1d8 Mon Sep 17 00:00:00 2001 From: Zhichang Yuan Date: Wed, 21 Mar 2018 17:23:02 -0500 Subject: HISI LPC: Support the LPC host on Hip06/Hip07 with DT bindings The low-pin-count (LPC) interface of Hip06/Hip07 accesses I/O port space of peripherals. Implement the LPC host controller driver which performs the I/O operations on the underlying hardware. We don't want to touch existing drivers such as ipmi-bt, so this driver applies the indirect-IO introduced in the previous patch after registering an indirect-IO node to the indirect-IO devices list which will be searched by the I/O accessors to retrieve the host-local I/O port. The driver config is set as a bool instead of a tristate. The reason here is that, by the very nature of the driver providing a logical PIO range, it does not make sense to have this driver as a loadable module. Another more specific reason is that the Huawei D03 board which includes Hip06 SoC requires the LPC bus for UART console, so should be built in. Tested-by: dann frazier Signed-off-by: Zou Rongrong Signed-off-by: Zhichang Yuan Signed-off-by: John Garry Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Acked-by: Rob Herring # dts part --- .../arm/hisilicon/hisilicon-low-pin-count.txt | 33 ++ drivers/bus/Kconfig | 8 + drivers/bus/Makefile | 1 + drivers/bus/hisi_lpc.c | 415 +++++++++++++++++++++ 4 files changed, 457 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt create mode 100644 drivers/bus/hisi_lpc.c diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt new file mode 100644 index 000000000000..10bd35f9207f --- /dev/null +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt @@ -0,0 +1,33 @@ +Hisilicon Hip06 Low Pin Count device + Hisilicon Hip06 SoCs implement a Low Pin Count (LPC) controller, which + provides I/O access to some legacy ISA devices. + Hip06 is based on arm64 architecture where there is no I/O space. So, the + I/O ports here are not CPU addresses, and there is no 'ranges' property in + LPC device node. + +Required properties: +- compatible: value should be as follows: + (a) "hisilicon,hip06-lpc" + (b) "hisilicon,hip07-lpc" +- #address-cells: must be 2 which stick to the ISA/EISA binding doc. +- #size-cells: must be 1 which stick to the ISA/EISA binding doc. +- reg: base memory range where the LPC register set is mapped. + +Note: + The node name before '@' must be "isa" to represent the binding stick to the + ISA/EISA binding specification. + +Example: + +isa@a01b0000 { + compatible = "hisilicon,hip06-lpc"; + #address-cells = <2>; + #size-cells = <1>; + reg = <0x0 0xa01b0000 0x0 0x1000>; + + ipmi0: bt@e4 { + compatible = "ipmi-bt"; + device_type = "ipmi"; + reg = <0x01 0xe4 0x04>; + }; +}; diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 57e011d36a79..a3fad0f0292f 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -65,6 +65,14 @@ config BRCMSTB_GISB_ARB arbiter. This driver provides timeout and target abort error handling and internal bus master decoding. +config HISILICON_LPC + bool "Support for ISA I/O space on HiSilicon Hip06/7" + depends on ARM64 && (ARCH_HISI || COMPILE_TEST) + select INDIRECT_PIO + help + Driver to enable I/O access to devices attached to the Low Pin + Count bus on the HiSilicon Hip06/7 SoC. + config IMX_WEIM bool "Freescale EIM DRIVER" depends on ARCH_MXC diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 9bcd0bf3954b..50bb12a971a0 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_ARM_CCI) += arm-cci.o obj-$(CONFIG_ARM_CCN) += arm-ccn.o +obj-$(CONFIG_HISILICON_LPC) += hisi_lpc.o obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o obj-$(CONFIG_IMX_WEIM) += imx-weim.o obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c new file mode 100644 index 000000000000..6123bff3215f --- /dev/null +++ b/drivers/bus/hisi_lpc.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved. + * Author: Zhichang Yuan + * Author: Zou Rongrong + * Author: John Garry + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "hisi-lpc" + +/* + * Setting this bit means each IO operation will target a different port + * address; 0 means repeated IO operations will use the same port, + * such as BT. + */ +#define FG_INCRADDR_LPC 0x02 + +struct lpc_cycle_para { + unsigned int opflags; + unsigned int csize; /* data length of each operation */ +}; + +struct hisi_lpc_dev { + spinlock_t cycle_lock; + void __iomem *membase; + struct logic_pio_hwaddr *io_host; +}; + +/* The max IO cycle counts supported is four per operation at maximum */ +#define LPC_MAX_DWIDTH 4 + +#define LPC_REG_STARTUP_SIGNAL 0x00 +#define LPC_REG_STARTUP_SIGNAL_START BIT(0) +#define LPC_REG_OP_STATUS 0x04 +#define LPC_REG_OP_STATUS_IDLE BIT(0) +#define LPC_REG_OP_STATUS_FINISHED BIT(1) +#define LPC_REG_OP_LEN 0x10 /* LPC cycles count per start */ +#define LPC_REG_CMD 0x14 +#define LPC_REG_CMD_OP BIT(0) /* 0: read, 1: write */ +#define LPC_REG_CMD_SAMEADDR BIT(3) +#define LPC_REG_ADDR 0x20 /* target address */ +#define LPC_REG_WDATA 0x24 /* write FIFO */ +#define LPC_REG_RDATA 0x28 /* read FIFO */ + +/* The minimal nanosecond interval for each query on LPC cycle status */ +#define LPC_NSEC_PERWAIT 100 + +/* + * The maximum waiting time is about 128us. It is specific for stream I/O, + * such as ins. + * + * The fastest IO cycle time is about 390ns, but the worst case will wait + * for extra 256 lpc clocks, so (256 + 13) * 30ns = 8 us. The maximum burst + * cycles is 16. So, the maximum waiting time is about 128us under worst + * case. + * + * Choose 1300 as the maximum. + */ +#define LPC_MAX_WAITCNT 1300 + +/* About 10us. This is specific for single IO operations, such as inb */ +#define LPC_PEROP_WAITCNT 100 + +static int wait_lpc_idle(unsigned char *mbase, unsigned int waitcnt) +{ + u32 status; + + do { + status = readl(mbase + LPC_REG_OP_STATUS); + if (status & LPC_REG_OP_STATUS_IDLE) + return (status & LPC_REG_OP_STATUS_FINISHED) ? 0 : -EIO; + ndelay(LPC_NSEC_PERWAIT); + } while (--waitcnt); + + return -ETIME; +} + +/* + * hisi_lpc_target_in - trigger a series of LPC cycles for read operation + * @lpcdev: pointer to hisi lpc device + * @para: some parameters used to control the lpc I/O operations + * @addr: the lpc I/O target port address + * @buf: where the read back data is stored + * @opcnt: how many I/O operations required, i.e. data width + * + * Returns 0 on success, non-zero on fail. + */ +static int hisi_lpc_target_in(struct hisi_lpc_dev *lpcdev, + struct lpc_cycle_para *para, unsigned long addr, + unsigned char *buf, unsigned long opcnt) +{ + unsigned int cmd_word; + unsigned int waitcnt; + unsigned long flags; + int ret; + + if (!buf || !opcnt || !para || !para->csize || !lpcdev) + return -EINVAL; + + cmd_word = 0; /* IO mode, Read */ + waitcnt = LPC_PEROP_WAITCNT; + if (!(para->opflags & FG_INCRADDR_LPC)) { + cmd_word |= LPC_REG_CMD_SAMEADDR; + waitcnt = LPC_MAX_WAITCNT; + } + + /* whole operation must be atomic */ + spin_lock_irqsave(&lpcdev->cycle_lock, flags); + + writel_relaxed(opcnt, lpcdev->membase + LPC_REG_OP_LEN); + writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD); + writel_relaxed(addr, lpcdev->membase + LPC_REG_ADDR); + + writel(LPC_REG_STARTUP_SIGNAL_START, + lpcdev->membase + LPC_REG_STARTUP_SIGNAL); + + /* whether the operation is finished */ + ret = wait_lpc_idle(lpcdev->membase, waitcnt); + if (ret) { + spin_unlock_irqrestore(&lpcdev->cycle_lock, flags); + return ret; + } + + readsb(lpcdev->membase + LPC_REG_RDATA, buf, opcnt); + + spin_unlock_irqrestore(&lpcdev->cycle_lock, flags); + + return 0; +} + +/* + * hisi_lpc_target_out - trigger a series of LPC cycles for write operation + * @lpcdev: pointer to hisi lpc device + * @para: some parameters used to control the lpc I/O operations + * @addr: the lpc I/O target port address + * @buf: where the data to be written is stored + * @opcnt: how many I/O operations required, i.e. data width + * + * Returns 0 on success, non-zero on fail. + */ +static int hisi_lpc_target_out(struct hisi_lpc_dev *lpcdev, + struct lpc_cycle_para *para, unsigned long addr, + const unsigned char *buf, unsigned long opcnt) +{ + unsigned int waitcnt; + unsigned long flags; + u32 cmd_word; + int ret; + + if (!buf || !opcnt || !para || !lpcdev) + return -EINVAL; + + /* default is increasing address */ + cmd_word = LPC_REG_CMD_OP; /* IO mode, write */ + waitcnt = LPC_PEROP_WAITCNT; + if (!(para->opflags & FG_INCRADDR_LPC)) { + cmd_word |= LPC_REG_CMD_SAMEADDR; + waitcnt = LPC_MAX_WAITCNT; + } + + spin_lock_irqsave(&lpcdev->cycle_lock, flags); + + writel_relaxed(opcnt, lpcdev->membase + LPC_REG_OP_LEN); + writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD); + writel_relaxed(addr, lpcdev->membase + LPC_REG_ADDR); + + writesb(lpcdev->membase + LPC_REG_WDATA, buf, opcnt); + + writel(LPC_REG_STARTUP_SIGNAL_START, + lpcdev->membase + LPC_REG_STARTUP_SIGNAL); + + /* whether the operation is finished */ + ret = wait_lpc_idle(lpcdev->membase, waitcnt); + + spin_unlock_irqrestore(&lpcdev->cycle_lock, flags); + + return ret; +} + +static unsigned long hisi_lpc_pio_to_addr(struct hisi_lpc_dev *lpcdev, + unsigned long pio) +{ + return pio - lpcdev->io_host->io_start + lpcdev->io_host->hw_start; +} + +/* + * hisi_lpc_comm_in - input the data in a single operation + * @hostdata: pointer to the device information relevant to LPC controller + * @pio: the target I/O port address + * @dwidth: the data length required to read from the target I/O port + * + * When success, data is returned. Otherwise, ~0 is returned. + */ +static u32 hisi_lpc_comm_in(void *hostdata, unsigned long pio, size_t dwidth) +{ + struct hisi_lpc_dev *lpcdev = hostdata; + struct lpc_cycle_para iopara; + unsigned long addr; + u32 rd_data = 0; + int ret; + + if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH) + return ~0; + + addr = hisi_lpc_pio_to_addr(lpcdev, pio); + + iopara.opflags = FG_INCRADDR_LPC; + iopara.csize = dwidth; + + ret = hisi_lpc_target_in(lpcdev, &iopara, addr, + (unsigned char *)&rd_data, dwidth); + if (ret) + return ~0; + + return le32_to_cpu(rd_data); +} + +/* + * hisi_lpc_comm_out - output the data in a single operation + * @hostdata: pointer to the device information relevant to LPC controller + * @pio: the target I/O port address + * @val: a value to be output from caller, maximum is four bytes + * @dwidth: the data width required writing to the target I/O port + * + * This function corresponds to out(b,w,l) only. + */ +static void hisi_lpc_comm_out(void *hostdata, unsigned long pio, + u32 val, size_t dwidth) +{ + struct hisi_lpc_dev *lpcdev = hostdata; + struct lpc_cycle_para iopara; + const unsigned char *buf; + unsigned long addr; + + if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH) + return; + + val = cpu_to_le32(val); + + buf = (const unsigned char *)&val; + addr = hisi_lpc_pio_to_addr(lpcdev, pio); + + iopara.opflags = FG_INCRADDR_LPC; + iopara.csize = dwidth; + + hisi_lpc_target_out(lpcdev, &iopara, addr, buf, dwidth); +} + +/* + * hisi_lpc_comm_ins - input the data in the buffer in multiple operations + * @hostdata: pointer to the device information relevant to LPC controller + * @pio: the target I/O port address + * @buffer: a buffer where read/input data bytes are stored + * @dwidth: the data width required writing to the target I/O port + * @count: how many data units whose length is dwidth will be read + * + * When success, the data read back is stored in buffer pointed by buffer. + * Returns 0 on success, -errno otherwise. + */ +static u32 hisi_lpc_comm_ins(void *hostdata, unsigned long pio, void *buffer, + size_t dwidth, unsigned int count) +{ + struct hisi_lpc_dev *lpcdev = hostdata; + unsigned char *buf = buffer; + struct lpc_cycle_para iopara; + unsigned long addr; + + if (!lpcdev || !buf || !count || !dwidth || dwidth > LPC_MAX_DWIDTH) + return -EINVAL; + + iopara.opflags = 0; + if (dwidth > 1) + iopara.opflags |= FG_INCRADDR_LPC; + iopara.csize = dwidth; + + addr = hisi_lpc_pio_to_addr(lpcdev, pio); + + do { + int ret; + + ret = hisi_lpc_target_in(lpcdev, &iopara, addr, buf, dwidth); + if (ret) + return ret; + buf += dwidth; + } while (--count); + + return 0; +} + +/* + * hisi_lpc_comm_outs - output the data in the buffer in multiple operations + * @hostdata: pointer to the device information relevant to LPC controller + * @pio: the target I/O port address + * @buffer: a buffer where write/output data bytes are stored + * @dwidth: the data width required writing to the target I/O port + * @count: how many data units whose length is dwidth will be written + */ +static void hisi_lpc_comm_outs(void *hostdata, unsigned long pio, + const void *buffer, size_t dwidth, + unsigned int count) +{ + struct hisi_lpc_dev *lpcdev = hostdata; + struct lpc_cycle_para iopara; + const unsigned char *buf = buffer; + unsigned long addr; + + if (!lpcdev || !buf || !count || !dwidth || dwidth > LPC_MAX_DWIDTH) + return; + + iopara.opflags = 0; + if (dwidth > 1) + iopara.opflags |= FG_INCRADDR_LPC; + iopara.csize = dwidth; + + addr = hisi_lpc_pio_to_addr(lpcdev, pio); + do { + if (hisi_lpc_target_out(lpcdev, &iopara, addr, buf, dwidth)) + break; + buf += dwidth; + } while (--count); +} + +static const struct logic_pio_host_ops hisi_lpc_ops = { + .in = hisi_lpc_comm_in, + .out = hisi_lpc_comm_out, + .ins = hisi_lpc_comm_ins, + .outs = hisi_lpc_comm_outs, +}; + +/* + * hisi_lpc_probe - the probe callback function for hisi lpc host, + * will finish all the initialization. + * @pdev: the platform device corresponding to hisi lpc host + * + * Returns 0 on success, non-zero on fail. + */ +static int hisi_lpc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct acpi_device *acpi_device = ACPI_COMPANION(dev); + struct logic_pio_hwaddr *range; + struct hisi_lpc_dev *lpcdev; + resource_size_t io_end; + struct resource *res; + int ret; + + lpcdev = devm_kzalloc(dev, sizeof(*lpcdev), GFP_KERNEL); + if (!lpcdev) + return -ENOMEM; + + spin_lock_init(&lpcdev->cycle_lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + lpcdev->membase = devm_ioremap_resource(dev, res); + if (IS_ERR(lpcdev->membase)) + return PTR_ERR(lpcdev->membase); + + range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL); + if (!range) + return -ENOMEM; + + range->fwnode = dev->fwnode; + range->flags = LOGIC_PIO_INDIRECT; + range->size = PIO_INDIRECT_SIZE; + + ret = logic_pio_register_range(range); + if (ret) { + dev_err(dev, "register IO range failed (%d)!\n", ret); + return ret; + } + lpcdev->io_host = range; + + /* register the LPC host PIO resources */ + if (!acpi_device) { + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + if (ret) + return ret; + } + + lpcdev->io_host->hostdata = lpcdev; + lpcdev->io_host->ops = &hisi_lpc_ops; + + io_end = lpcdev->io_host->io_start + lpcdev->io_host->size; + dev_info(dev, "registered range [%pa - %pa]\n", + &lpcdev->io_host->io_start, &io_end); + + return ret; +} + +static const struct of_device_id hisi_lpc_of_match[] = { + { .compatible = "hisilicon,hip06-lpc", }, + { .compatible = "hisilicon,hip07-lpc", }, + {} +}; + +static struct platform_driver hisi_lpc_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = hisi_lpc_of_match, + }, + .probe = hisi_lpc_probe, +}; +builtin_platform_driver(hisi_lpc_driver); -- cgit v1.2.3 From d87fb0917a073d71300b2b31b3773f6690bd1712 Mon Sep 17 00:00:00 2001 From: John Garry Date: Thu, 15 Mar 2018 02:15:56 +0800 Subject: ACPI / scan: Rename acpi_is_serial_bus_slave() for more general use Currently the ACPI scan has special handling for serial bus slaves, in that it makes it the responsibility of the slave device's parent to enumerate the device. To support other types of slave devices which require the same special handling but where the bus is not strictly a serial bus, such as devices on the HiSilicon LPC controller bus, rename acpi_is_serial_bus_slave() to acpi_device_enumeration_by_parent(), so that the name can fit the wider purpose. Also rename the associated device flag acpi_device_flags.serial_bus_slave to .enumeration_by_parent. Signed-off-by: John Garry Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 19 ++++++++++--------- include/acpi/acpi_bus.h | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8e63d937babb..f9e790479059 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1524,7 +1524,7 @@ static int acpi_check_serial_bus_slave(struct acpi_resource *ares, void *data) return -1; } -static bool acpi_is_serial_bus_slave(struct acpi_device *device) +static bool acpi_device_enumeration_by_parent(struct acpi_device *device) { struct list_head resource_list; bool is_serial_bus_slave = false; @@ -1560,7 +1560,8 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, acpi_bus_get_flags(device); device->flags.match_driver = false; device->flags.initialized = true; - device->flags.serial_bus_slave = acpi_is_serial_bus_slave(device); + device->flags.enumeration_by_parent = + acpi_device_enumeration_by_parent(device); acpi_device_clear_enumerated(device); device_initialize(&device->dev); dev_set_uevent_suppress(&device->dev, true); @@ -1858,10 +1859,10 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, static void acpi_default_enumeration(struct acpi_device *device) { /* - * Do not enumerate SPI/I2C/UART slaves as they will be enumerated by - * their respective parents. + * Do not enumerate devices with enumeration_by_parent flag set as + * they will be enumerated by their respective parents. */ - if (!device->flags.serial_bus_slave) { + if (!device->flags.enumeration_by_parent) { acpi_create_platform_device(device, NULL); acpi_device_set_enumerated(device); } else { @@ -1958,7 +1959,7 @@ static void acpi_bus_attach(struct acpi_device *device) return; device->flags.match_driver = true; - if (ret > 0 && !device->flags.serial_bus_slave) { + if (ret > 0 && !device->flags.enumeration_by_parent) { acpi_device_set_enumerated(device); goto ok; } @@ -1967,10 +1968,10 @@ static void acpi_bus_attach(struct acpi_device *device) if (ret < 0) return; - if (!device->pnp.type.platform_id && !device->flags.serial_bus_slave) - acpi_device_set_enumerated(device); - else + if (device->pnp.type.platform_id || device->flags.enumeration_by_parent) acpi_default_enumeration(device); + else + acpi_device_set_enumerated(device); ok: list_for_each_entry(child, &device->children, node) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index c9608b0b80c6..ba4dd54f2c82 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -215,7 +215,7 @@ struct acpi_device_flags { u32 of_compatible_ok:1; u32 coherent_dma:1; u32 cca_seen:1; - u32 serial_bus_slave:1; + u32 enumeration_by_parent:1; u32 reserved:19; }; -- cgit v1.2.3 From dfda4492322ed0a1eb9c4d4715c4b90c9af57352 Mon Sep 17 00:00:00 2001 From: John Garry Date: Thu, 15 Mar 2018 02:15:57 +0800 Subject: ACPI / scan: Do not enumerate Indirect IO host children Through the logical PIO framework, systems which otherwise have no IO space access to legacy ISA/LPC devices may access these devices through so-called "indirect IO" method. In this, IO space accesses for non-PCI hosts are redirected to a host LLDD to manually generate the IO space (bus) accesses. Hosts are able to register a region in logical PIO space to map to its bus address range. Indirect IO child devices have an associated host-specific bus address. Special translation is required to map between a logical PIO address for a device and its host bus address. Since in the ACPI tables the child device IO resources would be the host-specific values, it is required the ACPI scan code should not enumerate these devices, and that this should be the responsibility of the host driver so that it can "fixup" the resources so that they map to the appropriate logical PIO addresses. To avoid enumerating these child devices, add a check from acpi_device_enumeration_by_parent() as to whether the parent for a device is a member of a known list of "indirect IO" hosts. For now, the HiSilicon LPC host controller ID is added. Tested-by: dann frazier Signed-off-by: John Garry Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Acked-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index f9e790479059..a4cbf3efc809 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1524,11 +1524,25 @@ static int acpi_check_serial_bus_slave(struct acpi_resource *ares, void *data) return -1; } +static bool acpi_is_indirect_io_slave(struct acpi_device *device) +{ + struct acpi_device *parent = device->parent; + const struct acpi_device_id indirect_io_hosts[] = { + {"HISI0191", 0}, + {} + }; + + return parent && !acpi_match_device_ids(parent, indirect_io_hosts); +} + static bool acpi_device_enumeration_by_parent(struct acpi_device *device) { struct list_head resource_list; bool is_serial_bus_slave = false; + if (acpi_is_indirect_io_slave(device)) + return true; + /* Macs use device properties in lieu of _CRS resources */ if (x86_apple_machine && (fwnode_property_present(&device->fwnode, "spiSclkPeriod") || -- cgit v1.2.3 From e0aa1563f8945d9b8f472426d100bed190a4308f Mon Sep 17 00:00:00 2001 From: John Garry Date: Thu, 15 Mar 2018 02:15:58 +0800 Subject: HISI LPC: Add ACPI support Based on the previous patches, this patch supports the LPC host on Hip06/Hip07 for ACPI FW. It is the responsibility of the LPC host driver to enumerate the child devices, as the ACPI scan code will not enumerate children of "indirect IO" hosts. The ACPI table for the LPC host controller and the child devices is in the following format: Device (LPC0) { Name (_HID, "HISI0191") // HiSi LPC Name (_CRS, ResourceTemplate () { Memory32Fixed (ReadWrite, 0xa01b0000, 0x1000) }) } Device (LPC0.IPMI) { Name (_HID, "IPI0001") Name (LORS, ResourceTemplate() { QWordIO ( ResourceConsumer, MinNotFixed, // _MIF MaxNotFixed, // _MAF PosDecode, EntireRange, 0x0, // _GRA 0xe4, // _MIN 0x3fff, // _MAX 0x0, // _TRA 0x04, // _LEN , , BTIO ) }) Since the IO resources of the child devices need to be translated from LPC bus addresses to logical PIO addresses, and we shouldn't modify the resources of the devices generated in the FW scan, a per-child MFD is created as a substitute. The MFD IO resources will be the translated bus addresses of the ACPI child. Tested-by: dann frazier Signed-off-by: John Garry Signed-off-by: Zhichang Yuan Signed-off-by: Gabriele Paoloni Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko --- drivers/bus/hisi_lpc.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 204 insertions(+), 4 deletions(-) diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c index 6123bff3215f..2d4611e4c339 100644 --- a/drivers/bus/hisi_lpc.c +++ b/drivers/bus/hisi_lpc.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -339,6 +340,203 @@ static const struct logic_pio_host_ops hisi_lpc_ops = { .outs = hisi_lpc_comm_outs, }; +#ifdef CONFIG_ACPI +#define MFD_CHILD_NAME_PREFIX DRV_NAME"-" +#define MFD_CHILD_NAME_LEN (ACPI_ID_LEN + sizeof(MFD_CHILD_NAME_PREFIX) - 1) + +struct hisi_lpc_mfd_cell { + struct mfd_cell_acpi_match acpi_match; + char name[MFD_CHILD_NAME_LEN]; + char pnpid[ACPI_ID_LEN]; +}; + +static int hisi_lpc_acpi_xlat_io_res(struct acpi_device *adev, + struct acpi_device *host, + struct resource *res) +{ + unsigned long sys_port; + resource_size_t len = resource_size(res); + + sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len); + if (sys_port == ~0UL) + return -EFAULT; + + res->start = sys_port; + res->end = sys_port + len; + + return 0; +} + +/* + * hisi_lpc_acpi_set_io_res - set the resources for a child's MFD + * @child: the device node to be updated the I/O resource + * @hostdev: the device node associated with host controller + * @res: double pointer to be set to the address of translated resources + * @num_res: pointer to variable to hold the number of translated resources + * + * Returns 0 when successful, and a negative value for failure. + * + * For a given host controller, each child device will have an associated + * host-relative address resource. This function will return the translated + * logical PIO addresses for each child devices resources. + */ +static int hisi_lpc_acpi_set_io_res(struct device *child, + struct device *hostdev, + const struct resource **res, int *num_res) +{ + struct acpi_device *adev; + struct acpi_device *host; + struct resource_entry *rentry; + LIST_HEAD(resource_list); + struct resource *resources; + int count; + int i; + + if (!child || !hostdev) + return -EINVAL; + + host = to_acpi_device(hostdev); + adev = to_acpi_device(child); + + if (!adev->status.present) { + dev_dbg(child, "device is not present\n"); + return -EIO; + } + + if (acpi_device_enumerated(adev)) { + dev_dbg(child, "has been enumerated\n"); + return -EIO; + } + + /* + * The following code segment to retrieve the resources is common to + * acpi_create_platform_device(), so consider a common helper function + * in future. + */ + count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); + if (count <= 0) { + dev_dbg(child, "failed to get resources\n"); + return count ? count : -EIO; + } + + resources = devm_kcalloc(hostdev, count, sizeof(*resources), + GFP_KERNEL); + if (!resources) { + dev_warn(hostdev, "could not allocate memory for %d resources\n", + count); + acpi_dev_free_resource_list(&resource_list); + return -ENOMEM; + } + count = 0; + list_for_each_entry(rentry, &resource_list, node) + resources[count++] = *rentry->res; + + acpi_dev_free_resource_list(&resource_list); + + /* translate the I/O resources */ + for (i = 0; i < count; i++) { + int ret; + + if (!(resources[i].flags & IORESOURCE_IO)) + continue; + ret = hisi_lpc_acpi_xlat_io_res(adev, host, &resources[i]); + if (ret) { + dev_err(child, "translate IO range %pR failed (%d)\n", + &resources[i], ret); + return ret; + } + } + *res = resources; + *num_res = count; + + return 0; +} + +/* + * hisi_lpc_acpi_probe - probe children for ACPI FW + * @hostdev: LPC host device pointer + * + * Returns 0 when successful, and a negative value for failure. + * + * Scan all child devices and create a per-device MFD with + * logical PIO translated IO resources. + */ +static int hisi_lpc_acpi_probe(struct device *hostdev) +{ + struct acpi_device *adev = ACPI_COMPANION(hostdev); + struct hisi_lpc_mfd_cell *hisi_lpc_mfd_cells; + struct mfd_cell *mfd_cells; + struct acpi_device *child; + int size, ret, count = 0, cell_num = 0; + + list_for_each_entry(child, &adev->children, node) + cell_num++; + + /* allocate the mfd cell and companion ACPI info, one per child */ + size = sizeof(*mfd_cells) + sizeof(*hisi_lpc_mfd_cells); + mfd_cells = devm_kcalloc(hostdev, cell_num, size, GFP_KERNEL); + if (!mfd_cells) + return -ENOMEM; + + hisi_lpc_mfd_cells = (struct hisi_lpc_mfd_cell *)&mfd_cells[cell_num]; + /* Only consider the children of the host */ + list_for_each_entry(child, &adev->children, node) { + struct mfd_cell *mfd_cell = &mfd_cells[count]; + struct hisi_lpc_mfd_cell *hisi_lpc_mfd_cell = + &hisi_lpc_mfd_cells[count]; + struct mfd_cell_acpi_match *acpi_match = + &hisi_lpc_mfd_cell->acpi_match; + char *name = hisi_lpc_mfd_cell[count].name; + char *pnpid = hisi_lpc_mfd_cell[count].pnpid; + struct mfd_cell_acpi_match match = { + .pnpid = pnpid, + }; + + /* + * For any instances of this host controller (Hip06 and Hip07 + * are the only chipsets), we would not have multiple slaves + * with the same HID. And in any system we would have just one + * controller active. So don't worrry about MFD name clashes. + */ + snprintf(name, MFD_CHILD_NAME_LEN, MFD_CHILD_NAME_PREFIX"%s", + acpi_device_hid(child)); + snprintf(pnpid, ACPI_ID_LEN, "%s", acpi_device_hid(child)); + + memcpy(acpi_match, &match, sizeof(*acpi_match)); + mfd_cell->name = name; + mfd_cell->acpi_match = acpi_match; + + ret = hisi_lpc_acpi_set_io_res(&child->dev, &adev->dev, + &mfd_cell->resources, + &mfd_cell->num_resources); + if (ret) { + dev_warn(&child->dev, "set resource fail (%d)\n", ret); + return ret; + } + count++; + } + + ret = mfd_add_devices(hostdev, PLATFORM_DEVID_NONE, + mfd_cells, cell_num, NULL, 0, NULL); + if (ret) { + dev_err(hostdev, "failed to add mfd cells (%d)\n", ret); + return ret; + } + + return 0; +} + +static const struct acpi_device_id hisi_lpc_acpi_match[] = { + {"HISI0191"}, + {} +}; +#else +static int hisi_lpc_acpi_probe(struct device *dev) +{ + return -ENODEV; +} +#endif // CONFIG_ACPI + /* * hisi_lpc_probe - the probe callback function for hisi lpc host, * will finish all the initialization. @@ -383,11 +581,12 @@ static int hisi_lpc_probe(struct platform_device *pdev) lpcdev->io_host = range; /* register the LPC host PIO resources */ - if (!acpi_device) { + if (acpi_device) + ret = hisi_lpc_acpi_probe(dev); + else ret = of_platform_populate(dev->of_node, NULL, NULL, dev); - if (ret) - return ret; - } + if (ret) + return ret; lpcdev->io_host->hostdata = lpcdev; lpcdev->io_host->ops = &hisi_lpc_ops; @@ -409,6 +608,7 @@ static struct platform_driver hisi_lpc_driver = { .driver = { .name = DRV_NAME, .of_match_table = hisi_lpc_of_match, + .acpi_match_table = ACPI_PTR(hisi_lpc_acpi_match), }, .probe = hisi_lpc_probe, }; -- cgit v1.2.3 From 6183d9b3ce7979eda940e594a5c342171a5da998 Mon Sep 17 00:00:00 2001 From: John Garry Date: Thu, 15 Mar 2018 02:15:59 +0800 Subject: MAINTAINERS: Add John Garry as maintainer for HiSilicon LPC driver Add John Garry as maintainer for drivers/bus/hisi_lpc.c, the HiSilicon LPC driver. Signed-off-by: John Garry Signed-off-by: Bjorn Helgaas --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 3bdc260e36b7..39eedce23186 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6386,6 +6386,13 @@ W: http://www.hisilicon.com S: Maintained F: drivers/net/ethernet/hisilicon/hns3/ +HISILICON LPC BUS DRIVER +M: john.garry@huawei.com +W: http://www.hisilicon.com +S: Maintained +F: drivers/bus/hisi_lpc.c +F: Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt + HISILICON NETWORK SUBSYSTEM DRIVER M: Yisen Zhuang M: Salil Mehta -- cgit v1.2.3