From e54ea45a4955e8a2901c74ab43870f2a56ed647b Mon Sep 17 00:00:00 2001 From: Dilip Kota Date: Mon, 9 Dec 2019 11:20:04 +0800 Subject: [PATCH 01/60] dt-bindings: PCI: intel: Add YAML schemas for the PCIe RC controller Add YAML schemas for PCIe RC controller on Intel Gateway SoCs which is Synopsys DesignWare based PCIe core. Signed-off-by: Dilip Kota Signed-off-by: Lorenzo Pieralisi Reviewed-by: Andrew Murray Reviewed-by: Rob Herring --- .../bindings/pci/intel-gw-pcie.yaml | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml diff --git a/Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml b/Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml new file mode 100644 index 000000000000..db605d8a387d --- /dev/null +++ b/Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml @@ -0,0 +1,138 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/intel-gw-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: PCIe RC controller on Intel Gateway SoCs + +maintainers: + - Dilip Kota + +properties: + compatible: + items: + - const: intel,lgm-pcie + - const: snps,dw-pcie + + device_type: + const: pci + + "#address-cells": + const: 3 + + "#size-cells": + const: 2 + + reg: + items: + - description: Controller control and status registers. + - description: PCIe configuration registers. + - description: Controller application registers. + + reg-names: + items: + - const: dbi + - const: config + - const: app + + ranges: + maxItems: 1 + + resets: + maxItems: 1 + + clocks: + maxItems: 1 + + phys: + maxItems: 1 + + phy-names: + const: pcie + + reset-gpios: + maxItems: 1 + + linux,pci-domain: true + + num-lanes: + maximum: 2 + description: Number of lanes to use for this port. + + '#interrupt-cells': + const: 1 + + interrupt-map-mask: + description: Standard PCI IRQ mapping properties. + + interrupt-map: + description: Standard PCI IRQ mapping properties. + + max-link-speed: + description: Specify PCI Gen for link capability. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [ 1, 2, 3, 4 ] + - default: 1 + + bus-range: + description: Range of bus numbers associated with this controller. + + reset-assert-ms: + description: | + Delay after asserting reset to the PCIe device. + maximum: 500 + default: 100 + +required: + - compatible + - device_type + - "#address-cells" + - "#size-cells" + - reg + - reg-names + - ranges + - resets + - clocks + - phys + - phy-names + - reset-gpios + - '#interrupt-cells' + - interrupt-map + - interrupt-map-mask + +additionalProperties: false + +examples: + - | + #include + #include + pcie10: pcie@d0e00000 { + compatible = "intel,lgm-pcie", "snps,dw-pcie"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + reg = <0xd0e00000 0x1000>, + <0xd2000000 0x800000>, + <0xd0a41000 0x1000>; + reg-names = "dbi", "config", "app"; + linux,pci-domain = <0>; + max-link-speed = <4>; + bus-range = <0x00 0x08>; + interrupt-parent = <&ioapic1>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &ioapic1 27 1>, + <0 0 0 2 &ioapic1 28 1>, + <0 0 0 3 &ioapic1 29 1>, + <0 0 0 4 &ioapic1 30 1>; + ranges = <0x02000000 0 0xd4000000 0xd4000000 0 0x04000000>; + resets = <&rcu0 0x50 0>; + clocks = <&cgu0 LGM_GCLK_PCIE10>; + phys = <&cb0phy0>; + phy-names = "pcie"; + reset-assert-ms = <500>; + reset-gpios = <&gpio0 3 GPIO_ACTIVE_LOW>; + num-lanes = <2>; + }; From 8c386cc817878588195dde38e919aa6ba9409d58 Mon Sep 17 00:00:00 2001 From: Navid Emamdoost Date: Mon, 25 Nov 2019 13:52:52 -0600 Subject: [PATCH 02/60] PCI/IOV: Fix memory leak in pci_iov_add_virtfn() In the implementation of pci_iov_add_virtfn() the allocated virtfn is leaked if pci_setup_device() fails. The error handling is not calling pci_stop_and_remove_bus_device(). Change the goto label to failed2. Fixes: 156c55325d30 ("PCI: Check for pci_setup_device() failure in pci_iov_add_virtfn()") Link: https://lore.kernel.org/r/20191125195255.23740-1-navid.emamdoost@gmail.com Signed-off-by: Navid Emamdoost Signed-off-by: Bjorn Helgaas --- drivers/pci/iov.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 1e88fd427757..4d1f392b05f9 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -186,10 +186,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 failed2; + goto failed1; rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn"); if (rc) - goto failed3; + goto failed2; kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE); @@ -197,11 +197,10 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id) return 0; -failed3: - sysfs_remove_link(&dev->dev.kobj, buf); failed2: - pci_stop_and_remove_bus_device(virtfn); + sysfs_remove_link(&dev->dev.kobj, buf); failed1: + pci_stop_and_remove_bus_device(virtfn); pci_dev_put(dev); failed0: virtfn_remove_bus(dev->bus, bus); From bc123a515cb76c432293a6c4b4765b5ec0c813cf Mon Sep 17 00:00:00 2001 From: Armen Baloyan Date: Fri, 6 Dec 2019 13:52:45 -0800 Subject: [PATCH 03/60] PCI/P2PDMA: Add Intel SkyLake-E to the whitelist Intel SkyLake-E was successfully tested for p2pdma transactions spanning over a host bridge and PCI bridge with IOMMU on. Add it to the whitelist. Link: https://lore.kernel.org/r/1575669165-31697-1-git-send-email-abaloyan@gigaio.com Signed-off-by: Armen Baloyan Signed-off-by: Bjorn Helgaas Reviewed-by: Logan Gunthorpe --- drivers/pci/p2pdma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 0608aae72ccc..9a8a38384121 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -289,6 +289,9 @@ static const struct pci_p2pdma_whitelist_entry { /* Intel Xeon E7 v3/Xeon E5 v3/Core i7 */ {PCI_VENDOR_ID_INTEL, 0x2f00, REQ_SAME_HOST_BRIDGE}, {PCI_VENDOR_ID_INTEL, 0x2f01, REQ_SAME_HOST_BRIDGE}, + /* Intel SkyLake-E */ + {PCI_VENDOR_ID_INTEL, 0x2030, 0}, + {PCI_VENDOR_ID_INTEL, 0x2020, 0}, {} }; From 574f29036fce385e28617547955dd6911d375025 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Wed, 11 Dec 2019 17:45:11 +0000 Subject: [PATCH 04/60] PCI: iproc: Apply quirk_paxc_bridge() for module as well as built-in Previously quirk_paxc_bridge() was applied when the iproc driver was built-in, but not when it was compiled as a module. This happened because it was under #ifdef CONFIG_PCIE_IPROC_PLATFORM: PCIE_IPROC_PLATFORM=y causes CONFIG_PCIE_IPROC_PLATFORM to be defined, but PCIE_IPROC_PLATFORM=m causes CONFIG_PCIE_IPROC_PLATFORM_MODULE to be defined. Move quirk_paxc_bridge() to pcie-iproc.c and drop the #ifdef so the quirk is always applied, whether iproc is built-in or a module. [bhelgaas: commit log, move to pcie-iproc.c, not pcie-iproc-platform.c] Link: https://lore.kernel.org/r/20191211174511.89713-1-wei.liu@kernel.org Signed-off-by: Wei Liu Signed-off-by: Bjorn Helgaas --- drivers/pci/controller/pcie-iproc.c | 24 ++++++++++++++++++++++++ drivers/pci/quirks.c | 26 -------------------------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index 0a468c73bae3..8c7f875acf7f 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -1588,6 +1588,30 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802, DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804, quirk_paxc_disable_msi_parsing); +static void quirk_paxc_bridge(struct pci_dev *pdev) +{ + /* + * The PCI config space is shared with the PAXC root port and the first + * Ethernet device. So, we need to workaround this by telling the PCI + * code that the bridge is not an Ethernet device. + */ + if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) + pdev->class = PCI_CLASS_BRIDGE_PCI << 8; + + /* + * MPSS is not being set properly (as it is currently 0). This is + * because that area of the PCI config space is hard coded to zero, and + * is not modifiable by firmware. Set this to 2 (e.g., 512 byte MPS) + * so that the MPS can be set to the real max value. + */ + pdev->pcie_mpss = 2; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16cd, quirk_paxc_bridge); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, quirk_paxc_bridge); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd750, quirk_paxc_bridge); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802, quirk_paxc_bridge); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804, quirk_paxc_bridge); + MODULE_AUTHOR("Ray Jui "); MODULE_DESCRIPTION("Broadcom iPROC PCIe common driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 4937a088d7d8..202837ed68c9 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2381,32 +2381,6 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5719, quirk_brcm_5719_limit_mrrs); -#ifdef CONFIG_PCIE_IPROC_PLATFORM -static void quirk_paxc_bridge(struct pci_dev *pdev) -{ - /* - * The PCI config space is shared with the PAXC root port and the first - * Ethernet device. So, we need to workaround this by telling the PCI - * code that the bridge is not an Ethernet device. - */ - if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) - pdev->class = PCI_CLASS_BRIDGE_PCI << 8; - - /* - * MPSS is not being set properly (as it is currently 0). This is - * because that area of the PCI config space is hard coded to zero, and - * is not modifiable by firmware. Set this to 2 (e.g., 512 byte MPS) - * so that the MPS can be set to the real max value. - */ - pdev->pcie_mpss = 2; -} -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16cd, quirk_paxc_bridge); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, quirk_paxc_bridge); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd750, quirk_paxc_bridge); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802, quirk_paxc_bridge); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804, quirk_paxc_bridge); -#endif - /* * Originally in EDAC sources for i82875P: Intel tells BIOS developers to * hide device 6 which configures the overflow device access containing the From 62fe23df067715a21c4aef44068efe7ceaa8f627 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Wed, 27 Nov 2019 13:38:35 +0800 Subject: [PATCH 05/60] PCI: Add generic quirk for increasing D3hot delay Separate the D3 delay increase functionality out of quirk_radeon_pm() into its own function so that it can be shared with other quirks, including the AMD Ryzen XHCI quirk that will be introduced in a followup commit. Tweak the function name and message to indicate more clearly that the delay relates to a D3hot-to-D0 transition. Link: https://lore.kernel.org/r/20191127053836.31624-1-drake@endlessm.com Signed-off-by: Daniel Drake Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg --- drivers/pci/quirks.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 202837ed68c9..92e08dd7d692 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1871,16 +1871,21 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2609, quirk_intel_pcie_pm); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260a, quirk_intel_pcie_pm); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260b, quirk_intel_pcie_pm); +static void quirk_d3hot_delay(struct pci_dev *dev, unsigned int delay) +{ + if (dev->d3_delay >= delay) + return; + + dev->d3_delay = delay; + pci_info(dev, "extending delay after power-on from D3hot to %d msec\n", + dev->d3_delay); +} + static void quirk_radeon_pm(struct pci_dev *dev) { if (dev->subsystem_vendor == PCI_VENDOR_ID_APPLE && - dev->subsystem_device == 0x00e2) { - if (dev->d3_delay < 20) { - dev->d3_delay = 20; - pci_info(dev, "extending delay after power-on from D3 to %d msec\n", - dev->d3_delay); - } - } + dev->subsystem_device == 0x00e2) + quirk_d3hot_delay(dev, 20); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x6741, quirk_radeon_pm); From 3030df209aa8cf831b9963829bd9f94900ee8032 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Wed, 27 Nov 2019 13:38:36 +0800 Subject: [PATCH 06/60] PCI: Increase D3 delay for AMD Ryzen5/7 XHCI controllers On Asus UX434DA (AMD Ryzen7 3700U) and Asus X512DK (AMD Ryzen5 3500U), the XHCI controller fails to resume from runtime suspend or s2idle, and USB becomes unusable from that point. xhci_hcd 0000:03:00.4: Refused to change power state, currently in D3 xhci_hcd 0000:03:00.4: enabling device (0000 -> 0002) xhci_hcd 0000:03:00.4: WARN: xHC restore state timeout xhci_hcd 0000:03:00.4: PCI post-resume error -110! xhci_hcd 0000:03:00.4: HC died; cleaning up During suspend, a transition to D3cold is attempted, however the affected platforms do not seem to cut the power to the PCI device when in this state, so the device stays in D3hot. Upon resume, the D3hot-to-D0 transition is successful only if the D3 delay is increased to 20ms. The transition failure does not appear to be detectable as a CRS condition. Add a PCI quirk to increase the delay on the affected hardware. Link: https://bugzilla.kernel.org/show_bug.cgi?id=205587 Link: http://lkml.kernel.org/r/CAD8Lp47Vh69gQjROYG69=waJgL7hs1PwnLonL9+27S_TcRhixA@mail.gmail.com Link: https://lore.kernel.org/r/20191127053836.31624-2-drake@endlessm.com Signed-off-by: Daniel Drake Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg --- drivers/pci/quirks.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 92e08dd7d692..1ca383f7ece6 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1889,6 +1889,22 @@ static void quirk_radeon_pm(struct pci_dev *dev) } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x6741, quirk_radeon_pm); +/* + * Ryzen5/7 XHCI controllers fail upon resume from runtime suspend or s2idle. + * https://bugzilla.kernel.org/show_bug.cgi?id=205587 + * + * The kernel attempts to transition these devices to D3cold, but that seems + * to be ineffective on the platforms in question; the PCI device appears to + * remain on in D3hot state. The D3hot-to-D0 transition then requires an + * extended delay in order to succeed. + */ +static void quirk_ryzen_xhci_d3hot(struct pci_dev *dev) +{ + quirk_d3hot_delay(dev, 20); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x15e0, quirk_ryzen_xhci_d3hot); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x15e1, quirk_ryzen_xhci_d3hot); + #ifdef CONFIG_X86_IO_APIC static int dmi_disable_ioapicreroute(const struct dmi_system_id *d) { From f8bf2aeb651b3460a4b36fd7ba1ba1d31777d35c Mon Sep 17 00:00:00 2001 From: James Sewart Date: Tue, 10 Dec 2019 15:51:33 -0600 Subject: [PATCH 07/60] PCI: Fix pci_add_dma_alias() bitmask size The number of possible devfns is 256, but pci_add_dma_alias() allocated a bitmap of size 255. Fix this off-by-one error. This fixes commits 338c3149a221 ("PCI: Add support for multiple DMA aliases") and c6635792737b ("PCI: Allocate dma_alias_mask with bitmap_zalloc()"), but I doubt it was possible to see a problem because it takes 4 64-bit longs (or 8 32-bit longs) to hold 255 bits, and bitmap_zalloc() doesn't save the 255-bit size anywhere. [bhelgaas: commit log, move #define to drivers/pci/pci.h, include loop limit fix from Qian Cai: https://lore.kernel.org/r/20191218170004.5297-1-cai@lca.pw] Signed-off-by: James Sewart Signed-off-by: Bjorn Helgaas Reviewed-by: Logan Gunthorpe --- drivers/pci/pci.c | 2 +- drivers/pci/pci.h | 3 +++ drivers/pci/search.c | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e87196cc1a7f..7b5fa2eabe09 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -6017,7 +6017,7 @@ EXPORT_SYMBOL_GPL(pci_pr3_present); void pci_add_dma_alias(struct pci_dev *dev, u8 devfn) { if (!dev->dma_alias_mask) - dev->dma_alias_mask = bitmap_zalloc(U8_MAX, GFP_KERNEL); + dev->dma_alias_mask = bitmap_zalloc(MAX_NR_DEVFNS, GFP_KERNEL); if (!dev->dma_alias_mask) { pci_warn(dev, "Unable to allocate DMA alias mask\n"); return; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index a0a53bd05a0b..6394e7746fb5 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -4,6 +4,9 @@ #include +/* Number of possible devfns: 0.0 to 1f.7 inclusive */ +#define MAX_NR_DEVFNS 256 + #define PCI_FIND_CAP_TTL 48 #define PCI_VSEC_ID_INTEL_TBT 0x1234 /* Thunderbolt */ diff --git a/drivers/pci/search.c b/drivers/pci/search.c index bade14002fd8..e4dbdef5aef0 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -41,9 +41,9 @@ int pci_for_each_dma_alias(struct pci_dev *pdev, * DMA, iterate over that too. */ if (unlikely(pdev->dma_alias_mask)) { - u8 devfn; + unsigned int devfn; - for_each_set_bit(devfn, pdev->dma_alias_mask, U8_MAX) { + for_each_set_bit(devfn, pdev->dma_alias_mask, MAX_NR_DEVFNS) { ret = fn(pdev, PCI_DEVID(pdev->bus->number, devfn), data); if (ret) From 09298542cd891b43778db1f65aa3613aa5a562eb Mon Sep 17 00:00:00 2001 From: James Sewart Date: Tue, 10 Dec 2019 16:07:30 -0600 Subject: [PATCH 08/60] PCI: Add nr_devfns parameter to pci_add_dma_alias() Add a "nr_devfns" parameter to pci_add_dma_alias() so it can be used to create DMA aliases for a range of devfns. [bhelgaas: incorporate nr_devfns fix from James, update quirk_pex_vca_alias() and setup_aliases()] Signed-off-by: James Sewart Signed-off-by: Bjorn Helgaas --- drivers/iommu/amd_iommu.c | 7 ++----- drivers/pci/pci.c | 22 +++++++++++++++++----- drivers/pci/quirks.c | 23 +++++++++-------------- include/linux/pci.h | 2 +- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index bd25674ee4db..7a6c056b9b9c 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -230,11 +230,8 @@ static struct pci_dev *setup_aliases(struct device *dev) */ ivrs_alias = amd_iommu_alias_table[pci_dev_id(pdev)]; if (ivrs_alias != pci_dev_id(pdev) && - PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) { - pci_add_dma_alias(pdev, ivrs_alias & 0xff); - pci_info(pdev, "Added PCI DMA alias %02x.%d\n", - PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias)); - } + PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) + pci_add_dma_alias(pdev, ivrs_alias & 0xff, 1); clone_aliases(pdev); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7b5fa2eabe09..951099279192 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5998,7 +5998,8 @@ EXPORT_SYMBOL_GPL(pci_pr3_present); /** * pci_add_dma_alias - Add a DMA devfn alias for a device * @dev: the PCI device for which alias is added - * @devfn: alias slot and function + * @devfn_from: alias slot and function + * @nr_devfns: number of subsequent devfns to alias * * This helper encodes an 8-bit devfn as a bit number in dma_alias_mask * which is used to program permissible bus-devfn source addresses for DMA @@ -6014,8 +6015,13 @@ EXPORT_SYMBOL_GPL(pci_pr3_present); * cannot be left as a userspace activity). DMA aliases should therefore * be configured via quirks, such as the PCI fixup header quirk. */ -void pci_add_dma_alias(struct pci_dev *dev, u8 devfn) +void pci_add_dma_alias(struct pci_dev *dev, u8 devfn_from, unsigned nr_devfns) { + int devfn_to; + + nr_devfns = min(nr_devfns, (unsigned) MAX_NR_DEVFNS - devfn_from); + devfn_to = devfn_from + nr_devfns - 1; + if (!dev->dma_alias_mask) dev->dma_alias_mask = bitmap_zalloc(MAX_NR_DEVFNS, GFP_KERNEL); if (!dev->dma_alias_mask) { @@ -6023,9 +6029,15 @@ void pci_add_dma_alias(struct pci_dev *dev, u8 devfn) return; } - set_bit(devfn, dev->dma_alias_mask); - pci_info(dev, "Enabling fixed DMA alias to %02x.%d\n", - PCI_SLOT(devfn), PCI_FUNC(devfn)); + bitmap_set(dev->dma_alias_mask, devfn_from, nr_devfns); + + if (nr_devfns == 1) + pci_info(dev, "Enabling fixed DMA alias to %02x.%d\n", + PCI_SLOT(devfn_from), PCI_FUNC(devfn_from)); + else if (nr_devfns > 1) + pci_info(dev, "Enabling fixed DMA alias for devfn range from %02x.%d to %02x.%d\n", + PCI_SLOT(devfn_from), PCI_FUNC(devfn_from), + PCI_SLOT(devfn_to), PCI_FUNC(devfn_to)); } bool pci_devs_are_dma_aliases(struct pci_dev *dev1, struct pci_dev *dev2) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 4937a088d7d8..2fcad18622eb 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3932,7 +3932,7 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe) static void quirk_dma_func0_alias(struct pci_dev *dev) { if (PCI_FUNC(dev->devfn) != 0) - pci_add_dma_alias(dev, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + pci_add_dma_alias(dev, PCI_DEVFN(PCI_SLOT(dev->devfn), 0), 1); } /* @@ -3946,7 +3946,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RICOH, 0xe476, quirk_dma_func0_alias); static void quirk_dma_func1_alias(struct pci_dev *dev) { if (PCI_FUNC(dev->devfn) != 1) - pci_add_dma_alias(dev, PCI_DEVFN(PCI_SLOT(dev->devfn), 1)); + pci_add_dma_alias(dev, PCI_DEVFN(PCI_SLOT(dev->devfn), 1), 1); } /* @@ -4031,7 +4031,7 @@ static void quirk_fixed_dma_alias(struct pci_dev *dev) id = pci_match_id(fixed_dma_alias_tbl, dev); if (id) - pci_add_dma_alias(dev, id->driver_data); + pci_add_dma_alias(dev, id->driver_data, 1); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ADAPTEC2, 0x0285, quirk_fixed_dma_alias); @@ -4072,9 +4072,9 @@ DECLARE_PCI_FIXUP_HEADER(0x8086, 0x244e, quirk_use_pcie_bridge_dma_alias); */ static void quirk_mic_x200_dma_alias(struct pci_dev *pdev) { - pci_add_dma_alias(pdev, PCI_DEVFN(0x10, 0x0)); - pci_add_dma_alias(pdev, PCI_DEVFN(0x11, 0x0)); - pci_add_dma_alias(pdev, PCI_DEVFN(0x12, 0x3)); + pci_add_dma_alias(pdev, PCI_DEVFN(0x10, 0x0), 1); + pci_add_dma_alias(pdev, PCI_DEVFN(0x11, 0x0), 1); + pci_add_dma_alias(pdev, PCI_DEVFN(0x12, 0x3), 1); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2260, quirk_mic_x200_dma_alias); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2264, quirk_mic_x200_dma_alias); @@ -4098,13 +4098,8 @@ static void quirk_pex_vca_alias(struct pci_dev *pdev) const unsigned int num_pci_slots = 0x20; unsigned int slot; - for (slot = 0; slot < num_pci_slots; slot++) { - pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x0)); - pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x1)); - pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x2)); - pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x3)); - pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x4)); - } + for (slot = 0; slot < num_pci_slots; slot++) + pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x0), 5); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2954, quirk_pex_vca_alias); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2955, quirk_pex_vca_alias); @@ -5332,7 +5327,7 @@ static void quirk_switchtec_ntb_dma_alias(struct pci_dev *pdev) pci_dbg(pdev, "Aliasing Partition %d Proxy ID %02x.%d\n", pp, PCI_SLOT(devfn), PCI_FUNC(devfn)); - pci_add_dma_alias(pdev, devfn); + pci_add_dma_alias(pdev, devfn, 1); } } diff --git a/include/linux/pci.h b/include/linux/pci.h index c393dff2d66f..930fab293073 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2310,7 +2310,7 @@ static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev) } #endif -void pci_add_dma_alias(struct pci_dev *dev, u8 devfn); +void pci_add_dma_alias(struct pci_dev *dev, u8 devfn_from, unsigned nr_devfns); bool pci_devs_are_dma_aliases(struct pci_dev *dev1, struct pci_dev *dev2); int pci_for_each_dma_alias(struct pci_dev *pdev, int (*fn)(struct pci_dev *pdev, From 7b90dfc4873b87c468cc6046538f46a531c1d785 Mon Sep 17 00:00:00 2001 From: James Sewart Date: Tue, 10 Dec 2019 16:25:40 -0600 Subject: [PATCH 09/60] PCI: Add DMA alias quirk for PLX PEX NTB The PLX PEX NTB forwards DMA transactions using Requester IDs that don't exist as PCI devices. The devfn for a transaction is used as an index into a lookup table storing the origin of a transaction on the other side of the bridge. Alias all possible devfns to the NTB device so that any transaction coming in is governed by the mappings for the NTB. Signed-off-by: James Sewart Signed-off-by: Bjorn Helgaas Reviewed-by: Logan Gunthorpe --- drivers/pci/quirks.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2fcad18622eb..e0b014157288 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5369,6 +5369,21 @@ SWITCHTEC_QUIRK(0x8574); /* PFXI 64XG3 */ SWITCHTEC_QUIRK(0x8575); /* PFXI 80XG3 */ SWITCHTEC_QUIRK(0x8576); /* PFXI 96XG3 */ +/* + * The PLX NTB uses devfn proxy IDs to move TLPs between NT endpoints. + * These IDs are used to forward responses to the originator on the other + * side of the NTB. Alias all possible IDs to the NTB to permit access when + * the IOMMU is turned on. + */ +static void quirk_plx_ntb_dma_alias(struct pci_dev *pdev) +{ + pci_info(pdev, "Setting PLX NTB proxy ID aliases\n"); + /* PLX NTB may use all 256 devfns */ + pci_add_dma_alias(pdev, 0, 256); +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PLX, 0x87b0, quirk_plx_ntb_dma_alias); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PLX, 0x87b1, quirk_plx_ntb_dma_alias); + /* * On Lenovo Thinkpad P50 SKUs with a Nvidia Quadro M1000M, the BIOS does * not always reset the secondary Nvidia GPU between reboots if the system From 7730c3be06e255082077594f60aae77f9914c5ad Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Mon, 30 Dec 2019 21:14:28 +0800 Subject: [PATCH 10/60] Documentation: PCI: Fix pci_alloc_irq_vectors() function name typo Documentation/PCI/msi-howto.rst referred to pci_irq_alloc_vectors() when it should refer to pci_alloc_irq_vectors(). Fix the typo. Link: https://lore.kernel.org/r/20191230131428.1200-1-yuzenghui@huawei.com Signed-off-by: Zenghui Yu Signed-off-by: Bjorn Helgaas Reviewed-by: Andrew Murray --- Documentation/PCI/msi-howto.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/PCI/msi-howto.rst b/Documentation/PCI/msi-howto.rst index 994cbb660ade..aa2046af69f7 100644 --- a/Documentation/PCI/msi-howto.rst +++ b/Documentation/PCI/msi-howto.rst @@ -283,5 +283,5 @@ or disabled (0). If 0 is found in any of the msi_bus files belonging to bridges between the PCI root and the device, MSIs are disabled. It is also worth checking the device driver to see whether it supports MSIs. -For example, it may contain calls to pci_irq_alloc_vectors() with the +For example, it may contain calls to pci_alloc_irq_vectors() with the PCI_IRQ_MSI or PCI_IRQ_MSIX flags. From aa82130a22f77c1aa5794703730304d035a0c1f4 Mon Sep 17 00:00:00 2001 From: Wesley Sheng Date: Mon, 6 Jan 2020 12:03:26 -0700 Subject: [PATCH 11/60] PCI/switchtec: Use dma_set_mask_and_coherent() Use dma_set_mask_and_coherent() instead of dma_set_coherent_mask() as the Switchtec hardware fully supports 64bit addressing and we should set both the streaming and coherent masks the same. [logang@deltatee.com: reworked commit message] Fixes: aff614c6339c ("switchtec: Set DMA coherent mask") Link: https://lore.kernel.org/r/20200106190337.2428-2-logang@deltatee.com Signed-off-by: Wesley Sheng Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 88091bbfe77f..2bf670977b9c 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -1349,7 +1349,7 @@ static int switchtec_init_pci(struct switchtec_dev *stdev, if (rc) return rc; - rc = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)); + rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); if (rc) return rc; From 9375646b4cf03aee81bc6c305aa18cc80b682796 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Mon, 6 Jan 2020 12:03:27 -0700 Subject: [PATCH 12/60] PCI/switchtec: Fix vep_vector_number ioread width vep_vector_number is actually a 16 bit register which should be read with ioread16() instead of ioread32(). Fixes: 080b47def5e5 ("MicroSemi Switchtec management interface driver") Link: https://lore.kernel.org/r/20200106190337.2428-3-logang@deltatee.com Reported-by: Doug Meyer Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 2bf670977b9c..9c3ad09d3022 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -1276,7 +1276,7 @@ static int switchtec_init_isr(struct switchtec_dev *stdev) if (nvecs < 0) return nvecs; - event_irq = ioread32(&stdev->mmio_part_cfg->vep_vector_number); + event_irq = ioread16(&stdev->mmio_part_cfg->vep_vector_number); if (event_irq < 0 || event_irq >= nvecs) return -EFAULT; From ed22aaaede44f647477a5048e62855c0ed49c9bd Mon Sep 17 00:00:00 2001 From: Dilip Kota Date: Mon, 9 Dec 2019 11:20:05 +0800 Subject: [PATCH 13/60] PCI: dwc: intel: PCIe RC controller driver Add support to PCIe RC controller on Intel Gateway SoCs. PCIe controller is based of Synopsys DesignWare PCIe core. Intel PCIe driver requires Upconfigure support, Fast Training Sequence and link speed configurations. So adding the respective helper functions in the PCIe DesignWare framework. It also programs hardware autonomous speed during speed configuration so defining it in pci_regs.h. Also, mark Intel PCIe driver depends on MSI IRQ Domain as Synopsys DesignWare framework depends on the PCI_MSI_IRQ_DOMAIN. Signed-off-by: Dilip Kota Signed-off-by: Lorenzo Pieralisi Reviewed-by: Andrew Murray Reviewed-by: Andy Shevchenko Acked-by: Gustavo Pimentel --- drivers/pci/controller/dwc/Kconfig | 11 + drivers/pci/controller/dwc/Makefile | 1 + drivers/pci/controller/dwc/pcie-designware.c | 56 ++ drivers/pci/controller/dwc/pcie-designware.h | 12 + drivers/pci/controller/dwc/pcie-intel-gw.c | 545 +++++++++++++++++++ include/uapi/linux/pci_regs.h | 1 + 6 files changed, 626 insertions(+) create mode 100644 drivers/pci/controller/dwc/pcie-intel-gw.c diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 625a031b2193..0830dfcfa43a 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -209,6 +209,17 @@ config PCIE_ARTPEC6_EP Enables support for the PCIe controller in the ARTPEC-6 SoC to work in endpoint mode. This uses the DesignWare core. +config PCIE_INTEL_GW + bool "Intel Gateway PCIe host controller support" + depends on OF && (X86 || COMPILE_TEST) + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Say 'Y' here to enable PCIe Host controller support on Intel + Gateway SoCs. + The PCIe controller uses the DesignWare core plus Intel-specific + hardware wrappers. + config PCIE_KIRIN depends on OF && (ARM64 || COMPILE_TEST) bool "HiSilicon Kirin series SoCs PCIe controllers" diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 69faff371f11..8a637cfcf6e9 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_PCI_LAYERSCAPE_EP) += pci-layerscape-ep.o obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o +obj-$(CONFIG_PCIE_INTEL_GW) += pcie-intel-gw.o obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o obj-$(CONFIG_PCI_MESON) += pci-meson.o diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 820488dfeaed..681548c88282 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -12,6 +12,7 @@ #include #include +#include "../../pci.h" #include "pcie-designware.h" /* @@ -474,6 +475,61 @@ int dw_pcie_link_up(struct dw_pcie *pci) (!(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING))); } +void dw_pcie_upconfig_setup(struct dw_pcie *pci) +{ + u32 val; + + val = dw_pcie_readl_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL); + val |= PORT_MLTI_UPCFG_SUPPORT; + dw_pcie_writel_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL, val); +} +EXPORT_SYMBOL_GPL(dw_pcie_upconfig_setup); + +void dw_pcie_link_set_max_speed(struct dw_pcie *pci, u32 link_gen) +{ + u32 reg, val; + u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + + reg = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCTL2); + reg &= ~PCI_EXP_LNKCTL2_TLS; + + switch (pcie_link_speed[link_gen]) { + case PCIE_SPEED_2_5GT: + reg |= PCI_EXP_LNKCTL2_TLS_2_5GT; + break; + case PCIE_SPEED_5_0GT: + reg |= PCI_EXP_LNKCTL2_TLS_5_0GT; + break; + case PCIE_SPEED_8_0GT: + reg |= PCI_EXP_LNKCTL2_TLS_8_0GT; + break; + case PCIE_SPEED_16_0GT: + reg |= PCI_EXP_LNKCTL2_TLS_16_0GT; + break; + default: + /* Use hardware capability */ + val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP); + val = FIELD_GET(PCI_EXP_LNKCAP_SLS, val); + reg &= ~PCI_EXP_LNKCTL2_HASD; + reg |= FIELD_PREP(PCI_EXP_LNKCTL2_TLS, val); + break; + } + + dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCTL2, reg); +} +EXPORT_SYMBOL_GPL(dw_pcie_link_set_max_speed); + +void dw_pcie_link_set_n_fts(struct dw_pcie *pci, u32 n_fts) +{ + u32 val; + + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + val &= ~PORT_LOGIC_N_FTS_MASK; + val |= n_fts & PORT_LOGIC_N_FTS_MASK; + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); +} +EXPORT_SYMBOL_GPL(dw_pcie_link_set_n_fts); + static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci) { u32 val; diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 5accdd6bc388..a22ea5982817 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -30,7 +30,12 @@ #define LINK_WAIT_IATU 9 /* Synopsys-specific PCIe configuration registers */ +#define PCIE_PORT_AFR 0x70C +#define PORT_AFR_N_FTS_MASK GENMASK(15, 8) +#define PORT_AFR_CC_N_FTS_MASK GENMASK(23, 16) + #define PCIE_PORT_LINK_CONTROL 0x710 +#define PORT_LINK_DLL_LINK_EN BIT(5) #define PORT_LINK_MODE_MASK GENMASK(21, 16) #define PORT_LINK_MODE(n) FIELD_PREP(PORT_LINK_MODE_MASK, n) #define PORT_LINK_MODE_1_LANES PORT_LINK_MODE(0x1) @@ -46,6 +51,7 @@ #define PCIE_PORT_DEBUG1_LINK_IN_TRAINING BIT(29) #define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C +#define PORT_LOGIC_N_FTS_MASK GENMASK(7, 0) #define PORT_LOGIC_SPEED_CHANGE BIT(17) #define PORT_LOGIC_LINK_WIDTH_MASK GENMASK(12, 8) #define PORT_LOGIC_LINK_WIDTH(n) FIELD_PREP(PORT_LOGIC_LINK_WIDTH_MASK, n) @@ -60,6 +66,9 @@ #define PCIE_MSI_INTR0_MASK 0x82C #define PCIE_MSI_INTR0_STATUS 0x830 +#define PCIE_PORT_MULTI_LANE_CTRL 0x8C0 +#define PORT_MLTI_UPCFG_SUPPORT BIT(7) + #define PCIE_ATU_VIEWPORT 0x900 #define PCIE_ATU_REGION_INBOUND BIT(31) #define PCIE_ATU_REGION_OUTBOUND 0 @@ -273,6 +282,9 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val); u32 dw_pcie_read_atu(struct dw_pcie *pci, u32 reg, size_t size); void dw_pcie_write_atu(struct dw_pcie *pci, u32 reg, size_t size, u32 val); int dw_pcie_link_up(struct dw_pcie *pci); +void dw_pcie_upconfig_setup(struct dw_pcie *pci); +void dw_pcie_link_set_max_speed(struct dw_pcie *pci, u32 link_gen); +void dw_pcie_link_set_n_fts(struct dw_pcie *pci, u32 n_fts); int dw_pcie_wait_for_link(struct dw_pcie *pci); void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type, u64 cpu_addr, u64 pci_addr, diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c new file mode 100644 index 000000000000..fc2a12212dec --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -0,0 +1,545 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Intel Gateway SoCs + * + * Copyright (c) 2019 Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../pci.h" +#include "pcie-designware.h" + +#define PORT_AFR_N_FTS_GEN12_DFT (SZ_128 - 1) +#define PORT_AFR_N_FTS_GEN3 180 +#define PORT_AFR_N_FTS_GEN4 196 + +/* PCIe Application logic Registers */ +#define PCIE_APP_CCR 0x10 +#define PCIE_APP_CCR_LTSSM_ENABLE BIT(0) + +#define PCIE_APP_MSG_CR 0x30 +#define PCIE_APP_MSG_XMT_PM_TURNOFF BIT(0) + +#define PCIE_APP_PMC 0x44 +#define PCIE_APP_PMC_IN_L2 BIT(20) + +#define PCIE_APP_IRNEN 0xF4 +#define PCIE_APP_IRNCR 0xF8 +#define PCIE_APP_IRN_AER_REPORT BIT(0) +#define PCIE_APP_IRN_PME BIT(2) +#define PCIE_APP_IRN_RX_VDM_MSG BIT(4) +#define PCIE_APP_IRN_PM_TO_ACK BIT(9) +#define PCIE_APP_IRN_LINK_AUTO_BW_STAT BIT(11) +#define PCIE_APP_IRN_BW_MGT BIT(12) +#define PCIE_APP_IRN_MSG_LTR BIT(18) +#define PCIE_APP_IRN_SYS_ERR_RC BIT(29) +#define PCIE_APP_INTX_OFST 12 + +#define PCIE_APP_IRN_INT \ + (PCIE_APP_IRN_AER_REPORT | PCIE_APP_IRN_PME | \ + PCIE_APP_IRN_RX_VDM_MSG | PCIE_APP_IRN_SYS_ERR_RC | \ + PCIE_APP_IRN_PM_TO_ACK | PCIE_APP_IRN_MSG_LTR | \ + PCIE_APP_IRN_BW_MGT | PCIE_APP_IRN_LINK_AUTO_BW_STAT | \ + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTA) | \ + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTB) | \ + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTC) | \ + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTD)) + +#define BUS_IATU_OFFSET SZ_256M +#define RESET_INTERVAL_MS 100 + +struct intel_pcie_soc { + unsigned int pcie_ver; + unsigned int pcie_atu_offset; + u32 num_viewport; +}; + +struct intel_pcie_port { + struct dw_pcie pci; + void __iomem *app_base; + struct gpio_desc *reset_gpio; + u32 rst_intrvl; + u32 max_speed; + u32 link_gen; + u32 max_width; + u32 n_fts; + struct clk *core_clk; + struct reset_control *core_rst; + struct phy *phy; + u8 pcie_cap_ofst; +}; + +static void pcie_update_bits(void __iomem *base, u32 ofs, u32 mask, u32 val) +{ + u32 old; + + old = readl(base + ofs); + val = (old & ~mask) | (val & mask); + + if (val != old) + writel(val, base + ofs); +} + +static inline u32 pcie_app_rd(struct intel_pcie_port *lpp, u32 ofs) +{ + return readl(lpp->app_base + ofs); +} + +static inline void pcie_app_wr(struct intel_pcie_port *lpp, u32 ofs, u32 val) +{ + writel(val, lpp->app_base + ofs); +} + +static void pcie_app_wr_mask(struct intel_pcie_port *lpp, u32 ofs, + u32 mask, u32 val) +{ + pcie_update_bits(lpp->app_base, ofs, mask, val); +} + +static inline u32 pcie_rc_cfg_rd(struct intel_pcie_port *lpp, u32 ofs) +{ + return dw_pcie_readl_dbi(&lpp->pci, ofs); +} + +static inline void pcie_rc_cfg_wr(struct intel_pcie_port *lpp, u32 ofs, u32 val) +{ + dw_pcie_writel_dbi(&lpp->pci, ofs, val); +} + +static void pcie_rc_cfg_wr_mask(struct intel_pcie_port *lpp, u32 ofs, + u32 mask, u32 val) +{ + pcie_update_bits(lpp->pci.dbi_base, ofs, mask, val); +} + +static void intel_pcie_ltssm_enable(struct intel_pcie_port *lpp) +{ + pcie_app_wr_mask(lpp, PCIE_APP_CCR, PCIE_APP_CCR_LTSSM_ENABLE, + PCIE_APP_CCR_LTSSM_ENABLE); +} + +static void intel_pcie_ltssm_disable(struct intel_pcie_port *lpp) +{ + pcie_app_wr_mask(lpp, PCIE_APP_CCR, PCIE_APP_CCR_LTSSM_ENABLE, 0); +} + +static void intel_pcie_link_setup(struct intel_pcie_port *lpp) +{ + u32 val; + u8 offset = lpp->pcie_cap_ofst; + + val = pcie_rc_cfg_rd(lpp, offset + PCI_EXP_LNKCAP); + lpp->max_speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, val); + lpp->max_width = FIELD_GET(PCI_EXP_LNKCAP_MLW, val); + + val = pcie_rc_cfg_rd(lpp, offset + PCI_EXP_LNKCTL); + + val &= ~(PCI_EXP_LNKCTL_LD | PCI_EXP_LNKCTL_ASPMC); + pcie_rc_cfg_wr(lpp, offset + PCI_EXP_LNKCTL, val); +} + +static void intel_pcie_port_logic_setup(struct intel_pcie_port *lpp) +{ + u32 val, mask; + + switch (pcie_link_speed[lpp->max_speed]) { + case PCIE_SPEED_8_0GT: + lpp->n_fts = PORT_AFR_N_FTS_GEN3; + break; + case PCIE_SPEED_16_0GT: + lpp->n_fts = PORT_AFR_N_FTS_GEN4; + break; + default: + lpp->n_fts = PORT_AFR_N_FTS_GEN12_DFT; + break; + } + + mask = PORT_AFR_N_FTS_MASK | PORT_AFR_CC_N_FTS_MASK; + val = FIELD_PREP(PORT_AFR_N_FTS_MASK, lpp->n_fts) | + FIELD_PREP(PORT_AFR_CC_N_FTS_MASK, lpp->n_fts); + pcie_rc_cfg_wr_mask(lpp, PCIE_PORT_AFR, mask, val); + + /* Port Link Control Register */ + pcie_rc_cfg_wr_mask(lpp, PCIE_PORT_LINK_CONTROL, PORT_LINK_DLL_LINK_EN, + PORT_LINK_DLL_LINK_EN); +} + +static void intel_pcie_rc_setup(struct intel_pcie_port *lpp) +{ + intel_pcie_ltssm_disable(lpp); + intel_pcie_link_setup(lpp); + dw_pcie_setup_rc(&lpp->pci.pp); + dw_pcie_upconfig_setup(&lpp->pci); + intel_pcie_port_logic_setup(lpp); + dw_pcie_link_set_max_speed(&lpp->pci, lpp->link_gen); + dw_pcie_link_set_n_fts(&lpp->pci, lpp->n_fts); +} + +static int intel_pcie_ep_rst_init(struct intel_pcie_port *lpp) +{ + struct device *dev = lpp->pci.dev; + int ret; + + lpp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(lpp->reset_gpio)) { + ret = PTR_ERR(lpp->reset_gpio); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to request PCIe GPIO: %d\n", ret); + return ret; + } + + /* Make initial reset last for 100us */ + usleep_range(100, 200); + + return 0; +} + +static void intel_pcie_core_rst_assert(struct intel_pcie_port *lpp) +{ + reset_control_assert(lpp->core_rst); +} + +static void intel_pcie_core_rst_deassert(struct intel_pcie_port *lpp) +{ + /* + * One micro-second delay to make sure the reset pulse + * wide enough so that core reset is clean. + */ + udelay(1); + reset_control_deassert(lpp->core_rst); + + /* + * Some SoC core reset also reset PHY, more delay needed + * to make sure the reset process is done. + */ + usleep_range(1000, 2000); +} + +static void intel_pcie_device_rst_assert(struct intel_pcie_port *lpp) +{ + gpiod_set_value_cansleep(lpp->reset_gpio, 1); +} + +static void intel_pcie_device_rst_deassert(struct intel_pcie_port *lpp) +{ + msleep(lpp->rst_intrvl); + gpiod_set_value_cansleep(lpp->reset_gpio, 0); +} + +static int intel_pcie_app_logic_setup(struct intel_pcie_port *lpp) +{ + intel_pcie_device_rst_deassert(lpp); + intel_pcie_ltssm_enable(lpp); + + return dw_pcie_wait_for_link(&lpp->pci); +} + +static void intel_pcie_core_irq_disable(struct intel_pcie_port *lpp) +{ + pcie_app_wr(lpp, PCIE_APP_IRNEN, 0); + pcie_app_wr(lpp, PCIE_APP_IRNCR, PCIE_APP_IRN_INT); +} + +static int intel_pcie_get_resources(struct platform_device *pdev) +{ + struct intel_pcie_port *lpp = platform_get_drvdata(pdev); + struct dw_pcie *pci = &lpp->pci; + struct device *dev = pci->dev; + struct resource *res; + int ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + pci->dbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + lpp->core_clk = devm_clk_get(dev, NULL); + if (IS_ERR(lpp->core_clk)) { + ret = PTR_ERR(lpp->core_clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get clks: %d\n", ret); + return ret; + } + + lpp->core_rst = devm_reset_control_get(dev, NULL); + if (IS_ERR(lpp->core_rst)) { + ret = PTR_ERR(lpp->core_rst); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get resets: %d\n", ret); + return ret; + } + + ret = device_property_match_string(dev, "device_type", "pci"); + if (ret) { + dev_err(dev, "Failed to find pci device type: %d\n", ret); + return ret; + } + + ret = device_property_read_u32(dev, "reset-assert-ms", + &lpp->rst_intrvl); + if (ret) + lpp->rst_intrvl = RESET_INTERVAL_MS; + + ret = of_pci_get_max_link_speed(dev->of_node); + lpp->link_gen = ret < 0 ? 0 : ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "app"); + lpp->app_base = devm_ioremap_resource(dev, res); + if (IS_ERR(lpp->app_base)) + return PTR_ERR(lpp->app_base); + + lpp->phy = devm_phy_get(dev, "pcie"); + if (IS_ERR(lpp->phy)) { + ret = PTR_ERR(lpp->phy); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Couldn't get pcie-phy: %d\n", ret); + return ret; + } + + return 0; +} + +static void intel_pcie_deinit_phy(struct intel_pcie_port *lpp) +{ + phy_exit(lpp->phy); +} + +static int intel_pcie_wait_l2(struct intel_pcie_port *lpp) +{ + u32 value; + int ret; + + if (pcie_link_speed[lpp->max_speed] < PCIE_SPEED_8_0GT) + return 0; + + /* Send PME_TURN_OFF message */ + pcie_app_wr_mask(lpp, PCIE_APP_MSG_CR, PCIE_APP_MSG_XMT_PM_TURNOFF, + PCIE_APP_MSG_XMT_PM_TURNOFF); + + /* Read PMC status and wait for falling into L2 link state */ + ret = readl_poll_timeout(lpp->app_base + PCIE_APP_PMC, value, + value & PCIE_APP_PMC_IN_L2, 20, + jiffies_to_usecs(5 * HZ)); + if (ret) + dev_err(lpp->pci.dev, "PCIe link enter L2 timeout!\n"); + + return ret; +} + +static void intel_pcie_turn_off(struct intel_pcie_port *lpp) +{ + if (dw_pcie_link_up(&lpp->pci)) + intel_pcie_wait_l2(lpp); + + /* Put endpoint device in reset state */ + intel_pcie_device_rst_assert(lpp); + pcie_rc_cfg_wr_mask(lpp, PCI_COMMAND, PCI_COMMAND_MEMORY, 0); +} + +static int intel_pcie_host_setup(struct intel_pcie_port *lpp) +{ + struct device *dev = lpp->pci.dev; + int ret; + + intel_pcie_core_rst_assert(lpp); + intel_pcie_device_rst_assert(lpp); + + ret = phy_init(lpp->phy); + if (ret) + return ret; + + intel_pcie_core_rst_deassert(lpp); + + ret = clk_prepare_enable(lpp->core_clk); + if (ret) { + dev_err(lpp->pci.dev, "Core clock enable failed: %d\n", ret); + goto clk_err; + } + + if (!lpp->pcie_cap_ofst) { + ret = dw_pcie_find_capability(&lpp->pci, PCI_CAP_ID_EXP); + if (!ret) { + ret = -ENXIO; + dev_err(dev, "Invalid PCIe capability offset\n"); + goto app_init_err; + } + + lpp->pcie_cap_ofst = ret; + } + + intel_pcie_rc_setup(lpp); + ret = intel_pcie_app_logic_setup(lpp); + if (ret) + goto app_init_err; + + /* Enable integrated interrupts */ + pcie_app_wr_mask(lpp, PCIE_APP_IRNEN, PCIE_APP_IRN_INT, + PCIE_APP_IRN_INT); + + return 0; + +app_init_err: + clk_disable_unprepare(lpp->core_clk); +clk_err: + intel_pcie_core_rst_assert(lpp); + intel_pcie_deinit_phy(lpp); + + return ret; +} + +static void __intel_pcie_remove(struct intel_pcie_port *lpp) +{ + intel_pcie_core_irq_disable(lpp); + intel_pcie_turn_off(lpp); + clk_disable_unprepare(lpp->core_clk); + intel_pcie_core_rst_assert(lpp); + intel_pcie_deinit_phy(lpp); +} + +static int intel_pcie_remove(struct platform_device *pdev) +{ + struct intel_pcie_port *lpp = platform_get_drvdata(pdev); + struct pcie_port *pp = &lpp->pci.pp; + + dw_pcie_host_deinit(pp); + __intel_pcie_remove(lpp); + + return 0; +} + +static int __maybe_unused intel_pcie_suspend_noirq(struct device *dev) +{ + struct intel_pcie_port *lpp = dev_get_drvdata(dev); + int ret; + + intel_pcie_core_irq_disable(lpp); + ret = intel_pcie_wait_l2(lpp); + if (ret) + return ret; + + intel_pcie_deinit_phy(lpp); + clk_disable_unprepare(lpp->core_clk); + return ret; +} + +static int __maybe_unused intel_pcie_resume_noirq(struct device *dev) +{ + struct intel_pcie_port *lpp = dev_get_drvdata(dev); + + return intel_pcie_host_setup(lpp); +} + +static int intel_pcie_rc_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct intel_pcie_port *lpp = dev_get_drvdata(pci->dev); + + return intel_pcie_host_setup(lpp); +} + +/* + * Dummy function so that DW core doesn't configure MSI + */ +static int intel_pcie_msi_init(struct pcie_port *pp) +{ + return 0; +} + +u64 intel_pcie_cpu_addr(struct dw_pcie *pcie, u64 cpu_addr) +{ + return cpu_addr + BUS_IATU_OFFSET; +} + +static const struct dw_pcie_ops intel_pcie_ops = { + .cpu_addr_fixup = intel_pcie_cpu_addr, +}; + +static const struct dw_pcie_host_ops intel_pcie_dw_ops = { + .host_init = intel_pcie_rc_init, + .msi_host_init = intel_pcie_msi_init, +}; + +static const struct intel_pcie_soc pcie_data = { + .pcie_ver = 0x520A, + .pcie_atu_offset = 0xC0000, + .num_viewport = 3, +}; + +static int intel_pcie_probe(struct platform_device *pdev) +{ + const struct intel_pcie_soc *data; + struct device *dev = &pdev->dev; + struct intel_pcie_port *lpp; + struct pcie_port *pp; + struct dw_pcie *pci; + int ret; + + lpp = devm_kzalloc(dev, sizeof(*lpp), GFP_KERNEL); + if (!lpp) + return -ENOMEM; + + platform_set_drvdata(pdev, lpp); + pci = &lpp->pci; + pci->dev = dev; + pp = &pci->pp; + + ret = intel_pcie_get_resources(pdev); + if (ret) + return ret; + + ret = intel_pcie_ep_rst_init(lpp); + if (ret) + return ret; + + data = device_get_match_data(dev); + if (!data) + return -ENODEV; + + pci->ops = &intel_pcie_ops; + pci->version = data->pcie_ver; + pci->atu_base = pci->dbi_base + data->pcie_atu_offset; + pp->ops = &intel_pcie_dw_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "Cannot initialize host\n"); + return ret; + } + + /* + * Intel PCIe doesn't configure IO region, so set viewport + * to not perform IO region access. + */ + pci->num_viewport = data->num_viewport; + + return 0; +} + +static const struct dev_pm_ops intel_pcie_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(intel_pcie_suspend_noirq, + intel_pcie_resume_noirq) +}; + +static const struct of_device_id of_intel_pcie_match[] = { + { .compatible = "intel,lgm-pcie", .data = &pcie_data }, + {} +}; + +static struct platform_driver intel_pcie_driver = { + .probe = intel_pcie_probe, + .remove = intel_pcie_remove, + .driver = { + .name = "intel-gw-pcie", + .of_match_table = of_intel_pcie_match, + .pm = &intel_pcie_pm_ops, + }, +}; +builtin_platform_driver(intel_pcie_driver); diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index acb7d2bdb419..5437690483cd 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -676,6 +676,7 @@ #define PCI_EXP_LNKCTL2_TLS_32_0GT 0x0005 /* Supported Speed 32GT/s */ #define PCI_EXP_LNKCTL2_ENTER_COMP 0x0010 /* Enter Compliance */ #define PCI_EXP_LNKCTL2_TX_MARGIN 0x0380 /* Transmit Margin */ +#define PCI_EXP_LNKCTL2_HASD 0x0020 /* HW Autonomous Speed Disable */ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */ #define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */ From 6fd622c226e6d8387bc5340bfb2f2ca7ef28da07 Mon Sep 17 00:00:00 2001 From: Dilip Kota Date: Mon, 9 Dec 2019 11:20:06 +0800 Subject: [PATCH 14/60] PCI: artpec6: Configure FTS with dwc helper function Use DesignWare helper functions to configure Fast Training Sequence. Drop the respective code in the driver. Signed-off-by: Dilip Kota Signed-off-by: Lorenzo Pieralisi Reviewed-by: Andrew Murray --- drivers/pci/controller/dwc/pcie-artpec6.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index 9e2482bd7b6d..28d5a1095200 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -51,9 +51,6 @@ static const struct of_device_id artpec6_pcie_of_match[]; #define ACK_N_FTS_MASK GENMASK(15, 8) #define ACK_N_FTS(x) (((x) << 8) & ACK_N_FTS_MASK) -#define FAST_TRAINING_SEQ_MASK GENMASK(7, 0) -#define FAST_TRAINING_SEQ(x) (((x) << 0) & FAST_TRAINING_SEQ_MASK) - /* ARTPEC-6 specific registers */ #define PCIECFG 0x18 #define PCIECFG_DBG_OEN BIT(24) @@ -313,10 +310,7 @@ static void artpec6_pcie_set_nfts(struct artpec6_pcie *artpec6_pcie) * Set the Number of Fast Training Sequences that the core * advertises as its N_FTS during Gen2 or Gen3 link training. */ - val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); - val &= ~FAST_TRAINING_SEQ_MASK; - val |= FAST_TRAINING_SEQ(180); - dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); + dw_pcie_link_set_n_fts(pci, 180); } static void artpec6_pcie_assert_core_reset(struct artpec6_pcie *artpec6_pcie) From 5d28bee7c91eb61ae485f60adf5397e2ea0d1e4a Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 6 Nov 2019 16:16:41 -0800 Subject: [PATCH 15/60] dt-bindings: PCI: qcom: Add support for SDM845 PCIe Add compatible and necessary clocks and resets definitions for the SDM845 PCIe controller. Signed-off-by: Bjorn Andersson Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring Reviewed-by: Vinod Koul Reviewed-by: Andrew Murray --- .../devicetree/bindings/pci/qcom,pcie.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt index ada80b01bf0c..981b4de12807 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt @@ -11,6 +11,7 @@ - "qcom,pcie-ipq4019" for ipq4019 - "qcom,pcie-ipq8074" for ipq8074 - "qcom,pcie-qcs404" for qcs404 + - "qcom,pcie-sdm845" for sdm845 - reg: Usage: required @@ -126,6 +127,18 @@ - "master_bus" AXI Master clock - "slave_bus" AXI Slave clock +-clock-names: + Usage: required for sdm845 + Value type: + Definition: Should contain the following entries + - "aux" Auxiliary clock + - "cfg" Configuration clock + - "bus_master" Master AXI clock + - "bus_slave" Slave AXI clock + - "slave_q2a" Slave Q2A clock + - "tbu" PCIe TBU clock + - "pipe" PIPE clock + - resets: Usage: required Value type: @@ -188,6 +201,12 @@ - "pwr" PWR reset - "ahb" AHB reset +- reset-names: + Usage: required for sdm845 + Value type: + Definition: Should contain the following entries + - "pci" PCIe core reset + - power-domains: Usage: required for apq8084 and msm8996/apq8096 Value type: From ed8cc3b1fc84f110336edaafaa203f65aa26b5e0 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 6 Nov 2019 16:16:42 -0800 Subject: [PATCH 16/60] PCI: qcom: Add support for SDM845 PCIe controller The SDM845 has one Gen2 and one Gen3 controller, add support for these. Signed-off-by: Bjorn Andersson Signed-off-by: Lorenzo Pieralisi Reviewed-by: Vinod Koul Reviewed-by: Philipp Zabel Reviewed-by: Andrew Murray Acked-by: Stanimir Varbanov --- drivers/pci/controller/dwc/pcie-qcom.c | 150 +++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 7e581748ee9f..5ea527a6bd9f 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -54,6 +54,7 @@ #define PCIE20_PARF_LTSSM 0x1B0 #define PCIE20_PARF_SID_OFFSET 0x234 #define PCIE20_PARF_BDF_TRANSLATE_CFG 0x24C +#define PCIE20_PARF_DEVICE_TYPE 0x1000 #define PCIE20_ELBI_SYS_CTRL 0x04 #define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0) @@ -80,6 +81,8 @@ #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE 0x358 #define SLV_ADDR_SPACE_SZ 0x10000000 +#define DEVICE_TYPE_RC 0x4 + #define QCOM_PCIE_2_1_0_MAX_SUPPLY 3 struct qcom_pcie_resources_2_1_0 { struct clk *iface_clk; @@ -139,12 +142,20 @@ struct qcom_pcie_resources_2_3_3 { struct reset_control *rst[7]; }; +struct qcom_pcie_resources_2_7_0 { + struct clk_bulk_data clks[6]; + struct regulator_bulk_data supplies[2]; + struct reset_control *pci_reset; + struct clk *pipe_clk; +}; + union qcom_pcie_resources { struct qcom_pcie_resources_1_0_0 v1_0_0; struct qcom_pcie_resources_2_1_0 v2_1_0; struct qcom_pcie_resources_2_3_2 v2_3_2; struct qcom_pcie_resources_2_3_3 v2_3_3; struct qcom_pcie_resources_2_4_0 v2_4_0; + struct qcom_pcie_resources_2_7_0 v2_7_0; }; struct qcom_pcie; @@ -1068,6 +1079,134 @@ err_clk_iface: return ret; } +static int qcom_pcie_get_resources_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + int ret; + + res->pci_reset = devm_reset_control_get_exclusive(dev, "pci"); + if (IS_ERR(res->pci_reset)) + return PTR_ERR(res->pci_reset); + + 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->clks[0].id = "aux"; + res->clks[1].id = "cfg"; + res->clks[2].id = "bus_master"; + res->clks[3].id = "bus_slave"; + res->clks[4].id = "slave_q2a"; + res->clks[5].id = "tbu"; + + ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks); + if (ret < 0) + return ret; + + res->pipe_clk = devm_clk_get(dev, "pipe"); + return PTR_ERR_OR_ZERO(res->pipe_clk); +} + +static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + 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_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks); + if (ret < 0) + goto err_disable_regulators; + + ret = reset_control_assert(res->pci_reset); + if (ret < 0) { + dev_err(dev, "cannot deassert pci reset\n"); + goto err_disable_clocks; + } + + usleep_range(1000, 1500); + + ret = reset_control_deassert(res->pci_reset); + if (ret < 0) { + dev_err(dev, "cannot deassert pci reset\n"); + goto err_disable_clocks; + } + + ret = clk_prepare_enable(res->pipe_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable pipe clock\n"); + goto err_disable_clocks; + } + + /* configure PCIe to RC mode */ + writel(DEVICE_TYPE_RC, pcie->parf + PCIE20_PARF_DEVICE_TYPE); + + /* enable PCIe clocks and resets */ + val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val &= ~BIT(0); + writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + + /* change DBI base address */ + writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); + + /* MAC PHY_POWERDOWN MUX DISABLE */ + val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL); + val &= ~BIT(29); + writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL); + + val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + val |= BIT(4); + writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT); + val |= BIT(31); + writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT); + } + + return 0; +err_disable_clocks: + clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks); +err_disable_regulators: + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); + + return ret; +} + +static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + + clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks); + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); +} + +static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + + return clk_prepare_enable(res->pipe_clk); +} + +static void qcom_pcie_post_deinit_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + + clk_disable_unprepare(res->pipe_clk); +} + static int qcom_pcie_link_up(struct dw_pcie *pci) { u16 val = readw(pci->dbi_base + PCIE20_CAP + PCI_EXP_LNKSTA); @@ -1167,6 +1306,16 @@ static const struct qcom_pcie_ops ops_2_3_3 = { .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, }; +/* Qcom IP rev.: 2.7.0 Synopsys IP rev.: 4.30a */ +static const struct qcom_pcie_ops ops_2_7_0 = { + .get_resources = qcom_pcie_get_resources_2_7_0, + .init = qcom_pcie_init_2_7_0, + .deinit = qcom_pcie_deinit_2_7_0, + .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, + .post_init = qcom_pcie_post_init_2_7_0, + .post_deinit = qcom_pcie_post_deinit_2_7_0, +}; + static const struct dw_pcie_ops dw_pcie_ops = { .link_up = qcom_pcie_link_up, }; @@ -1282,6 +1431,7 @@ static const struct of_device_id qcom_pcie_match[] = { { .compatible = "qcom,pcie-ipq8074", .data = &ops_2_3_3 }, { .compatible = "qcom,pcie-ipq4019", .data = &ops_2_4_0 }, { .compatible = "qcom,pcie-qcs404", .data = &ops_2_4_0 }, + { .compatible = "qcom,pcie-sdm845", .data = &ops_2_7_0 }, { } }; From 6df19872d881641e6394f93ef2938cffcbdae5bb Mon Sep 17 00:00:00 2001 From: Yurii Monakov Date: Tue, 17 Dec 2019 14:38:36 +0300 Subject: [PATCH 17/60] PCI: keystone: Fix link training retries initiation ks_pcie_stop_link() function does not clear LTSSM_EN_VAL bit so link training was not triggered more than once after startup. In configurations where link can be unstable during early boot, for example, under low temperature, it will never be established. Fixes: 0c4ffcfe1fbc ("PCI: keystone: Add TI Keystone PCIe driver") Signed-off-by: Yurii Monakov Signed-off-by: Lorenzo Pieralisi Acked-by: Andrew Murray Cc: stable@vger.kernel.org --- drivers/pci/controller/dwc/pci-keystone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index af677254a072..d4de4f6cff8b 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -510,7 +510,7 @@ static void ks_pcie_stop_link(struct dw_pcie *pci) /* Disable Link training */ val = ks_pcie_app_readl(ks_pcie, CMD_STATUS); val &= ~LTSSM_EN_VAL; - ks_pcie_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val); + ks_pcie_app_writel(ks_pcie, CMD_STATUS, val); } static int ks_pcie_start_link(struct dw_pcie *pci) From 2d0c3fbe43fa0e6fcb7a6c755c5f4cd702c0d2f4 Mon Sep 17 00:00:00 2001 From: Yurii Monakov Date: Fri, 4 Oct 2019 18:48:11 +0300 Subject: [PATCH 18/60] PCI: keystone: Fix outbound region mapping The Keystone outbound Address Translation Unit (ATU) maps PCI MMIO space in 8 MB windows. When programming the ATU windows, we previously incremented the starting address by 8, not 8 MB, so all the windows were mapped to the first 8 MB. Therefore, only 8 MB of MMIO space was accessible. Update the loop so it increments the starting address by 8 MB, not 8, so more MMIO space is accessible. Fixes: e75043ad9792 ("PCI: keystone: Cleanup outbound window configuration") Link: https://lore.kernel.org/r/20191004154811.GA31397@monakov-y.office.kontur-niirs.ru Signed-off-by: Yurii Monakov [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Signed-off-by: Lorenzo Pieralisi Acked-by: Andrew Murray Acked-by: Kishon Vijay Abraham I Cc: stable@vger.kernel.org # v4.20+ --- drivers/pci/controller/dwc/pci-keystone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index d4de4f6cff8b..ea8e7ebd8c4f 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -422,7 +422,7 @@ static void ks_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie) lower_32_bits(start) | OB_ENABLEN); ks_pcie_app_writel(ks_pcie, OB_OFFSET_HI(i), upper_32_bits(start)); - start += OB_WIN_SIZE; + start += OB_WIN_SIZE * SZ_1M; } val = ks_pcie_app_readl(ks_pcie, CMD_STATUS); From 885199148442f56b880995d703d2ed03b6481a3c Mon Sep 17 00:00:00 2001 From: David Engraf Date: Mon, 16 Dec 2019 12:18:25 +0100 Subject: [PATCH 19/60] PCI: tegra: Fix return value check of pm_runtime_get_sync() pm_runtime_get_sync() returns the device's usage counter. This might be >0 if the device is already powered up or CONFIG_PM is disabled. Abort probe function on real error only. Fixes: da76ba50963b ("PCI: tegra: Add power management support") Link: https://lore.kernel.org/r/20191216111825.28136-1-david.engraf@sysgo.com Signed-off-by: David Engraf Signed-off-by: Bjorn Helgaas Signed-off-by: Lorenzo Pieralisi Acked-by: Andrew Murray Cc: stable@vger.kernel.org # v4.17+ --- drivers/pci/controller/pci-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 673a1725ef38..090b632965e2 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2798,7 +2798,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) pm_runtime_enable(pcie->dev); err = pm_runtime_get_sync(pcie->dev); - if (err) { + if (err < 0) { dev_err(dev, "fail to enable pcie controller: %d\n", err); goto teardown_msi; } From 21a92676e1fe292acb077b13106b08c22ed36b14 Mon Sep 17 00:00:00 2001 From: Marcel Ziswiler Date: Tue, 7 Jan 2020 09:14:02 +0100 Subject: [PATCH 20/60] PCI: tegra: Fix afi_pex2_ctrl reg offset for Tegra30 Fix AFI_PEX2_CTRL reg offset for Tegra30 by moving it from the Tegra20 SoC struct where it erroneously got added. This fixes the AFI_PEX2_CTRL reg offset being uninitialised subsequently failing to bring up the third PCIe port. Fixes: adb2653b3d2e ("PCI: tegra: Add AFI_PEX2_CTRL reg offset as part of SoC struct") Signed-off-by: Marcel Ziswiler Signed-off-by: Lorenzo Pieralisi Reviewed-by: Andrew Murray Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 090b632965e2..ac93f5a0398e 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2499,7 +2499,6 @@ static const struct tegra_pcie_soc tegra20_pcie = { .num_ports = 2, .ports = tegra20_pcie_ports, .msi_base_shift = 0, - .afi_pex2_ctrl = 0x128, .pads_pll_ctl = PADS_PLL_CTL_TEGRA20, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10, .pads_refclk_cfg0 = 0xfa5cfa5c, @@ -2528,6 +2527,7 @@ static const struct tegra_pcie_soc tegra30_pcie = { .num_ports = 3, .ports = tegra30_pcie_ports, .msi_base_shift = 8, + .afi_pex2_ctrl = 0x128, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, .pads_refclk_cfg0 = 0xfa5cfa5c, From a0601a1f1dee47d7167d4f46280f8ab46967ef87 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 4 Jan 2020 16:21:03 +0100 Subject: [PATCH 21/60] PCI: exynos: Rename Exynos to lowercase Fix up inconsistent usage of upper and lowercase letters in "Exynos" name. "EXYNOS" is not an abbreviation but a regular trademarked name. Therefore it should be written with lowercase letters starting with capital letter. The lowercase "Exynos" name is promoted by its manufacturer Samsung Electronics Co., Ltd., in advertisement materials and on website. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pci-exynos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c index 14a6ba4067fb..c5043d951e80 100644 --- a/drivers/pci/controller/dwc/pci-exynos.c +++ b/drivers/pci/controller/dwc/pci-exynos.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * PCIe host controller driver for Samsung EXYNOS SoCs + * PCIe host controller driver for Samsung Exynos SoCs * * Copyright (C) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com From d36925be53821306ffcaf642967335cd1fb12410 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 16 Dec 2019 07:39:37 +0900 Subject: [PATCH 22/60] PCI: uniphier: remove module code from built-in driver builtin_platform_driver() and MODULE_* are always odd combination. This file is not compiled as a module by anyone because CONFIG_PCIE_UNIPHIER is a bool option. Let's remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. We explicitly disallow a driver unbind, since that doesn't have a sensible use case anyway, and it allows us to drop the ".remove" code. Signed-off-by: Masahiro Yamada Signed-off-by: Lorenzo Pieralisi Reviewed-by: Andrew Murray --- drivers/pci/controller/dwc/pcie-uniphier.c | 31 +--------------------- 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-uniphier.c b/drivers/pci/controller/dwc/pcie-uniphier.c index 8fd7badd59c2..a5401a0b1e58 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier.c +++ b/drivers/pci/controller/dwc/pcie-uniphier.c @@ -9,11 +9,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -171,12 +171,6 @@ static void uniphier_pcie_irq_enable(struct uniphier_pcie_priv *priv) writel(PCL_RCV_INTX_ALL_ENABLE, priv->base + PCL_RCV_INTX); } -static void uniphier_pcie_irq_disable(struct uniphier_pcie_priv *priv) -{ - writel(0, priv->base + PCL_RCV_INT); - writel(0, priv->base + PCL_RCV_INTX); -} - static void uniphier_pcie_irq_ack(struct irq_data *d) { struct pcie_port *pp = irq_data_get_irq_chip_data(d); @@ -397,14 +391,6 @@ out_clk_disable: return ret; } -static void uniphier_pcie_host_disable(struct uniphier_pcie_priv *priv) -{ - uniphier_pcie_irq_disable(priv); - phy_exit(priv->phy); - reset_control_assert(priv->rst); - clk_disable_unprepare(priv->clk); -} - static const struct dw_pcie_ops dw_pcie_ops = { .start_link = uniphier_pcie_establish_link, .stop_link = uniphier_pcie_stop_link, @@ -456,31 +442,16 @@ static int uniphier_pcie_probe(struct platform_device *pdev) return uniphier_add_pcie_port(priv, pdev); } -static int uniphier_pcie_remove(struct platform_device *pdev) -{ - struct uniphier_pcie_priv *priv = platform_get_drvdata(pdev); - - uniphier_pcie_host_disable(priv); - - return 0; -} - static const struct of_device_id uniphier_pcie_match[] = { { .compatible = "socionext,uniphier-pcie", }, { /* sentinel */ }, }; -MODULE_DEVICE_TABLE(of, uniphier_pcie_match); static struct platform_driver uniphier_pcie_driver = { .probe = uniphier_pcie_probe, - .remove = uniphier_pcie_remove, .driver = { .name = "uniphier-pcie", .of_match_table = uniphier_pcie_match, }, }; builtin_platform_driver(uniphier_pcie_driver); - -MODULE_AUTHOR("Kunihiko Hayashi "); -MODULE_DESCRIPTION("UniPhier PCIe host controller driver"); -MODULE_LICENSE("GPL v2"); From 9db8dc6d0785225c42a37be7b44d1b07b31b8957 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Wed, 8 Jan 2020 14:32:08 -0700 Subject: [PATCH 23/60] PCI: Don't disable bridge BARs when assigning bus resources Some PCI bridges implement BARs in addition to bridge windows. For example, here's a PLX switch: 04:00.0 PCI bridge: PLX Technology, Inc. PEX 8724 24-Lane, 6-Port PCI Express Gen 3 (8 GT/s) Switch, 19 x 19mm FCBGA (rev ca) (prog-if 00 [Normal decode]) Flags: bus master, fast devsel, latency 0, IRQ 30, NUMA node 0 Memory at 90a00000 (32-bit, non-prefetchable) [size=256K] Bus: primary=04, secondary=05, subordinate=0a, sec-latency=0 I/O behind bridge: 00002000-00003fff Memory behind bridge: 90000000-909fffff Prefetchable memory behind bridge: 0000380000800000-0000380000bfffff Previously, when the kernel assigned resource addresses (with the pci=realloc command line parameter, for example) it could clear the struct resource corresponding to the BAR. When this happened, lspci would report this BAR as "ignored": Region 0: Memory at (32-bit, non-prefetchable) [size=256K] This is because the kernel reports a zero start address and zero flags in the corresponding sysfs resource file and in /proc/bus/pci/devices. Investigation with 'lspci -x', however, shows the BIOS-assigned address will still be programmed in the device's BAR registers. It's clearly a bug that the kernel lost track of the BAR value, but in most cases, this still won't result in a visible issue because nothing uses the memory, so nothing is affected. However, when an IOMMU is in use, it will not reserve this space in the IOVA because the kernel no longer thinks the range is valid. (See dmar_init_reserved_ranges() for the Intel implementation of this.) Without the proper reserved range, a DMA mapping may allocate an IOVA that matches a bridge BAR, which results in DMA accesses going to the BAR instead of the intended RAM. The problem was in pci_assign_unassigned_root_bus_resources(). When any resource from a bridge device fails to get assigned, the code set the resource's flags to zero. This makes sense for bridge windows, as they will be re-enabled later, but for regular BARs, it makes the kernel permanently lose track of the fact that they decode address space. Change pci_assign_unassigned_root_bus_resources() and pci_assign_unassigned_bridge_resources() so they only clear "res->flags" for bridge *windows*, not bridge BARs. Fixes: da7822e5ad71 ("PCI: update bridge resources to get more big ranges when allocating space (again)") Link: https://lore.kernel.org/r/20200108213208.4612-1-logang@deltatee.com [bhelgaas: commit log, check for pci_is_bridge()] Reported-by: Kit Chow Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index f279826204eb..591161ce0f51 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1803,12 +1803,18 @@ again: /* Restore size and flags */ list_for_each_entry(fail_res, &fail_head, list) { struct resource *res = fail_res->res; + int idx; res->start = fail_res->start; res->end = fail_res->end; res->flags = fail_res->flags; - if (fail_res->dev->subordinate) - res->flags = 0; + + if (pci_is_bridge(fail_res->dev)) { + idx = res - &fail_res->dev->resource[0]; + if (idx >= PCI_BRIDGE_RESOURCES && + idx <= PCI_BRIDGE_RESOURCE_END) + res->flags = 0; + } } free_list(&fail_head); @@ -2055,12 +2061,18 @@ again: /* Restore size and flags */ list_for_each_entry(fail_res, &fail_head, list) { struct resource *res = fail_res->res; + int idx; res->start = fail_res->start; res->end = fail_res->end; res->flags = fail_res->flags; - if (fail_res->dev->subordinate) - res->flags = 0; + + if (pci_is_bridge(fail_res->dev)) { + idx = res - &fail_res->dev->resource[0]; + if (idx >= PCI_BRIDGE_RESOURCES && + idx <= PCI_BRIDGE_RESOURCE_END) + res->flags = 0; + } } free_list(&fail_head); From 47b802d5d80c2fef3ccd9772d518bd602c83cea1 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Mon, 13 Jan 2020 14:07:24 +0800 Subject: [PATCH 24/60] PCI/PM: Print config space of devices before suspend When resuming from hibernation (S4, also known as "suspend to disk") on a VM, we have seen invalid config space, e.g., serial 0000:00:16.3: restoring config space at offset 0x14 (was 0x9104e000, writing 0xffffffff) To help debug problems like this, log the config space being saved before suspend, similar to the log in pci_restore_config_dword() when resuming. Link: https://lore.kernel.org/r/20200113060724.19571-1-yu.c.chen@intel.com [bhelgaas: commit log] Signed-off-by: Chen Yu Signed-off-by: Bjorn Helgaas Cc: Len Brown --- drivers/pci/pci.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e87196cc1a7f..34cde70440c3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1372,8 +1372,11 @@ int pci_save_state(struct pci_dev *dev) { int i; /* XXX: 100% dword access ok here? */ - for (i = 0; i < 16; i++) + for (i = 0; i < 16; i++) { pci_read_config_dword(dev, i * 4, &dev->saved_config_space[i]); + pci_dbg(dev, "saving config space at offset %#x (reading %#x)\n", + i * 4, dev->saved_config_space[i]); + } dev->state_saved = true; i = pci_save_pcie_state(dev); From 0956dcb853dcbfb16e3d98162255e4a73f875cbe Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Mon, 16 Dec 2019 12:01:07 +0100 Subject: [PATCH 25/60] dt-bindings: PCI: Add bindings for brcmstb's PCIe device The DT bindings description of the brcmstb PCIe device is described. This node can only be used for now on the Raspberry Pi 4. Signed-off-by: Jim Quinlan Co-developed-by: Nicolas Saenz Julienne Signed-off-by: Nicolas Saenz Julienne Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring Reviewed-by: Andrew Murray --- .../bindings/pci/brcm,stb-pcie.yaml | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml diff --git a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml new file mode 100644 index 000000000000..77d3e81a437b --- /dev/null +++ b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/brcm,stb-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Brcmstb PCIe Host Controller Device Tree Bindings + +maintainers: + - Nicolas Saenz Julienne + +allOf: + - $ref: /schemas/pci/pci-bus.yaml# + +properties: + compatible: + const: brcm,bcm2711-pcie # The Raspberry Pi 4 + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 2 + items: + - description: PCIe host controller + - description: builtin MSI controller + + interrupt-names: + minItems: 1 + maxItems: 2 + items: + - const: pcie + - const: msi + + ranges: + maxItems: 1 + + dma-ranges: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: sw_pcie + + msi-controller: + description: Identifies the node as an MSI controller. + + msi-parent: + description: MSI controller the device is capable of using. + + brcm,enable-ssc: + description: Indicates usage of spread-spectrum clocking. + type: boolean + +required: + - reg + - dma-ranges + - "#interrupt-cells" + - interrupts + - interrupt-names + - interrupt-map-mask + - interrupt-map + - msi-controller + +unevaluatedProperties: false + +examples: + - | + #include + #include + + scb { + #address-cells = <2>; + #size-cells = <1>; + pcie0: pcie@7d500000 { + compatible = "brcm,bcm2711-pcie"; + reg = <0x0 0x7d500000 0x9310>; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + interrupts = , + ; + interrupt-names = "pcie", "msi"; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>; + msi-parent = <&pcie0>; + msi-controller; + ranges = <0x02000000 0x0 0xf8000000 0x6 0x00000000 0x0 0x04000000>; + dma-ranges = <0x02000000 0x0 0x00000000 0x0 0x00000000 0x0 0x80000000>; + brcm,enable-ssc; + }; + }; From a6b0ef9a7d03bb78d37c420753741ef8a082160b Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Mon, 6 Jan 2020 12:03:28 -0700 Subject: [PATCH 26/60] PCI/switchtec: Add support for Intercomm Notify and Upstream Error Containment Add support for the Inter Fabric Manager Communication (Intercomm) Notify event in PAX variants of Switchtec hardware and the Upstream Error Containment port in the MR1 release of Gen3 firmware. Link: https://lore.kernel.org/r/20200106190337.2428-4-logang@deltatee.com Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 3 +++ include/linux/switchtec.h | 7 +++++-- include/uapi/linux/switchtec_ioctl.h | 4 +++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 9c3ad09d3022..218e67428cf9 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -751,10 +751,13 @@ static const struct event_reg { EV_PAR(SWITCHTEC_IOCTL_EVENT_MRPC_COMP, mrpc_comp_hdr), EV_PAR(SWITCHTEC_IOCTL_EVENT_MRPC_COMP_ASYNC, mrpc_comp_async_hdr), EV_PAR(SWITCHTEC_IOCTL_EVENT_DYN_PART_BIND_COMP, dyn_binding_hdr), + EV_PAR(SWITCHTEC_IOCTL_EVENT_INTERCOMM_REQ_NOTIFY, + intercomm_notify_hdr), EV_PFF(SWITCHTEC_IOCTL_EVENT_AER_IN_P2P, aer_in_p2p_hdr), EV_PFF(SWITCHTEC_IOCTL_EVENT_AER_IN_VEP, aer_in_vep_hdr), EV_PFF(SWITCHTEC_IOCTL_EVENT_DPC, dpc_hdr), EV_PFF(SWITCHTEC_IOCTL_EVENT_CTS, cts_hdr), + EV_PFF(SWITCHTEC_IOCTL_EVENT_UEC, uec_hdr), EV_PFF(SWITCHTEC_IOCTL_EVENT_HOTPLUG, hotplug_hdr), EV_PFF(SWITCHTEC_IOCTL_EVENT_IER, ier_hdr), EV_PFF(SWITCHTEC_IOCTL_EVENT_THRESH, threshold_hdr), diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index e295515bc3f3..b4ba3a38f30f 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -196,7 +196,9 @@ struct part_cfg_regs { u32 mrpc_comp_async_data[5]; u32 dyn_binding_hdr; u32 dyn_binding_data[5]; - u32 reserved4[159]; + u32 intercomm_notify_hdr; + u32 intercomm_notify_data[5]; + u32 reserved4[153]; } __packed; enum { @@ -320,7 +322,8 @@ struct pff_csr_regs { u32 dpc_data[5]; u32 cts_hdr; u32 cts_data[5]; - u32 reserved3[6]; + u32 uec_hdr; + u32 uec_data[5]; u32 hotplug_hdr; u32 hotplug_data[5]; u32 ier_hdr; diff --git a/include/uapi/linux/switchtec_ioctl.h b/include/uapi/linux/switchtec_ioctl.h index c912b5a678e4..e8db938985ca 100644 --- a/include/uapi/linux/switchtec_ioctl.h +++ b/include/uapi/linux/switchtec_ioctl.h @@ -98,7 +98,9 @@ struct switchtec_ioctl_event_summary { #define SWITCHTEC_IOCTL_EVENT_CREDIT_TIMEOUT 27 #define SWITCHTEC_IOCTL_EVENT_LINK_STATE 28 #define SWITCHTEC_IOCTL_EVENT_GFMS 29 -#define SWITCHTEC_IOCTL_MAX_EVENTS 30 +#define SWITCHTEC_IOCTL_EVENT_INTERCOMM_REQ_NOTIFY 30 +#define SWITCHTEC_IOCTL_EVENT_UEC 31 +#define SWITCHTEC_IOCTL_MAX_EVENTS 32 #define SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX -1 #define SWITCHTEC_IOCTL_EVENT_IDX_ALL -2 From 7501a02a9dfca3b4aa5caa74d86b7215b2697f54 Mon Sep 17 00:00:00 2001 From: Wesley Sheng Date: Mon, 6 Jan 2020 12:03:29 -0700 Subject: [PATCH 27/60] PCI/switchtec: Remove redundant valid PFF number count Remove the redundant valid PFF number count from ioctl_event_summary(), since init_pff() has already counted the valid PFFs. Link: https://lore.kernel.org/r/20200106190337.2428-5-logang@deltatee.com Signed-off-by: Wesley Sheng Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 218e67428cf9..231499da2899 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -683,11 +683,7 @@ static int ioctl_event_summary(struct switchtec_dev *stdev, s->part[i] = reg; } - for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) { - reg = ioread16(&stdev->mmio_pff_csr[i].vendor_id); - if (reg != PCI_VENDOR_ID_MICROSEMI) - break; - + for (i = 0; i < stdev->pff_csr_count; i++) { reg = ioread32(&stdev->mmio_pff_csr[i].pff_event_summary); s->pff[i] = reg; } @@ -1327,16 +1323,16 @@ static void init_pff(struct switchtec_dev *stdev) stdev->pff_csr_count = i; reg = ioread32(&pcfg->usp_pff_inst_id); - if (reg < SWITCHTEC_MAX_PFF_CSR) + if (reg < stdev->pff_csr_count) stdev->pff_local[reg] = 1; reg = ioread32(&pcfg->vep_pff_inst_id); - if (reg < SWITCHTEC_MAX_PFF_CSR) + if (reg < stdev->pff_csr_count) stdev->pff_local[reg] = 1; for (i = 0; i < ARRAY_SIZE(pcfg->dsp_pff_inst_id); i++) { reg = ioread32(&pcfg->dsp_pff_inst_id[i]); - if (reg < SWITCHTEC_MAX_PFF_CSR) + if (reg < stdev->pff_csr_count) stdev->pff_local[reg] = 1; } } From 2085747d533b9be5f5a51efbc3e86be0164571a2 Mon Sep 17 00:00:00 2001 From: Wesley Sheng Date: Mon, 6 Jan 2020 12:03:30 -0700 Subject: [PATCH 28/60] PCI/switchtec: Move check event ID from mask_event() to switchtec_event_isr() The event ID check doesn't depend on anything in the mask_all_events() to mask_event() path. Do it in switchtec_event_isr() to avoid the CSR read in mask_event(). Link: https://lore.kernel.org/r/20200106190337.2428-6-logang@deltatee.com Signed-off-by: Wesley Sheng Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 231499da2899..05d4cb49219b 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -1180,10 +1180,6 @@ static int mask_event(struct switchtec_dev *stdev, int eid, int idx) if (!(hdr & SWITCHTEC_EVENT_OCCURRED && hdr & SWITCHTEC_EVENT_EN_IRQ)) return 0; - if (eid == SWITCHTEC_IOCTL_EVENT_LINK_STATE || - eid == SWITCHTEC_IOCTL_EVENT_MRPC_COMP) - return 0; - dev_dbg(&stdev->dev, "%s: %d %d %x\n", __func__, eid, idx, hdr); hdr &= ~(SWITCHTEC_EVENT_EN_IRQ | SWITCHTEC_EVENT_OCCURRED); iowrite32(hdr, hdr_reg); @@ -1230,8 +1226,13 @@ static irqreturn_t switchtec_event_isr(int irq, void *dev) check_link_state_events(stdev); - for (eid = 0; eid < SWITCHTEC_IOCTL_MAX_EVENTS; eid++) + for (eid = 0; eid < SWITCHTEC_IOCTL_MAX_EVENTS; eid++) { + if (eid == SWITCHTEC_IOCTL_EVENT_LINK_STATE || + eid == SWITCHTEC_IOCTL_EVENT_MRPC_COMP) + continue; + event_count += mask_all_events(stdev, eid); + } if (event_count) { atomic_inc(&stdev->event_cnt); From fcccd282b633ab9fc7d53ff8ccf82ab5c30a0985 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 14 Jan 2020 20:56:42 -0700 Subject: [PATCH 29/60] PCI/switchtec: Rename generation-specific constants Gen4 hardware will have different values for the SWITCHTEC_X_RUNNING and SWITCHTEC_IOCTL_NUM_PARTITIONS, so rename them with GEN3 in their name. No functional changes intended. Link: https://lore.kernel.org/r/20200115035648.2578-2-logang@deltatee.com Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 10 +++++----- include/linux/switchtec.h | 8 ++++---- include/uapi/linux/switchtec_ioctl.h | 5 ++++- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 05d4cb49219b..e4d9291864bc 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -569,7 +569,7 @@ static int ioctl_flash_info(struct switchtec_dev *stdev, struct flash_info_regs __iomem *fi = stdev->mmio_flash_info; info.flash_length = ioread32(&fi->flash_length); - info.num_partitions = SWITCHTEC_IOCTL_NUM_PARTITIONS; + info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN3; if (copy_to_user(uinfo, &info, sizeof(info))) return -EFAULT; @@ -599,25 +599,25 @@ static int ioctl_flash_part_info(struct switchtec_dev *stdev, case SWITCHTEC_IOCTL_PART_CFG0: active_addr = ioread32(&fi->active_cfg); set_fw_info_part(&info, &fi->cfg0); - if (ioread16(&si->cfg_running) == SWITCHTEC_CFG0_RUNNING) + if (ioread16(&si->cfg_running) == SWITCHTEC_GEN3_CFG0_RUNNING) info.active |= SWITCHTEC_IOCTL_PART_RUNNING; break; case SWITCHTEC_IOCTL_PART_CFG1: active_addr = ioread32(&fi->active_cfg); set_fw_info_part(&info, &fi->cfg1); - if (ioread16(&si->cfg_running) == SWITCHTEC_CFG1_RUNNING) + if (ioread16(&si->cfg_running) == SWITCHTEC_GEN3_CFG1_RUNNING) info.active |= SWITCHTEC_IOCTL_PART_RUNNING; break; case SWITCHTEC_IOCTL_PART_IMG0: active_addr = ioread32(&fi->active_img); set_fw_info_part(&info, &fi->img0); - if (ioread16(&si->img_running) == SWITCHTEC_IMG0_RUNNING) + if (ioread16(&si->img_running) == SWITCHTEC_GEN3_IMG0_RUNNING) info.active |= SWITCHTEC_IOCTL_PART_RUNNING; break; case SWITCHTEC_IOCTL_PART_IMG1: active_addr = ioread32(&fi->active_img); set_fw_info_part(&info, &fi->img1); - if (ioread16(&si->img_running) == SWITCHTEC_IMG1_RUNNING) + if (ioread16(&si->img_running) == SWITCHTEC_GEN3_IMG1_RUNNING) info.active |= SWITCHTEC_IOCTL_PART_RUNNING; break; case SWITCHTEC_IOCTL_PART_NVLOG: diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index b4ba3a38f30f..4ee450487fe4 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -98,10 +98,10 @@ struct sw_event_regs { } __packed; enum { - SWITCHTEC_CFG0_RUNNING = 0x04, - SWITCHTEC_CFG1_RUNNING = 0x05, - SWITCHTEC_IMG0_RUNNING = 0x03, - SWITCHTEC_IMG1_RUNNING = 0x07, + SWITCHTEC_GEN3_CFG0_RUNNING = 0x04, + SWITCHTEC_GEN3_CFG1_RUNNING = 0x05, + SWITCHTEC_GEN3_IMG0_RUNNING = 0x03, + SWITCHTEC_GEN3_IMG1_RUNNING = 0x07, }; struct sys_info_regs { diff --git a/include/uapi/linux/switchtec_ioctl.h b/include/uapi/linux/switchtec_ioctl.h index e8db938985ca..4d09cfa2e9e6 100644 --- a/include/uapi/linux/switchtec_ioctl.h +++ b/include/uapi/linux/switchtec_ioctl.h @@ -32,7 +32,10 @@ #define SWITCHTEC_IOCTL_PART_VENDOR5 10 #define SWITCHTEC_IOCTL_PART_VENDOR6 11 #define SWITCHTEC_IOCTL_PART_VENDOR7 12 -#define SWITCHTEC_IOCTL_NUM_PARTITIONS 13 +#define SWITCHTEC_NUM_PARTITIONS_GEN3 13 + +/* obsolete: for compatibility with old userspace software */ +#define SWITCHTEC_IOCTL_NUM_PARTITIONS SWITCHTEC_NUM_PARTITIONS_GEN3 struct switchtec_ioctl_flash_info { __u64 flash_length; From b13313a01a6a607bd92268822d498d5c7356ca71 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 14 Jan 2020 20:56:43 -0700 Subject: [PATCH 30/60] PCI/switchtec: Add 'generation' variable Add a generation variable passed through the device ID table and test for Gen3-specific registers. This will allow us to add Gen4 and other devices that extend the programming model. Link: https://lore.kernel.org/r/20200115035648.2578-3-logang@deltatee.com Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 90 ++++++++++++++++++++++------------ include/linux/switchtec.h | 6 +++ 2 files changed, 64 insertions(+), 32 deletions(-) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index e4d9291864bc..8302524734f0 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -326,7 +326,21 @@ static DEVICE_ATTR_RO(field) DEVICE_ATTR_SYS_INFO_STR(vendor_id); DEVICE_ATTR_SYS_INFO_STR(product_id); DEVICE_ATTR_SYS_INFO_STR(product_revision); -DEVICE_ATTR_SYS_INFO_STR(component_vendor); + +static ssize_t component_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct switchtec_dev *stdev = to_stdev(dev); + struct sys_info_regs __iomem *si = stdev->mmio_sys_info; + + /* component_vendor field not supported after gen3 */ + if (stdev->gen != SWITCHTEC_GEN3) + return sprintf(buf, "none\n"); + + return io_string_show(buf, &si->component_vendor, + sizeof(si->component_vendor)); +} +static DEVICE_ATTR_RO(component_vendor); static ssize_t component_id_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -334,6 +348,10 @@ static ssize_t component_id_show(struct device *dev, struct switchtec_dev *stdev = to_stdev(dev); int id = ioread16(&stdev->mmio_sys_info->component_id); + /* component_id field not supported after gen3 */ + if (stdev->gen != SWITCHTEC_GEN3) + return sprintf(buf, "none\n"); + return sprintf(buf, "PM%04X\n", id); } static DEVICE_ATTR_RO(component_id); @@ -344,6 +362,10 @@ static ssize_t component_revision_show(struct device *dev, struct switchtec_dev *stdev = to_stdev(dev); int rev = ioread8(&stdev->mmio_sys_info->component_revision); + /* component_revision field not supported after gen3 */ + if (stdev->gen != SWITCHTEC_GEN3) + return sprintf(buf, "255\n"); + return sprintf(buf, "%d\n", rev); } static DEVICE_ATTR_RO(component_revision); @@ -1420,6 +1442,8 @@ static int switchtec_pci_probe(struct pci_dev *pdev, if (IS_ERR(stdev)) return PTR_ERR(stdev); + stdev->gen = id->driver_data; + rc = switchtec_init_pci(stdev, pdev); if (rc) goto err_put; @@ -1467,7 +1491,7 @@ static void switchtec_pci_remove(struct pci_dev *pdev) put_device(&stdev->dev); } -#define SWITCHTEC_PCI_DEVICE(device_id) \ +#define SWITCHTEC_PCI_DEVICE(device_id, gen) \ { \ .vendor = PCI_VENDOR_ID_MICROSEMI, \ .device = device_id, \ @@ -1475,6 +1499,7 @@ static void switchtec_pci_remove(struct pci_dev *pdev) .subdevice = PCI_ANY_ID, \ .class = (PCI_CLASS_MEMORY_OTHER << 8), \ .class_mask = 0xFFFFFFFF, \ + .driver_data = gen, \ }, \ { \ .vendor = PCI_VENDOR_ID_MICROSEMI, \ @@ -1483,39 +1508,40 @@ static void switchtec_pci_remove(struct pci_dev *pdev) .subdevice = PCI_ANY_ID, \ .class = (PCI_CLASS_BRIDGE_OTHER << 8), \ .class_mask = 0xFFFFFFFF, \ + .driver_data = gen, \ } static const struct pci_device_id switchtec_pci_tbl[] = { - SWITCHTEC_PCI_DEVICE(0x8531), //PFX 24xG3 - SWITCHTEC_PCI_DEVICE(0x8532), //PFX 32xG3 - SWITCHTEC_PCI_DEVICE(0x8533), //PFX 48xG3 - SWITCHTEC_PCI_DEVICE(0x8534), //PFX 64xG3 - SWITCHTEC_PCI_DEVICE(0x8535), //PFX 80xG3 - SWITCHTEC_PCI_DEVICE(0x8536), //PFX 96xG3 - SWITCHTEC_PCI_DEVICE(0x8541), //PSX 24xG3 - SWITCHTEC_PCI_DEVICE(0x8542), //PSX 32xG3 - SWITCHTEC_PCI_DEVICE(0x8543), //PSX 48xG3 - SWITCHTEC_PCI_DEVICE(0x8544), //PSX 64xG3 - SWITCHTEC_PCI_DEVICE(0x8545), //PSX 80xG3 - SWITCHTEC_PCI_DEVICE(0x8546), //PSX 96xG3 - SWITCHTEC_PCI_DEVICE(0x8551), //PAX 24XG3 - SWITCHTEC_PCI_DEVICE(0x8552), //PAX 32XG3 - SWITCHTEC_PCI_DEVICE(0x8553), //PAX 48XG3 - SWITCHTEC_PCI_DEVICE(0x8554), //PAX 64XG3 - SWITCHTEC_PCI_DEVICE(0x8555), //PAX 80XG3 - SWITCHTEC_PCI_DEVICE(0x8556), //PAX 96XG3 - SWITCHTEC_PCI_DEVICE(0x8561), //PFXL 24XG3 - SWITCHTEC_PCI_DEVICE(0x8562), //PFXL 32XG3 - SWITCHTEC_PCI_DEVICE(0x8563), //PFXL 48XG3 - SWITCHTEC_PCI_DEVICE(0x8564), //PFXL 64XG3 - SWITCHTEC_PCI_DEVICE(0x8565), //PFXL 80XG3 - SWITCHTEC_PCI_DEVICE(0x8566), //PFXL 96XG3 - SWITCHTEC_PCI_DEVICE(0x8571), //PFXI 24XG3 - SWITCHTEC_PCI_DEVICE(0x8572), //PFXI 32XG3 - SWITCHTEC_PCI_DEVICE(0x8573), //PFXI 48XG3 - SWITCHTEC_PCI_DEVICE(0x8574), //PFXI 64XG3 - SWITCHTEC_PCI_DEVICE(0x8575), //PFXI 80XG3 - SWITCHTEC_PCI_DEVICE(0x8576), //PFXI 96XG3 + SWITCHTEC_PCI_DEVICE(0x8531, SWITCHTEC_GEN3), //PFX 24xG3 + SWITCHTEC_PCI_DEVICE(0x8532, SWITCHTEC_GEN3), //PFX 32xG3 + SWITCHTEC_PCI_DEVICE(0x8533, SWITCHTEC_GEN3), //PFX 48xG3 + SWITCHTEC_PCI_DEVICE(0x8534, SWITCHTEC_GEN3), //PFX 64xG3 + SWITCHTEC_PCI_DEVICE(0x8535, SWITCHTEC_GEN3), //PFX 80xG3 + SWITCHTEC_PCI_DEVICE(0x8536, SWITCHTEC_GEN3), //PFX 96xG3 + SWITCHTEC_PCI_DEVICE(0x8541, SWITCHTEC_GEN3), //PSX 24xG3 + SWITCHTEC_PCI_DEVICE(0x8542, SWITCHTEC_GEN3), //PSX 32xG3 + SWITCHTEC_PCI_DEVICE(0x8543, SWITCHTEC_GEN3), //PSX 48xG3 + SWITCHTEC_PCI_DEVICE(0x8544, SWITCHTEC_GEN3), //PSX 64xG3 + SWITCHTEC_PCI_DEVICE(0x8545, SWITCHTEC_GEN3), //PSX 80xG3 + SWITCHTEC_PCI_DEVICE(0x8546, SWITCHTEC_GEN3), //PSX 96xG3 + SWITCHTEC_PCI_DEVICE(0x8551, SWITCHTEC_GEN3), //PAX 24XG3 + SWITCHTEC_PCI_DEVICE(0x8552, SWITCHTEC_GEN3), //PAX 32XG3 + SWITCHTEC_PCI_DEVICE(0x8553, SWITCHTEC_GEN3), //PAX 48XG3 + SWITCHTEC_PCI_DEVICE(0x8554, SWITCHTEC_GEN3), //PAX 64XG3 + SWITCHTEC_PCI_DEVICE(0x8555, SWITCHTEC_GEN3), //PAX 80XG3 + SWITCHTEC_PCI_DEVICE(0x8556, SWITCHTEC_GEN3), //PAX 96XG3 + SWITCHTEC_PCI_DEVICE(0x8561, SWITCHTEC_GEN3), //PFXL 24XG3 + SWITCHTEC_PCI_DEVICE(0x8562, SWITCHTEC_GEN3), //PFXL 32XG3 + SWITCHTEC_PCI_DEVICE(0x8563, SWITCHTEC_GEN3), //PFXL 48XG3 + SWITCHTEC_PCI_DEVICE(0x8564, SWITCHTEC_GEN3), //PFXL 64XG3 + SWITCHTEC_PCI_DEVICE(0x8565, SWITCHTEC_GEN3), //PFXL 80XG3 + SWITCHTEC_PCI_DEVICE(0x8566, SWITCHTEC_GEN3), //PFXL 96XG3 + SWITCHTEC_PCI_DEVICE(0x8571, SWITCHTEC_GEN3), //PFXI 24XG3 + SWITCHTEC_PCI_DEVICE(0x8572, SWITCHTEC_GEN3), //PFXI 32XG3 + SWITCHTEC_PCI_DEVICE(0x8573, SWITCHTEC_GEN3), //PFXI 48XG3 + SWITCHTEC_PCI_DEVICE(0x8574, SWITCHTEC_GEN3), //PFXI 64XG3 + SWITCHTEC_PCI_DEVICE(0x8575, SWITCHTEC_GEN3), //PFXI 80XG3 + SWITCHTEC_PCI_DEVICE(0x8576, SWITCHTEC_GEN3), //PFXI 96XG3 {0} }; MODULE_DEVICE_TABLE(pci, switchtec_pci_tbl); diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index 4ee450487fe4..d0b5816549ed 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -32,6 +32,10 @@ enum { SWITCHTEC_GAS_PFF_CSR_OFFSET = 0x134000, }; +enum switchtec_gen { + SWITCHTEC_GEN3, +}; + struct mrpc_regs { u8 input_data[SWITCHTEC_MRPC_PAYLOAD_SIZE]; u8 output_data[SWITCHTEC_MRPC_PAYLOAD_SIZE]; @@ -358,6 +362,8 @@ struct switchtec_dev { struct device dev; struct cdev cdev; + enum switchtec_gen gen; + int partition; int partition_count; int pff_csr_count; From 6a3d1b542cfafe6241d4de11800d639c9bf69f29 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 14 Jan 2020 20:56:44 -0700 Subject: [PATCH 31/60] PCI/switchtec: Factor out Gen3 ioctl_flash_part_info() Refactor ioctl_flash_part_info() into a Gen3-specific function because the registers for flash partition information have changed significantly in Gen4 and will require a completely different implementation. No functional changes intended. Co-developed-by: Kelvin Cao Link: https://lore.kernel.org/r/20200115035648.2578-4-logang@deltatee.com Signed-off-by: Kelvin Cao Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 68 +++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 8302524734f0..0ae444517790 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -606,75 +606,91 @@ static void set_fw_info_part(struct switchtec_ioctl_flash_part_info *info, info->length = ioread32(&pi->length); } -static int ioctl_flash_part_info(struct switchtec_dev *stdev, - struct switchtec_ioctl_flash_part_info __user *uinfo) +static int flash_part_info_gen3(struct switchtec_dev *stdev, + struct switchtec_ioctl_flash_part_info *info) { - struct switchtec_ioctl_flash_part_info info = {0}; struct flash_info_regs __iomem *fi = stdev->mmio_flash_info; struct sys_info_regs __iomem *si = stdev->mmio_sys_info; u32 active_addr = -1; - if (copy_from_user(&info, uinfo, sizeof(info))) - return -EFAULT; - - switch (info.flash_partition) { + switch (info->flash_partition) { case SWITCHTEC_IOCTL_PART_CFG0: active_addr = ioread32(&fi->active_cfg); - set_fw_info_part(&info, &fi->cfg0); + set_fw_info_part(info, &fi->cfg0); if (ioread16(&si->cfg_running) == SWITCHTEC_GEN3_CFG0_RUNNING) - info.active |= SWITCHTEC_IOCTL_PART_RUNNING; + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; break; case SWITCHTEC_IOCTL_PART_CFG1: active_addr = ioread32(&fi->active_cfg); - set_fw_info_part(&info, &fi->cfg1); + set_fw_info_part(info, &fi->cfg1); if (ioread16(&si->cfg_running) == SWITCHTEC_GEN3_CFG1_RUNNING) - info.active |= SWITCHTEC_IOCTL_PART_RUNNING; + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; break; case SWITCHTEC_IOCTL_PART_IMG0: active_addr = ioread32(&fi->active_img); - set_fw_info_part(&info, &fi->img0); + set_fw_info_part(info, &fi->img0); if (ioread16(&si->img_running) == SWITCHTEC_GEN3_IMG0_RUNNING) - info.active |= SWITCHTEC_IOCTL_PART_RUNNING; + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; break; case SWITCHTEC_IOCTL_PART_IMG1: active_addr = ioread32(&fi->active_img); - set_fw_info_part(&info, &fi->img1); + set_fw_info_part(info, &fi->img1); if (ioread16(&si->img_running) == SWITCHTEC_GEN3_IMG1_RUNNING) - info.active |= SWITCHTEC_IOCTL_PART_RUNNING; + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; break; case SWITCHTEC_IOCTL_PART_NVLOG: - set_fw_info_part(&info, &fi->nvlog); + set_fw_info_part(info, &fi->nvlog); break; case SWITCHTEC_IOCTL_PART_VENDOR0: - set_fw_info_part(&info, &fi->vendor[0]); + set_fw_info_part(info, &fi->vendor[0]); break; case SWITCHTEC_IOCTL_PART_VENDOR1: - set_fw_info_part(&info, &fi->vendor[1]); + set_fw_info_part(info, &fi->vendor[1]); break; case SWITCHTEC_IOCTL_PART_VENDOR2: - set_fw_info_part(&info, &fi->vendor[2]); + set_fw_info_part(info, &fi->vendor[2]); break; case SWITCHTEC_IOCTL_PART_VENDOR3: - set_fw_info_part(&info, &fi->vendor[3]); + set_fw_info_part(info, &fi->vendor[3]); break; case SWITCHTEC_IOCTL_PART_VENDOR4: - set_fw_info_part(&info, &fi->vendor[4]); + set_fw_info_part(info, &fi->vendor[4]); break; case SWITCHTEC_IOCTL_PART_VENDOR5: - set_fw_info_part(&info, &fi->vendor[5]); + set_fw_info_part(info, &fi->vendor[5]); break; case SWITCHTEC_IOCTL_PART_VENDOR6: - set_fw_info_part(&info, &fi->vendor[6]); + set_fw_info_part(info, &fi->vendor[6]); break; case SWITCHTEC_IOCTL_PART_VENDOR7: - set_fw_info_part(&info, &fi->vendor[7]); + set_fw_info_part(info, &fi->vendor[7]); break; default: return -EINVAL; } - if (info.address == active_addr) - info.active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (info->address == active_addr) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + + return 0; +} + +static int ioctl_flash_part_info(struct switchtec_dev *stdev, + struct switchtec_ioctl_flash_part_info __user *uinfo) +{ + int ret; + struct switchtec_ioctl_flash_part_info info = {0}; + + if (copy_from_user(&info, uinfo, sizeof(info))) + return -EFAULT; + + if (stdev->gen == SWITCHTEC_GEN3) { + ret = flash_part_info_gen3(stdev, &info); + if (ret) + return ret; + } else { + return -ENOTSUPP; + } if (copy_to_user(uinfo, &info, sizeof(info))) return -EFAULT; From 993d208daaebebc3f3ec211e862a413a90e8d69b Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 14 Jan 2020 20:56:45 -0700 Subject: [PATCH 32/60] PCI/switchtec: Separate Gen3 register structures into unions Since the sys_info and flash_info registers differ significantly in Gen4 hardware, separate out the Gen3 registers into their own structure with a union in the main structure. No functional changes intended. Link: https://lore.kernel.org/r/20200115035648.2578-5-logang@deltatee.com Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 38 ++++++++++++++++++++++--------- include/linux/switchtec.h | 41 ++++++++++++++++++++++------------ 2 files changed, 54 insertions(+), 25 deletions(-) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 0ae444517790..c4dae075a692 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -317,8 +317,12 @@ static ssize_t field ## _show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct switchtec_dev *stdev = to_stdev(dev); \ - return io_string_show(buf, &stdev->mmio_sys_info->field, \ - sizeof(stdev->mmio_sys_info->field)); \ + struct sys_info_regs __iomem *si = stdev->mmio_sys_info; \ + if (stdev->gen == SWITCHTEC_GEN3) \ + return io_string_show(buf, &si->gen3.field, \ + sizeof(si->gen3.field)); \ + else \ + return -ENOTSUPP; \ } \ \ static DEVICE_ATTR_RO(field) @@ -337,8 +341,8 @@ static ssize_t component_vendor_show(struct device *dev, if (stdev->gen != SWITCHTEC_GEN3) return sprintf(buf, "none\n"); - return io_string_show(buf, &si->component_vendor, - sizeof(si->component_vendor)); + return io_string_show(buf, &si->gen3.component_vendor, + sizeof(si->gen3.component_vendor)); } static DEVICE_ATTR_RO(component_vendor); @@ -346,7 +350,7 @@ static ssize_t component_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct switchtec_dev *stdev = to_stdev(dev); - int id = ioread16(&stdev->mmio_sys_info->component_id); + int id = ioread16(&stdev->mmio_sys_info->gen3.component_id); /* component_id field not supported after gen3 */ if (stdev->gen != SWITCHTEC_GEN3) @@ -360,7 +364,7 @@ static ssize_t component_revision_show(struct device *dev, struct device_attribute *attr, char *buf) { struct switchtec_dev *stdev = to_stdev(dev); - int rev = ioread8(&stdev->mmio_sys_info->component_revision); + int rev = ioread8(&stdev->mmio_sys_info->gen3.component_revision); /* component_revision field not supported after gen3 */ if (stdev->gen != SWITCHTEC_GEN3) @@ -590,8 +594,12 @@ static int ioctl_flash_info(struct switchtec_dev *stdev, struct switchtec_ioctl_flash_info info = {0}; struct flash_info_regs __iomem *fi = stdev->mmio_flash_info; - info.flash_length = ioread32(&fi->flash_length); - info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN3; + if (stdev->gen == SWITCHTEC_GEN3) { + info.flash_length = ioread32(&fi->gen3.flash_length); + info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN3; + } else { + return -ENOTSUPP; + } if (copy_to_user(uinfo, &info, sizeof(info))) return -EFAULT; @@ -609,8 +617,9 @@ static void set_fw_info_part(struct switchtec_ioctl_flash_part_info *info, static int flash_part_info_gen3(struct switchtec_dev *stdev, struct switchtec_ioctl_flash_part_info *info) { - struct flash_info_regs __iomem *fi = stdev->mmio_flash_info; - struct sys_info_regs __iomem *si = stdev->mmio_sys_info; + struct flash_info_regs_gen3 __iomem *fi = + &stdev->mmio_flash_info->gen3; + struct sys_info_regs_gen3 __iomem *si = &stdev->mmio_sys_info->gen3; u32 active_addr = -1; switch (info->flash_partition) { @@ -1382,6 +1391,7 @@ static int switchtec_init_pci(struct switchtec_dev *stdev, int rc; void __iomem *map; unsigned long res_start, res_len; + u32 __iomem *part_id; rc = pcim_enable_device(pdev); if (rc) @@ -1416,7 +1426,13 @@ static int switchtec_init_pci(struct switchtec_dev *stdev, stdev->mmio_sys_info = stdev->mmio + SWITCHTEC_GAS_SYS_INFO_OFFSET; stdev->mmio_flash_info = stdev->mmio + SWITCHTEC_GAS_FLASH_INFO_OFFSET; stdev->mmio_ntb = stdev->mmio + SWITCHTEC_GAS_NTB_OFFSET; - stdev->partition = ioread8(&stdev->mmio_sys_info->partition_id); + + if (stdev->gen == SWITCHTEC_GEN3) + part_id = &stdev->mmio_sys_info->gen3.partition_id; + else + return -ENOTSUPP; + + stdev->partition = ioread8(part_id); stdev->partition_count = ioread8(&stdev->mmio_ntb->partition_count); stdev->mmio_part_cfg_all = stdev->mmio + SWITCHTEC_GAS_PART_CFG_OFFSET; stdev->mmio_part_cfg = &stdev->mmio_part_cfg_all[stdev->partition]; diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index d0b5816549ed..32a20d10b02e 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -108,10 +108,7 @@ enum { SWITCHTEC_GEN3_IMG1_RUNNING = 0x07, }; -struct sys_info_regs { - u32 device_id; - u32 device_version; - u32 firmware_version; +struct sys_info_regs_gen3 { u32 reserved1; u32 vendor_table_revision; u32 table_format_version; @@ -128,26 +125,36 @@ struct sys_info_regs { u8 component_revision; } __packed; -struct flash_info_regs { +struct sys_info_regs { + u32 device_id; + u32 device_version; + u32 firmware_version; + union { + struct sys_info_regs_gen3 gen3; + }; +} __packed; + +struct partition_info { + u32 address; + u32 length; +}; + +struct flash_info_regs_gen3 { u32 flash_part_map_upd_idx; - struct active_partition_info { + struct active_partition_info_gen3 { u32 address; u32 build_version; u32 build_string; } active_img; - struct active_partition_info active_cfg; - struct active_partition_info inactive_img; - struct active_partition_info inactive_cfg; + struct active_partition_info_gen3 active_cfg; + struct active_partition_info_gen3 inactive_img; + struct active_partition_info_gen3 inactive_cfg; u32 flash_length; - struct partition_info { - u32 address; - u32 length; - } cfg0; - + struct partition_info cfg0; struct partition_info cfg1; struct partition_info img0; struct partition_info img1; @@ -155,6 +162,12 @@ struct flash_info_regs { struct partition_info vendor[8]; }; +struct flash_info_regs { + union { + struct flash_info_regs_gen3 gen3; + }; +}; + enum { SWITCHTEC_NTB_REG_INFO_OFFSET = 0x0000, SWITCHTEC_NTB_REG_CTRL_OFFSET = 0x4000, From a3321ca394082f403b447646d81c18ff6b39f4a6 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 14 Jan 2020 20:56:46 -0700 Subject: [PATCH 33/60] PCI/switchtec: Add Gen4 system info register support Add the Gen4-specific system info registers and ensure their usage is guarded by a check on the device's generation. Link: https://lore.kernel.org/r/20200115035648.2578-6-logang@deltatee.com Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 5 ++++ include/linux/switchtec.h | 43 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index c4dae075a692..b09c3f53a552 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -321,6 +321,9 @@ static ssize_t field ## _show(struct device *dev, \ if (stdev->gen == SWITCHTEC_GEN3) \ return io_string_show(buf, &si->gen3.field, \ sizeof(si->gen3.field)); \ + else if (stdev->gen == SWITCHTEC_GEN4) \ + return io_string_show(buf, &si->gen4.field, \ + sizeof(si->gen4.field)); \ else \ return -ENOTSUPP; \ } \ @@ -1429,6 +1432,8 @@ static int switchtec_init_pci(struct switchtec_dev *stdev, if (stdev->gen == SWITCHTEC_GEN3) part_id = &stdev->mmio_sys_info->gen3.partition_id; + else if (stdev->gen == SWITCHTEC_GEN4) + part_id = &stdev->mmio_sys_info->gen4.partition_id; else return -ENOTSUPP; diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index 32a20d10b02e..d012520e5cc5 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -34,6 +34,7 @@ enum { enum switchtec_gen { SWITCHTEC_GEN3, + SWITCHTEC_GEN4, }; struct mrpc_regs { @@ -125,12 +126,54 @@ struct sys_info_regs_gen3 { u8 component_revision; } __packed; +struct sys_info_regs_gen4 { + u16 gas_layout_ver; + u8 evlist_ver; + u8 reserved1; + u16 mgmt_cmd_set_ver; + u16 fabric_cmd_set_ver; + u32 reserved2[2]; + u8 mrpc_uart_ver; + u8 mrpc_twi_ver; + u8 mrpc_eth_ver; + u8 mrpc_inband_ver; + u32 reserved3[7]; + u32 fw_update_tmo; + u32 xml_version_cfg; + u32 xml_version_img; + u32 partition_id; + u16 bl2_running; + u16 cfg_running; + u16 img_running; + u16 key_running; + u32 reserved4[43]; + u32 vendor_seeprom_twi; + u32 vendor_table_revision; + u32 vendor_specific_info[2]; + u16 p2p_vendor_id; + u16 p2p_device_id; + u8 p2p_revision_id; + u8 reserved5[3]; + u32 p2p_class_id; + u16 subsystem_vendor_id; + u16 subsystem_id; + u32 p2p_serial_number[2]; + u8 mac_addr[6]; + u8 reserved6[2]; + u32 reserved7[3]; + char vendor_id[8]; + char product_id[24]; + char product_revision[2]; + u16 reserved8; +} __packed; + struct sys_info_regs { u32 device_id; u32 device_version; u32 firmware_version; union { struct sys_info_regs_gen3 gen3; + struct sys_info_regs_gen4 gen4; }; } __packed; From 4efa1d2e36976d7b26f2e67f4c838330fbc91299 Mon Sep 17 00:00:00 2001 From: Kelvin Cao Date: Tue, 14 Jan 2020 20:56:47 -0700 Subject: [PATCH 34/60] PCI/switchtec: Add Gen4 flash information interface support Add the new flash_info registers struct and the implementation of ioctl_flash_part_info() for the new Gen4 hardware. [logang@deltatee.com: rewrote commit message] Link: https://lore.kernel.org/r/20200115035648.2578-7-logang@deltatee.com Signed-off-by: Kelvin Cao Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 111 +++++++++++++++++++++++++++ include/linux/switchtec.h | 52 +++++++++++++ include/uapi/linux/switchtec_ioctl.h | 8 ++ 3 files changed, 171 insertions(+) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index b09c3f53a552..af85d232d200 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -600,6 +600,9 @@ static int ioctl_flash_info(struct switchtec_dev *stdev, if (stdev->gen == SWITCHTEC_GEN3) { info.flash_length = ioread32(&fi->gen3.flash_length); info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN3; + } else if (stdev->gen == SWITCHTEC_GEN4) { + info.flash_length = ioread32(&fi->gen4.flash_length); + info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN4; } else { return -ENOTSUPP; } @@ -687,6 +690,110 @@ static int flash_part_info_gen3(struct switchtec_dev *stdev, return 0; } +static int flash_part_info_gen4(struct switchtec_dev *stdev, + struct switchtec_ioctl_flash_part_info *info) +{ + struct flash_info_regs_gen4 __iomem *fi = &stdev->mmio_flash_info->gen4; + struct sys_info_regs_gen4 __iomem *si = &stdev->mmio_sys_info->gen4; + struct active_partition_info_gen4 __iomem *af = &fi->active_flag; + + switch (info->flash_partition) { + case SWITCHTEC_IOCTL_PART_MAP_0: + set_fw_info_part(info, &fi->map0); + break; + case SWITCHTEC_IOCTL_PART_MAP_1: + set_fw_info_part(info, &fi->map1); + break; + case SWITCHTEC_IOCTL_PART_KEY_0: + set_fw_info_part(info, &fi->key0); + if (ioread8(&af->key) == SWITCHTEC_GEN4_KEY0_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->key_running) == SWITCHTEC_GEN4_KEY0_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_KEY_1: + set_fw_info_part(info, &fi->key1); + if (ioread8(&af->key) == SWITCHTEC_GEN4_KEY1_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->key_running) == SWITCHTEC_GEN4_KEY1_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_BL2_0: + set_fw_info_part(info, &fi->bl2_0); + if (ioread8(&af->bl2) == SWITCHTEC_GEN4_BL2_0_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->bl2_running) == SWITCHTEC_GEN4_BL2_0_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_BL2_1: + set_fw_info_part(info, &fi->bl2_1); + if (ioread8(&af->bl2) == SWITCHTEC_GEN4_BL2_1_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->bl2_running) == SWITCHTEC_GEN4_BL2_1_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_CFG0: + set_fw_info_part(info, &fi->cfg0); + if (ioread8(&af->cfg) == SWITCHTEC_GEN4_CFG0_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->cfg_running) == SWITCHTEC_GEN4_CFG0_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_CFG1: + set_fw_info_part(info, &fi->cfg1); + if (ioread8(&af->cfg) == SWITCHTEC_GEN4_CFG1_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->cfg_running) == SWITCHTEC_GEN4_CFG1_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_IMG0: + set_fw_info_part(info, &fi->img0); + if (ioread8(&af->img) == SWITCHTEC_GEN4_IMG0_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->img_running) == SWITCHTEC_GEN4_IMG0_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_IMG1: + set_fw_info_part(info, &fi->img1); + if (ioread8(&af->img) == SWITCHTEC_GEN4_IMG1_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->img_running) == SWITCHTEC_GEN4_IMG1_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_NVLOG: + set_fw_info_part(info, &fi->nvlog); + break; + case SWITCHTEC_IOCTL_PART_VENDOR0: + set_fw_info_part(info, &fi->vendor[0]); + break; + case SWITCHTEC_IOCTL_PART_VENDOR1: + set_fw_info_part(info, &fi->vendor[1]); + break; + case SWITCHTEC_IOCTL_PART_VENDOR2: + set_fw_info_part(info, &fi->vendor[2]); + break; + case SWITCHTEC_IOCTL_PART_VENDOR3: + set_fw_info_part(info, &fi->vendor[3]); + break; + case SWITCHTEC_IOCTL_PART_VENDOR4: + set_fw_info_part(info, &fi->vendor[4]); + break; + case SWITCHTEC_IOCTL_PART_VENDOR5: + set_fw_info_part(info, &fi->vendor[5]); + break; + case SWITCHTEC_IOCTL_PART_VENDOR6: + set_fw_info_part(info, &fi->vendor[6]); + break; + case SWITCHTEC_IOCTL_PART_VENDOR7: + set_fw_info_part(info, &fi->vendor[7]); + break; + default: + return -EINVAL; + } + + return 0; +} + static int ioctl_flash_part_info(struct switchtec_dev *stdev, struct switchtec_ioctl_flash_part_info __user *uinfo) { @@ -700,6 +807,10 @@ static int ioctl_flash_part_info(struct switchtec_dev *stdev, ret = flash_part_info_gen3(stdev, &info); if (ret) return ret; + } else if (stdev->gen == SWITCHTEC_GEN4) { + ret = flash_part_info_gen4(stdev, &info); + if (ret) + return ret; } else { return -ENOTSUPP; } diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index d012520e5cc5..e85155244135 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -109,6 +109,30 @@ enum { SWITCHTEC_GEN3_IMG1_RUNNING = 0x07, }; +enum { + SWITCHTEC_GEN4_MAP0_RUNNING = 0x00, + SWITCHTEC_GEN4_MAP1_RUNNING = 0x01, + SWITCHTEC_GEN4_KEY0_RUNNING = 0x02, + SWITCHTEC_GEN4_KEY1_RUNNING = 0x03, + SWITCHTEC_GEN4_BL2_0_RUNNING = 0x04, + SWITCHTEC_GEN4_BL2_1_RUNNING = 0x05, + SWITCHTEC_GEN4_CFG0_RUNNING = 0x06, + SWITCHTEC_GEN4_CFG1_RUNNING = 0x07, + SWITCHTEC_GEN4_IMG0_RUNNING = 0x08, + SWITCHTEC_GEN4_IMG1_RUNNING = 0x09, +}; + +enum { + SWITCHTEC_GEN4_KEY0_ACTIVE = 0, + SWITCHTEC_GEN4_KEY1_ACTIVE = 1, + SWITCHTEC_GEN4_BL2_0_ACTIVE = 0, + SWITCHTEC_GEN4_BL2_1_ACTIVE = 1, + SWITCHTEC_GEN4_CFG0_ACTIVE = 0, + SWITCHTEC_GEN4_CFG1_ACTIVE = 1, + SWITCHTEC_GEN4_IMG0_ACTIVE = 0, + SWITCHTEC_GEN4_IMG1_ACTIVE = 1, +}; + struct sys_info_regs_gen3 { u32 reserved1; u32 vendor_table_revision; @@ -205,9 +229,37 @@ struct flash_info_regs_gen3 { struct partition_info vendor[8]; }; +struct flash_info_regs_gen4 { + u32 flash_address; + u32 flash_length; + + struct active_partition_info_gen4 { + unsigned char bl2; + unsigned char cfg; + unsigned char img; + unsigned char key; + } active_flag; + + u32 reserved[3]; + + struct partition_info map0; + struct partition_info map1; + struct partition_info key0; + struct partition_info key1; + struct partition_info bl2_0; + struct partition_info bl2_1; + struct partition_info cfg0; + struct partition_info cfg1; + struct partition_info img0; + struct partition_info img1; + struct partition_info nvlog; + struct partition_info vendor[8]; +}; + struct flash_info_regs { union { struct flash_info_regs_gen3 gen3; + struct flash_info_regs_gen4 gen4; }; }; diff --git a/include/uapi/linux/switchtec_ioctl.h b/include/uapi/linux/switchtec_ioctl.h index 4d09cfa2e9e6..2c661a3557e5 100644 --- a/include/uapi/linux/switchtec_ioctl.h +++ b/include/uapi/linux/switchtec_ioctl.h @@ -32,7 +32,15 @@ #define SWITCHTEC_IOCTL_PART_VENDOR5 10 #define SWITCHTEC_IOCTL_PART_VENDOR6 11 #define SWITCHTEC_IOCTL_PART_VENDOR7 12 +#define SWITCHTEC_IOCTL_PART_BL2_0 13 +#define SWITCHTEC_IOCTL_PART_BL2_1 14 +#define SWITCHTEC_IOCTL_PART_MAP_0 15 +#define SWITCHTEC_IOCTL_PART_MAP_1 16 +#define SWITCHTEC_IOCTL_PART_KEY_0 17 +#define SWITCHTEC_IOCTL_PART_KEY_1 18 + #define SWITCHTEC_NUM_PARTITIONS_GEN3 13 +#define SWITCHTEC_NUM_PARTITIONS_GEN4 19 /* obsolete: for compatibility with old userspace software */ #define SWITCHTEC_IOCTL_NUM_PARTITIONS SWITCHTEC_NUM_PARTITIONS_GEN3 From ce7c88600bf8ec0d12cf8e147907f96cd58eeb2e Mon Sep 17 00:00:00 2001 From: Kelvin Cao Date: Mon, 6 Jan 2020 12:03:35 -0700 Subject: [PATCH 35/60] PCI/switchtec: Add Gen4 MRPC GAS access permission check Gen4 hardware provides new MRPC commands to read and write directly from any address in the PCI BAR (which Microsemi refers to as GAS). Since accessing BARs can be dangerous and break the driver, we don't want unprivileged users to have this ability. Therefore, require CAP_SYS_ADMIN for the local and remote GAS access MRPC commands. Privileged processes will already have access to the BAR through the sysfs resource file so this doesn't give userspace any capabilities it didn't already have. [logang@deltatee.com: rework commit message] Link: https://lore.kernel.org/r/20200106190337.2428-11-logang@deltatee.com Signed-off-by: Kelvin Cao Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 6 ++++++ include/linux/switchtec.h | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index af85d232d200..92b95e8067c0 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -479,6 +479,12 @@ static ssize_t switchtec_dev_write(struct file *filp, const char __user *data, rc = -EFAULT; goto out; } + if (((MRPC_CMD_ID(stuser->cmd) == MRPC_GAS_WRITE) || + (MRPC_CMD_ID(stuser->cmd) == MRPC_GAS_READ)) && + !capable(CAP_SYS_ADMIN)) { + rc = -EPERM; + goto out; + } data += sizeof(stuser->cmd); rc = copy_from_user(&stuser->data, data, size - sizeof(stuser->cmd)); diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index e85155244135..082f1d51957a 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -21,6 +21,11 @@ #define SWITCHTEC_EVENT_FATAL BIT(4) #define SWITCHTEC_DMA_MRPC_EN BIT(0) + +#define MRPC_GAS_READ 0x29 +#define MRPC_GAS_WRITE 0x87 +#define MRPC_CMD_ID(x) ((x) & 0xffff) + enum { SWITCHTEC_GAS_MRPC_OFFSET = 0x0000, SWITCHTEC_GAS_TOP_CFG_OFFSET = 0x1000, From 7a30ebb9f2a253eae908cc3e1ba7daaa3bfe2bba Mon Sep 17 00:00:00 2001 From: Kelvin Cao Date: Tue, 14 Jan 2020 20:56:48 -0700 Subject: [PATCH 36/60] PCI/switchtec: Add Gen4 device IDs Now that Gen4 is properly supported, advertise support in the module's device ID table and add the same IDs to the list of switchtec quirks. [logang@deltatee.com: add commit message and quirk IDs] Link: https://lore.kernel.org/r/20200115035648.2578-8-logang@deltatee.com Signed-off-by: Kelvin Cao Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 18 ++++++++++++++++++ drivers/pci/switch/switchtec.c | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 4937a088d7d8..d54692bc3af4 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5373,6 +5373,24 @@ SWITCHTEC_QUIRK(0x8573); /* PFXI 48XG3 */ SWITCHTEC_QUIRK(0x8574); /* PFXI 64XG3 */ SWITCHTEC_QUIRK(0x8575); /* PFXI 80XG3 */ SWITCHTEC_QUIRK(0x8576); /* PFXI 96XG3 */ +SWITCHTEC_QUIRK(0x4000); /* PFX 100XG4 */ +SWITCHTEC_QUIRK(0x4084); /* PFX 84XG4 */ +SWITCHTEC_QUIRK(0x4068); /* PFX 68XG4 */ +SWITCHTEC_QUIRK(0x4052); /* PFX 52XG4 */ +SWITCHTEC_QUIRK(0x4036); /* PFX 36XG4 */ +SWITCHTEC_QUIRK(0x4028); /* PFX 28XG4 */ +SWITCHTEC_QUIRK(0x4100); /* PSX 100XG4 */ +SWITCHTEC_QUIRK(0x4184); /* PSX 84XG4 */ +SWITCHTEC_QUIRK(0x4168); /* PSX 68XG4 */ +SWITCHTEC_QUIRK(0x4152); /* PSX 52XG4 */ +SWITCHTEC_QUIRK(0x4136); /* PSX 36XG4 */ +SWITCHTEC_QUIRK(0x4128); /* PSX 28XG4 */ +SWITCHTEC_QUIRK(0x4200); /* PAX 100XG4 */ +SWITCHTEC_QUIRK(0x4284); /* PAX 84XG4 */ +SWITCHTEC_QUIRK(0x4268); /* PAX 68XG4 */ +SWITCHTEC_QUIRK(0x4252); /* PAX 52XG4 */ +SWITCHTEC_QUIRK(0x4236); /* PAX 36XG4 */ +SWITCHTEC_QUIRK(0x4228); /* PAX 28XG4 */ /* * On Lenovo Thinkpad P50 SKUs with a Nvidia Quadro M1000M, the BIOS does diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 92b95e8067c0..a823b4b8ef8a 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -1696,6 +1696,24 @@ static const struct pci_device_id switchtec_pci_tbl[] = { SWITCHTEC_PCI_DEVICE(0x8574, SWITCHTEC_GEN3), //PFXI 64XG3 SWITCHTEC_PCI_DEVICE(0x8575, SWITCHTEC_GEN3), //PFXI 80XG3 SWITCHTEC_PCI_DEVICE(0x8576, SWITCHTEC_GEN3), //PFXI 96XG3 + SWITCHTEC_PCI_DEVICE(0x4000, SWITCHTEC_GEN4), //PFX 100XG4 + SWITCHTEC_PCI_DEVICE(0x4084, SWITCHTEC_GEN4), //PFX 84XG4 + SWITCHTEC_PCI_DEVICE(0x4068, SWITCHTEC_GEN4), //PFX 68XG4 + SWITCHTEC_PCI_DEVICE(0x4052, SWITCHTEC_GEN4), //PFX 52XG4 + SWITCHTEC_PCI_DEVICE(0x4036, SWITCHTEC_GEN4), //PFX 36XG4 + SWITCHTEC_PCI_DEVICE(0x4028, SWITCHTEC_GEN4), //PFX 28XG4 + SWITCHTEC_PCI_DEVICE(0x4100, SWITCHTEC_GEN4), //PSX 100XG4 + SWITCHTEC_PCI_DEVICE(0x4184, SWITCHTEC_GEN4), //PSX 84XG4 + SWITCHTEC_PCI_DEVICE(0x4168, SWITCHTEC_GEN4), //PSX 68XG4 + SWITCHTEC_PCI_DEVICE(0x4152, SWITCHTEC_GEN4), //PSX 52XG4 + SWITCHTEC_PCI_DEVICE(0x4136, SWITCHTEC_GEN4), //PSX 36XG4 + SWITCHTEC_PCI_DEVICE(0x4128, SWITCHTEC_GEN4), //PSX 28XG4 + SWITCHTEC_PCI_DEVICE(0x4200, SWITCHTEC_GEN4), //PAX 100XG4 + SWITCHTEC_PCI_DEVICE(0x4284, SWITCHTEC_GEN4), //PAX 84XG4 + SWITCHTEC_PCI_DEVICE(0x4268, SWITCHTEC_GEN4), //PAX 68XG4 + SWITCHTEC_PCI_DEVICE(0x4252, SWITCHTEC_GEN4), //PAX 52XG4 + SWITCHTEC_PCI_DEVICE(0x4236, SWITCHTEC_GEN4), //PAX 36XG4 + SWITCHTEC_PCI_DEVICE(0x4228, SWITCHTEC_GEN4), //PAX 28XG4 {0} }; MODULE_DEVICE_TABLE(pci, switchtec_pci_tbl); From b0de922af53eede340986a2d05b6cd4b6d6efa43 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 21 Jan 2020 17:27:34 +0530 Subject: [PATCH 37/60] PCI: keystone: Fix error handling when "num-viewport" DT property is not populated Fix error handling when "num-viewport" DT property is not populated. Fixes: 23284ad677a9 ("PCI: keystone: Add support for PCIe EP in AM654x Platforms") Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Cc: stable@vger.kernel.org # v5.2+ --- drivers/pci/controller/dwc/pci-keystone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index ea8e7ebd8c4f..c8c702c494a2 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -1354,7 +1354,7 @@ static int __init ks_pcie_probe(struct platform_device *pdev) ret = of_property_read_u32(np, "num-viewport", &num_viewport); if (ret < 0) { dev_err(dev, "unable to read *num-viewport* property\n"); - return ret; + goto err_get_sync; } /* From 216bbaa337bf240dc8c2ac413f0a0f3cf08ee338 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 22 Jan 2020 10:18:31 +0000 Subject: [PATCH 38/60] MAINTAINERS: Update my email address I will lose access to my @arm.com email address next week, so let's update the MAINTAINERS file and map it correctly in .mailmap Signed-off-by: Andrew Murray Signed-off-by: Lorenzo Pieralisi --- .mailmap | 2 ++ MAINTAINERS | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index c24773db04a7..610508725841 100644 --- a/.mailmap +++ b/.mailmap @@ -27,6 +27,8 @@ Andi Shyti Andreas Herrmann Andrey Ryabinin Andrew Morton +Andrew Murray +Andrew Murray Andrew Vasquez Andy Adamson Antoine Tenart diff --git a/MAINTAINERS b/MAINTAINERS index bd5847e802de..9f6b54cb1835 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12774,7 +12774,7 @@ F: arch/x86/kernel/early-quirks.c PCI NATIVE HOST BRIDGE AND ENDPOINT DRIVERS M: Lorenzo Pieralisi -R: Andrew Murray +R: Andrew Murray 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/ From 01daacfb9035e5b86d43a01f11a0614648f306c1 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 13 Dec 2019 19:44:34 +0800 Subject: [PATCH 39/60] PCI/AER: Log which device prevents error recovery PCI error recovery will fail if any device under the Root Port doesn't have an error_detected callback. Currently only the failure result is printed, which is not enough to identify the driver that lacks the callback. Log a message to identify the device with no error_detected callback. [bhelgaas: tweak log message] Link: https://lore.kernel.org/r/1576237474-32021-1-git-send-email-yangyicong@hisilicon.com Signed-off-by: Yicong Yang Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/err.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index b0e6048a9208..98acf944a27f 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -61,10 +61,12 @@ static int report_error_detected(struct pci_dev *dev, * error callbacks of "any" device in the subtree, and will * exit in the disconnected error state. */ - if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) + if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { vote = PCI_ERS_RESULT_NO_AER_DRIVER; - else + pci_info(dev, "AER: Can't recover (no error_detected callback)\n"); + } else { vote = PCI_ERS_RESULT_NONE; + } } else { err_handler = dev->driver->err_handler; vote = err_handler->error_detected(dev, state); From 8d077c3ce0109c406c265cafc334258caee47e6d Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 13 Dec 2019 16:46:05 -0600 Subject: [PATCH 40/60] PCI/AER: Factor message prefixes with dev_fmt() Define dev_fmt() with the common prefix of log messages so we don't have to repeat it in every printk. No functional change intended. Link: https://lore.kernel.org/r/20191213225709.GA213811@google.com Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/err.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index 98acf944a27f..01dfc8bb7ca0 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -10,6 +10,8 @@ * Zhang Yanmin (yanmin.zhang@intel.com) */ +#define dev_fmt(fmt) "AER: " fmt + #include #include #include @@ -63,7 +65,7 @@ static int report_error_detected(struct pci_dev *dev, */ if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { vote = PCI_ERS_RESULT_NO_AER_DRIVER; - pci_info(dev, "AER: Can't recover (no error_detected callback)\n"); + pci_info(dev, "can't recover (no error_detected callback)\n"); } else { vote = PCI_ERS_RESULT_NONE; } @@ -235,12 +237,12 @@ void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state, pci_aer_clear_device_status(dev); pci_cleanup_aer_uncorrect_error_status(dev); - pci_info(dev, "AER: Device recovery successful\n"); + pci_info(dev, "device recovery successful\n"); return; failed: pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT); /* TODO: Should kernel panic here? */ - pci_info(dev, "AER: Device recovery failed\n"); + pci_info(dev, "device recovery failed\n"); } From d95f20c4f07020ebc605f3b46af4b6db9eb5fc99 Mon Sep 17 00:00:00 2001 From: Dongdong Liu Date: Thu, 23 Jan 2020 16:26:31 +0800 Subject: [PATCH 41/60] PCI/AER: Initialize aer_fifo Previously we did not call INIT_KFIFO() for aer_fifo. This leads to kfifo_put() sometimes returning 0 (queue full) when in fact it is not. It is easy to reproduce the problem by using aer-inject: $ aer-inject -s :82:00.0 multiple-corr-nonfatal The content of the multiple-corr-nonfatal file is as below: AER COR RCVR HL 0 1 2 3 AER UNCOR POISON_TLP HL 4 5 6 7 Fixes: 27c1ce8bbed7 ("PCI/AER: Use kfifo for tracking events instead of reimplementing it") Link: https://lore.kernel.org/r/1579767991-103898-1-git-send-email-liudongdong3@huawei.com Signed-off-by: Dongdong Liu Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 1ca86f2e0166..4a818b07a1af 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -1445,6 +1445,7 @@ static int aer_probe(struct pcie_device *dev) return -ENOMEM; rpc->rpd = port; + INIT_KFIFO(rpc->aer_fifo); set_service_data(dev, rpc); status = devm_request_threaded_irq(device, dev->irq, aer_irq, aer_isr, From aad6aa0cd674b7935957354666f82e5b2cdc813d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 21 Jan 2020 06:37:45 -0700 Subject: [PATCH 42/60] x86/PCI: Add to_pci_sysdata() helper Various helpers need the pci_sysdata just to dereference a single field in it. Add a little helper that returns the properly typed sysdata pointer to require a little less boilerplate code. [jonathan.derrick: to_pci_sysdata const argument] Link: https://lore.kernel.org/r/1579613871-301529-2-git-send-email-jonathan.derrick@intel.com Signed-off-by: Christoph Hellwig Signed-off-by: Jon Derrick Signed-off-by: Bjorn Helgaas --- arch/x86/include/asm/pci.h | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index 90d0731fdcb6..a4e09db60f52 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -35,12 +35,15 @@ extern int noioapicreroute; #ifdef CONFIG_PCI +static inline struct pci_sysdata *to_pci_sysdata(const struct pci_bus *bus) +{ + return bus->sysdata; +} + #ifdef CONFIG_PCI_DOMAINS static inline int pci_domain_nr(struct pci_bus *bus) { - struct pci_sysdata *sd = bus->sysdata; - - return sd->domain; + return to_pci_sysdata(bus)->domain; } static inline int pci_proc_domain(struct pci_bus *bus) @@ -52,24 +55,20 @@ static inline int pci_proc_domain(struct pci_bus *bus) #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN static inline void *_pci_root_bus_fwnode(struct pci_bus *bus) { - struct pci_sysdata *sd = bus->sysdata; - - return sd->fwnode; + return to_pci_sysdata(bus)->fwnode; } #define pci_root_bus_fwnode _pci_root_bus_fwnode #endif +#if IS_ENABLED(CONFIG_VMD) static inline bool is_vmd(struct pci_bus *bus) { -#if IS_ENABLED(CONFIG_VMD) - struct pci_sysdata *sd = bus->sysdata; - - return sd->vmd_domain; -#else - return false; -#endif + return to_pci_sysdata(bus)->vmd_domain; } +#else +#define is_vmd(bus) false +#endif /* CONFIG_VMD */ /* Can be used to override the logic in pci_scan_bus for skipping already-configured bus numbers - to be used for buggy BIOSes @@ -124,9 +123,7 @@ void native_restore_msi_irqs(struct pci_dev *dev); /* Returns the node based on pci bus */ static inline int __pcibus_to_node(const struct pci_bus *bus) { - const struct pci_sysdata *sd = bus->sysdata; - - return sd->node; + return to_pci_sysdata(bus)->node; } static inline const struct cpumask * From 34067c56fa177d3582695da8e3e162ef78cd0371 Mon Sep 17 00:00:00 2001 From: Jon Derrick Date: Tue, 21 Jan 2020 06:37:46 -0700 Subject: [PATCH 43/60] x86/PCI: Expose VMD's pci_dev in struct pci_sysdata Expose VMD's pci_dev pointer in struct pci_sysdata. This will be used indirectly by intel-iommu.c to find the correct domain. Link: https://lore.kernel.org/r/1579613871-301529-3-git-send-email-jonathan.derrick@intel.com [bhelgaas: commit log] Signed-off-by: Jon Derrick Signed-off-by: Bjorn Helgaas Cc: Christoph Hellwig --- arch/x86/include/asm/pci.h | 4 ++-- drivers/pci/controller/vmd.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index a4e09db60f52..6512c5469bbb 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -25,7 +25,7 @@ struct pci_sysdata { void *fwnode; /* IRQ domain for MSI assignment */ #endif #if IS_ENABLED(CONFIG_VMD) - bool vmd_domain; /* True if in Intel VMD domain */ + struct pci_dev *vmd_dev; /* VMD Device if in Intel VMD domain */ #endif }; @@ -64,7 +64,7 @@ static inline void *_pci_root_bus_fwnode(struct pci_bus *bus) #if IS_ENABLED(CONFIG_VMD) static inline bool is_vmd(struct pci_bus *bus) { - return to_pci_sysdata(bus)->vmd_domain; + return to_pci_sysdata(bus)->vmd_dev != NULL; } #else #define is_vmd(bus) false diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index 212842263f55..d67ad568af65 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -679,7 +679,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) .parent = res, }; - sd->vmd_domain = true; + sd->vmd_dev = vmd->dev; sd->domain = vmd_find_free_domain(); if (sd->domain < 0) return sd->domain; From 2856ba6020fc5cbf051d5a75b2abb3046072c144 Mon Sep 17 00:00:00 2001 From: Jon Derrick Date: Tue, 21 Jan 2020 06:37:47 -0700 Subject: [PATCH 44/60] PCI: Introduce pci_real_dma_dev() The current DMA alias implementation requires the aliased device be on the same PCI bus as the requester ID. Add an arch-specific mechanism to point to another PCI device when doing mapping and PCI DMA alias search. The default case returns the actual device. Link: https://lore.kernel.org/r/1579613871-301529-4-git-send-email-jonathan.derrick@intel.com Signed-off-by: Jon Derrick Signed-off-by: Bjorn Helgaas Cc: Christoph Hellwig --- arch/x86/pci/common.c | 10 ++++++++++ drivers/pci/pci.c | 19 ++++++++++++++++++- drivers/pci/search.c | 6 ++++++ include/linux/pci.h | 1 + 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 1e59df041456..fe21a5c557e6 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -736,3 +736,13 @@ int pci_ext_cfg_avail(void) else return 0; } + +#if IS_ENABLED(CONFIG_VMD) +struct pci_dev *pci_real_dma_dev(struct pci_dev *dev) +{ + if (is_vmd(dev->bus)) + return to_pci_sysdata(dev->bus)->vmd_dev; + + return dev; +} +#endif diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e87196cc1a7f..ab2b8bcd29bf 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -6033,7 +6033,9 @@ bool pci_devs_are_dma_aliases(struct pci_dev *dev1, struct pci_dev *dev2) return (dev1->dma_alias_mask && test_bit(dev2->devfn, dev1->dma_alias_mask)) || (dev2->dma_alias_mask && - test_bit(dev1->devfn, dev2->dma_alias_mask)); + test_bit(dev1->devfn, dev2->dma_alias_mask)) || + pci_real_dma_dev(dev1) == dev2 || + pci_real_dma_dev(dev2) == dev1; } bool pci_device_is_present(struct pci_dev *pdev) @@ -6057,6 +6059,21 @@ void pci_ignore_hotplug(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_ignore_hotplug); +/** + * pci_real_dma_dev - Get PCI DMA device for PCI device + * @dev: the PCI device that may have a PCI DMA alias + * + * Permits the platform to provide architecture-specific functionality to + * devices needing to alias DMA to another PCI device on another PCI bus. If + * the PCI device is on the same bus, it is recommended to use + * pci_add_dma_alias(). This is the default implementation. Architecture + * implementations can override this. + */ +struct pci_dev __weak *pci_real_dma_dev(struct pci_dev *dev) +{ + return dev; +} + resource_size_t __weak pcibios_default_alignment(void) { return 0; diff --git a/drivers/pci/search.c b/drivers/pci/search.c index bade14002fd8..efdb8b56035f 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -32,6 +32,12 @@ int pci_for_each_dma_alias(struct pci_dev *pdev, struct pci_bus *bus; int ret; + /* + * The device may have an explicit alias requester ID for DMA where the + * requester is on another PCI bus. + */ + pdev = pci_real_dma_dev(pdev); + ret = fn(pdev, pci_dev_id(pdev), data); if (ret) return ret; diff --git a/include/linux/pci.h b/include/linux/pci.h index c393dff2d66f..7f4e80666972 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1202,6 +1202,7 @@ int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size); int pci_select_bars(struct pci_dev *dev, unsigned long flags); bool pci_device_is_present(struct pci_dev *pdev); void pci_ignore_hotplug(struct pci_dev *dev); +struct pci_dev *pci_real_dma_dev(struct pci_dev *dev); int __printf(6, 7) pci_request_irq(struct pci_dev *dev, unsigned int nr, irq_handler_t handler, irq_handler_t thread_fn, void *dev_id, From 2b0140c69637e6f3cf9f8a0b7629567de9645680 Mon Sep 17 00:00:00 2001 From: Jon Derrick Date: Tue, 21 Jan 2020 06:37:48 -0700 Subject: [PATCH 45/60] iommu/vt-d: Use pci_real_dma_dev() for mapping The PCI device may have a DMA requester on another bus, such as VMD subdevices needing to use the VMD endpoint. This case requires the real DMA device for the IOMMU mapping, so use pci_real_dma_dev() to find that device. Link: https://lore.kernel.org/r/1579613871-301529-5-git-send-email-jonathan.derrick@intel.com Signed-off-by: Jon Derrick Signed-off-by: Bjorn Helgaas Acked-by: Lu Baolu --- drivers/iommu/intel-iommu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 0c8d81f56a30..72f26e828124 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -782,6 +782,8 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf return NULL; #endif + pdev = pci_real_dma_dev(pdev); + /* VFs aren't listed in scope tables; we need to look up * the PF instead to find the IOMMU. */ pf_pdev = pci_physfn(pdev); @@ -2428,6 +2430,9 @@ static struct dmar_domain *find_domain(struct device *dev) dev->archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO)) return NULL; + if (dev_is_pci(dev)) + dev = &pci_real_dma_dev(to_pci_dev(dev))->dev; + /* No lock here, assumes no domain exit in normal case */ info = dev->archdata.iommu; if (likely(info)) From e3560ee4cfb29e232ea99ff9adfaa8ac5b414345 Mon Sep 17 00:00:00 2001 From: Jon Derrick Date: Tue, 21 Jan 2020 06:37:49 -0700 Subject: [PATCH 46/60] iommu/vt-d: Remove VMD child device sanity check Remove the sanity check required for VMD child devices. The new pci_real_dma_dev() DMA alias mechanism places them in the same IOMMU group as the VMD endpoint. Assignment of the group would require assigning the VMD endpoint, where unbinding the VMD endpoint removes the child device domain from the hierarchy. Link: https://lore.kernel.org/r/1579613871-301529-6-git-send-email-jonathan.derrick@intel.com Signed-off-by: Jon Derrick Signed-off-by: Bjorn Helgaas Acked-by: Lu Baolu --- drivers/iommu/intel-iommu.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 72f26e828124..7e2c492f314f 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -774,15 +774,7 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf if (dev_is_pci(dev)) { struct pci_dev *pf_pdev; - pdev = to_pci_dev(dev); - -#ifdef CONFIG_X86 - /* VMD child devices currently cannot be handled individually */ - if (is_vmd(pdev->bus)) - return NULL; -#endif - - pdev = pci_real_dma_dev(pdev); + pdev = pci_real_dma_dev(to_pci_dev(dev)); /* VFs aren't listed in scope tables; we need to look up * the PF instead to find the IOMMU. */ From 962e329d888cfcb923d39af4e14fd616467de167 Mon Sep 17 00:00:00 2001 From: Jon Derrick Date: Tue, 21 Jan 2020 06:37:50 -0700 Subject: [PATCH 47/60] PCI: vmd: Remove dma_map_ops overrides Devices on the VMD domain use the VMD endpoint's requester ID and have been relying on the VMD endpoint's DMA operations. The problem with this was that VMD domain devices would use the VMD endpoint's attributes when doing DMA and IOMMU mapping. We can be smarter about this by only using the VMD endpoint when mapping and providing the correct child device's attributes during DMA operations. Remove the dma_map_ops redirect. Link: https://lore.kernel.org/r/1579613871-301529-7-git-send-email-jonathan.derrick@intel.com Signed-off-by: Jon Derrick Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch Acked-by: Lorenzo Pieralisi --- drivers/pci/controller/Kconfig | 1 - drivers/pci/controller/vmd.c | 150 --------------------------------- 2 files changed, 151 deletions(-) diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index c77069c8ee5d..55671429154b 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -239,7 +239,6 @@ config PCIE_TANGO_SMP8759 config VMD depends on PCI_MSI && X86_64 && SRCU - select X86_DEV_DMA_OPS tristate "Intel Volume Management Device Driver" ---help--- Adds support for the Intel Volume Management Device (VMD). VMD is a diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index d67ad568af65..fe1acb0c037b 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -98,9 +98,6 @@ struct vmd_dev { struct irq_domain *irq_domain; struct pci_bus *bus; u8 busn_start; - - struct dma_map_ops dma_ops; - struct dma_domain dma_domain; }; static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus) @@ -295,151 +292,6 @@ static struct msi_domain_info vmd_msi_domain_info = { .chip = &vmd_msi_controller, }; -/* - * VMD replaces the requester ID with its own. DMA mappings for devices in a - * VMD domain need to be mapped for the VMD, not the device requiring - * the mapping. - */ -static struct device *to_vmd_dev(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct vmd_dev *vmd = vmd_from_bus(pdev->bus); - - return &vmd->dev->dev; -} - -static void *vmd_alloc(struct device *dev, size_t size, dma_addr_t *addr, - gfp_t flag, unsigned long attrs) -{ - return dma_alloc_attrs(to_vmd_dev(dev), size, addr, flag, attrs); -} - -static void vmd_free(struct device *dev, size_t size, void *vaddr, - dma_addr_t addr, unsigned long attrs) -{ - return dma_free_attrs(to_vmd_dev(dev), size, vaddr, addr, attrs); -} - -static int vmd_mmap(struct device *dev, struct vm_area_struct *vma, - void *cpu_addr, dma_addr_t addr, size_t size, - unsigned long attrs) -{ - return dma_mmap_attrs(to_vmd_dev(dev), vma, cpu_addr, addr, size, - attrs); -} - -static int vmd_get_sgtable(struct device *dev, struct sg_table *sgt, - void *cpu_addr, dma_addr_t addr, size_t size, - unsigned long attrs) -{ - return dma_get_sgtable_attrs(to_vmd_dev(dev), sgt, cpu_addr, addr, size, - attrs); -} - -static dma_addr_t vmd_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, - enum dma_data_direction dir, - unsigned long attrs) -{ - return dma_map_page_attrs(to_vmd_dev(dev), page, offset, size, dir, - attrs); -} - -static void vmd_unmap_page(struct device *dev, dma_addr_t addr, size_t size, - enum dma_data_direction dir, unsigned long attrs) -{ - dma_unmap_page_attrs(to_vmd_dev(dev), addr, size, dir, attrs); -} - -static int vmd_map_sg(struct device *dev, struct scatterlist *sg, int nents, - enum dma_data_direction dir, unsigned long attrs) -{ - return dma_map_sg_attrs(to_vmd_dev(dev), sg, nents, dir, attrs); -} - -static void vmd_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, - enum dma_data_direction dir, unsigned long attrs) -{ - dma_unmap_sg_attrs(to_vmd_dev(dev), sg, nents, dir, attrs); -} - -static void vmd_sync_single_for_cpu(struct device *dev, dma_addr_t addr, - size_t size, enum dma_data_direction dir) -{ - dma_sync_single_for_cpu(to_vmd_dev(dev), addr, size, dir); -} - -static void vmd_sync_single_for_device(struct device *dev, dma_addr_t addr, - size_t size, enum dma_data_direction dir) -{ - dma_sync_single_for_device(to_vmd_dev(dev), addr, size, dir); -} - -static void vmd_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir) -{ - dma_sync_sg_for_cpu(to_vmd_dev(dev), sg, nents, dir); -} - -static void vmd_sync_sg_for_device(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir) -{ - dma_sync_sg_for_device(to_vmd_dev(dev), sg, nents, dir); -} - -static int vmd_dma_supported(struct device *dev, u64 mask) -{ - return dma_supported(to_vmd_dev(dev), mask); -} - -static u64 vmd_get_required_mask(struct device *dev) -{ - return dma_get_required_mask(to_vmd_dev(dev)); -} - -static void vmd_teardown_dma_ops(struct vmd_dev *vmd) -{ - struct dma_domain *domain = &vmd->dma_domain; - - if (get_dma_ops(&vmd->dev->dev)) - del_dma_domain(domain); -} - -#define ASSIGN_VMD_DMA_OPS(source, dest, fn) \ - do { \ - if (source->fn) \ - dest->fn = vmd_##fn; \ - } while (0) - -static void vmd_setup_dma_ops(struct vmd_dev *vmd) -{ - const struct dma_map_ops *source = get_dma_ops(&vmd->dev->dev); - struct dma_map_ops *dest = &vmd->dma_ops; - struct dma_domain *domain = &vmd->dma_domain; - - domain->domain_nr = vmd->sysdata.domain; - domain->dma_ops = dest; - - if (!source) - return; - ASSIGN_VMD_DMA_OPS(source, dest, alloc); - ASSIGN_VMD_DMA_OPS(source, dest, free); - ASSIGN_VMD_DMA_OPS(source, dest, mmap); - ASSIGN_VMD_DMA_OPS(source, dest, get_sgtable); - ASSIGN_VMD_DMA_OPS(source, dest, map_page); - ASSIGN_VMD_DMA_OPS(source, dest, unmap_page); - ASSIGN_VMD_DMA_OPS(source, dest, map_sg); - ASSIGN_VMD_DMA_OPS(source, dest, unmap_sg); - ASSIGN_VMD_DMA_OPS(source, dest, sync_single_for_cpu); - ASSIGN_VMD_DMA_OPS(source, dest, sync_single_for_device); - ASSIGN_VMD_DMA_OPS(source, dest, sync_sg_for_cpu); - ASSIGN_VMD_DMA_OPS(source, dest, sync_sg_for_device); - ASSIGN_VMD_DMA_OPS(source, dest, dma_supported); - ASSIGN_VMD_DMA_OPS(source, dest, get_required_mask); - add_dma_domain(domain); -} -#undef ASSIGN_VMD_DMA_OPS - static char __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus, unsigned int devfn, int reg, int len) { @@ -709,7 +561,6 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) } vmd_attach_resources(vmd); - vmd_setup_dma_ops(vmd); dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain); pci_scan_child_bus(vmd->bus); @@ -824,7 +675,6 @@ static void vmd_remove(struct pci_dev *dev) pci_stop_root_bus(vmd->bus); pci_remove_root_bus(vmd->bus); vmd_cleanup_srcu(vmd); - vmd_teardown_dma_ops(vmd); vmd_detach_resources(vmd); irq_domain_remove(vmd->irq_domain); } From dab0198413d227f13be7da8abf0d5bc8620427f0 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 21 Jan 2020 06:37:51 -0700 Subject: [PATCH 48/60] x86/PCI: Remove X86_DEV_DMA_OPS There are no users of X86_DEV_DMA_OPS left, so remove the code. Link: https://lore.kernel.org/r/1579613871-301529-8-git-send-email-jonathan.derrick@intel.com Signed-off-by: Christoph Hellwig Signed-off-by: Bjorn Helgaas Reviewed-by: Jon Derrick --- arch/x86/Kconfig | 3 --- arch/x86/include/asm/device.h | 10 --------- arch/x86/pci/common.c | 38 ----------------------------------- 3 files changed, 51 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 5e8949953660..77f9426b47c8 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2955,9 +2955,6 @@ config HAVE_ATOMIC_IOMAP def_bool y depends on X86_32 -config X86_DEV_DMA_OPS - bool - source "drivers/firmware/Kconfig" source "arch/x86/kvm/Kconfig" diff --git a/arch/x86/include/asm/device.h b/arch/x86/include/asm/device.h index 5e12c63b47aa..7e31f7f1bb06 100644 --- a/arch/x86/include/asm/device.h +++ b/arch/x86/include/asm/device.h @@ -8,16 +8,6 @@ struct dev_archdata { #endif }; -#if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS) -struct dma_domain { - struct list_head node; - const struct dma_map_ops *dma_ops; - int domain_nr; -}; -void add_dma_domain(struct dma_domain *domain); -void del_dma_domain(struct dma_domain *domain); -#endif - struct pdev_archdata { }; diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index fe21a5c557e6..df1d95913d4e 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -625,43 +625,6 @@ unsigned int pcibios_assign_all_busses(void) return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0; } -#if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS) -static LIST_HEAD(dma_domain_list); -static DEFINE_SPINLOCK(dma_domain_list_lock); - -void add_dma_domain(struct dma_domain *domain) -{ - spin_lock(&dma_domain_list_lock); - list_add(&domain->node, &dma_domain_list); - spin_unlock(&dma_domain_list_lock); -} -EXPORT_SYMBOL_GPL(add_dma_domain); - -void del_dma_domain(struct dma_domain *domain) -{ - spin_lock(&dma_domain_list_lock); - list_del(&domain->node); - spin_unlock(&dma_domain_list_lock); -} -EXPORT_SYMBOL_GPL(del_dma_domain); - -static void set_dma_domain_ops(struct pci_dev *pdev) -{ - struct dma_domain *domain; - - spin_lock(&dma_domain_list_lock); - list_for_each_entry(domain, &dma_domain_list, node) { - if (pci_domain_nr(pdev->bus) == domain->domain_nr) { - pdev->dev.dma_ops = domain->dma_ops; - break; - } - } - spin_unlock(&dma_domain_list_lock); -} -#else -static void set_dma_domain_ops(struct pci_dev *pdev) {} -#endif - static void set_dev_domain_options(struct pci_dev *pdev) { if (is_vmd(pdev->bus)) @@ -697,7 +660,6 @@ int pcibios_add_device(struct pci_dev *dev) pa_data = data->next; memunmap(data); } - set_dma_domain_ops(dev); set_dev_domain_options(dev); return 0; } From c0452137034bda8f686dd9a2e167949bfffd6776 Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Mon, 16 Dec 2019 12:01:09 +0100 Subject: [PATCH 49/60] PCI: brcmstb: Add Broadcom STB PCIe host controller driver This adds a basic driver for Broadcom's STB PCIe controller, for now aimed at Raspberry Pi 4's SoC, bcm2711. Signed-off-by: Jim Quinlan Co-developed-by: Nicolas Saenz Julienne Signed-off-by: Nicolas Saenz Julienne [lorenzo.pieralisi@arm.com: updated brcm_pcie_get_rc_bar2_size_and_offset()according to https://lore.kernel.org/linux-pci/be8ddb33a7360af1815cf686f77f3f0913d02be3.camel@suse.de] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Andrew Murray Reviewed-by: Jeremy Linton --- drivers/pci/controller/Kconfig | 8 + drivers/pci/controller/Makefile | 1 + drivers/pci/controller/pcie-brcmstb.c | 755 ++++++++++++++++++++++++++ 3 files changed, 764 insertions(+) create mode 100644 drivers/pci/controller/pcie-brcmstb.c diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index c77069c8ee5d..27504f108ee5 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -253,6 +253,14 @@ config VMD To compile this driver as a module, choose M here: the module will be called vmd. +config PCIE_BRCMSTB + tristate "Broadcom Brcmstb PCIe host controller" + depends on ARCH_BCM2835 || COMPILE_TEST + depends on OF + help + Say Y here to enable PCIe host controller support for + Broadcom STB based SoCs, like the Raspberry Pi 4. + config PCI_HYPERV_INTERFACE tristate "Hyper-V PCI Interface" depends on X86 && HYPERV && PCI_MSI && PCI_MSI_IRQ_DOMAIN && X86_64 diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index 3d4f597f15ce..01b2502a5323 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o obj-$(CONFIG_VMD) += vmd.o +obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW obj-y += dwc/ diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c new file mode 100644 index 000000000000..3250a2e6b1b4 --- /dev/null +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -0,0 +1,755 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2009 - 2019 Broadcom */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +/* BRCM_PCIE_CAP_REGS - Offset for the mandatory capability config regs */ +#define BRCM_PCIE_CAP_REGS 0x00ac + +/* Broadcom STB PCIe Register Offsets */ +#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1 0x0188 +#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK 0xc +#define PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN 0x0 + +#define PCIE_RC_CFG_PRIV1_ID_VAL3 0x043c +#define PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK 0xffffff + +#define PCIE_RC_DL_MDIO_ADDR 0x1100 +#define PCIE_RC_DL_MDIO_WR_DATA 0x1104 +#define PCIE_RC_DL_MDIO_RD_DATA 0x1108 + +#define PCIE_MISC_MISC_CTRL 0x4008 +#define PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK 0x1000 +#define PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK 0x2000 +#define PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK 0x300000 +#define PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_128 0x0 +#define PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK 0xf8000000 + +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO 0x400c +#define PCIE_MEM_WIN0_LO(win) \ + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO + ((win) * 4) + +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI 0x4010 +#define PCIE_MEM_WIN0_HI(win) \ + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI + ((win) * 4) + +#define PCIE_MISC_RC_BAR1_CONFIG_LO 0x402c +#define PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK 0x1f + +#define PCIE_MISC_RC_BAR2_CONFIG_LO 0x4034 +#define PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK 0x1f +#define PCIE_MISC_RC_BAR2_CONFIG_HI 0x4038 + +#define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c +#define PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f + +#define PCIE_MISC_PCIE_CTRL 0x4064 +#define PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK 0x1 + +#define PCIE_MISC_PCIE_STATUS 0x4068 +#define PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK 0x80 +#define PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK 0x20 +#define PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK 0x10 +#define PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK 0x40 + +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT 0x4070 +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK 0xfff00000 +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK 0xfff0 +#define PCIE_MEM_WIN0_BASE_LIMIT(win) \ + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT + ((win) * 4) + +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI 0x4080 +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK 0xff +#define PCIE_MEM_WIN0_BASE_HI(win) \ + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI + ((win) * 8) + +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI 0x4084 +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK 0xff +#define PCIE_MEM_WIN0_LIMIT_HI(win) \ + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8) + +#define PCIE_MISC_HARD_PCIE_HARD_DEBUG 0x4204 +#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK 0x2 +#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000 + +#define PCIE_MSI_INTR2_STATUS 0x4500 +#define PCIE_MSI_INTR2_CLR 0x4508 +#define PCIE_MSI_INTR2_MASK_SET 0x4510 +#define PCIE_MSI_INTR2_MASK_CLR 0x4514 + +#define PCIE_EXT_CFG_DATA 0x8000 + +#define PCIE_EXT_CFG_INDEX 0x9000 +#define PCIE_EXT_BUSNUM_SHIFT 20 +#define PCIE_EXT_SLOT_SHIFT 15 +#define PCIE_EXT_FUNC_SHIFT 12 + +#define PCIE_RGR1_SW_INIT_1 0x9210 +#define PCIE_RGR1_SW_INIT_1_PERST_MASK 0x1 +#define PCIE_RGR1_SW_INIT_1_INIT_MASK 0x2 + +/* PCIe parameters */ +#define BRCM_NUM_PCIE_OUT_WINS 0x4 + +/* MDIO registers */ +#define MDIO_PORT0 0x0 +#define MDIO_DATA_MASK 0x7fffffff +#define MDIO_PORT_MASK 0xf0000 +#define MDIO_REGAD_MASK 0xffff +#define MDIO_CMD_MASK 0xfff00000 +#define MDIO_CMD_READ 0x1 +#define MDIO_CMD_WRITE 0x0 +#define MDIO_DATA_DONE_MASK 0x80000000 +#define MDIO_RD_DONE(x) (((x) & MDIO_DATA_DONE_MASK) ? 1 : 0) +#define MDIO_WT_DONE(x) (((x) & MDIO_DATA_DONE_MASK) ? 0 : 1) +#define SSC_REGS_ADDR 0x1100 +#define SET_ADDR_OFFSET 0x1f +#define SSC_CNTL_OFFSET 0x2 +#define SSC_CNTL_OVRD_EN_MASK 0x8000 +#define SSC_CNTL_OVRD_VAL_MASK 0x4000 +#define SSC_STATUS_OFFSET 0x1 +#define SSC_STATUS_SSC_MASK 0x400 +#define SSC_STATUS_PLL_LOCK_MASK 0x800 + +/* Internal PCIe Host Controller Information.*/ +struct brcm_pcie { + struct device *dev; + void __iomem *base; + struct clk *clk; + struct pci_bus *root_bus; + struct device_node *np; + bool ssc; + int gen; +}; + +/* + * This is to convert the size of the inbound "BAR" region to the + * non-linear values of PCIE_X_MISC_RC_BAR[123]_CONFIG_LO.SIZE + */ +static int brcm_pcie_encode_ibar_size(u64 size) +{ + int log2_in = ilog2(size); + + if (log2_in >= 12 && log2_in <= 15) + /* Covers 4KB to 32KB (inclusive) */ + return (log2_in - 12) + 0x1c; + else if (log2_in >= 16 && log2_in <= 35) + /* Covers 64KB to 32GB, (inclusive) */ + return log2_in - 15; + /* Something is awry so disable */ + return 0; +} + +static u32 brcm_pcie_mdio_form_pkt(int port, int regad, int cmd) +{ + u32 pkt = 0; + + pkt |= FIELD_PREP(MDIO_PORT_MASK, port); + pkt |= FIELD_PREP(MDIO_REGAD_MASK, regad); + pkt |= FIELD_PREP(MDIO_CMD_MASK, cmd); + + return pkt; +} + +/* negative return value indicates error */ +static int brcm_pcie_mdio_read(void __iomem *base, u8 port, u8 regad, u32 *val) +{ + int tries; + u32 data; + + writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_READ), + base + PCIE_RC_DL_MDIO_ADDR); + readl(base + PCIE_RC_DL_MDIO_ADDR); + + data = readl(base + PCIE_RC_DL_MDIO_RD_DATA); + for (tries = 0; !MDIO_RD_DONE(data) && tries < 10; tries++) { + udelay(10); + data = readl(base + PCIE_RC_DL_MDIO_RD_DATA); + } + + *val = FIELD_GET(MDIO_DATA_MASK, data); + return MDIO_RD_DONE(data) ? 0 : -EIO; +} + +/* negative return value indicates error */ +static int brcm_pcie_mdio_write(void __iomem *base, u8 port, + u8 regad, u16 wrdata) +{ + int tries; + u32 data; + + writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_WRITE), + base + PCIE_RC_DL_MDIO_ADDR); + readl(base + PCIE_RC_DL_MDIO_ADDR); + writel(MDIO_DATA_DONE_MASK | wrdata, base + PCIE_RC_DL_MDIO_WR_DATA); + + data = readl(base + PCIE_RC_DL_MDIO_WR_DATA); + for (tries = 0; !MDIO_WT_DONE(data) && tries < 10; tries++) { + udelay(10); + data = readl(base + PCIE_RC_DL_MDIO_WR_DATA); + } + + return MDIO_WT_DONE(data) ? 0 : -EIO; +} + +/* + * Configures device for Spread Spectrum Clocking (SSC) mode; a negative + * return value indicates error. + */ +static int brcm_pcie_set_ssc(struct brcm_pcie *pcie) +{ + int pll, ssc; + int ret; + u32 tmp; + + ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET, + SSC_REGS_ADDR); + if (ret < 0) + return ret; + + ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, + SSC_CNTL_OFFSET, &tmp); + if (ret < 0) + return ret; + + u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_EN_MASK); + u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_VAL_MASK); + ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, + SSC_CNTL_OFFSET, tmp); + if (ret < 0) + return ret; + + usleep_range(1000, 2000); + ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, + SSC_STATUS_OFFSET, &tmp); + if (ret < 0) + return ret; + + ssc = FIELD_GET(SSC_STATUS_SSC_MASK, tmp); + pll = FIELD_GET(SSC_STATUS_PLL_LOCK_MASK, tmp); + + return ssc && pll ? 0 : -EIO; +} + +/* Limits operation to a specific generation (1, 2, or 3) */ +static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen) +{ + u16 lnkctl2 = readw(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2); + u32 lnkcap = readl(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP); + + lnkcap = (lnkcap & ~PCI_EXP_LNKCAP_SLS) | gen; + writel(lnkcap, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP); + + lnkctl2 = (lnkctl2 & ~0xf) | gen; + writew(lnkctl2, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2); +} + +static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie, + unsigned int win, u64 cpu_addr, + u64 pcie_addr, u64 size) +{ + u32 cpu_addr_mb_high, limit_addr_mb_high; + phys_addr_t cpu_addr_mb, limit_addr_mb; + int high_addr_shift; + u32 tmp; + + /* Set the base of the pcie_addr window */ + writel(lower_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_LO(win)); + writel(upper_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_HI(win)); + + /* Write the addr base & limit lower bits (in MBs) */ + cpu_addr_mb = cpu_addr / SZ_1M; + limit_addr_mb = (cpu_addr + size - 1) / SZ_1M; + + tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win)); + u32p_replace_bits(&tmp, cpu_addr_mb, + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK); + u32p_replace_bits(&tmp, limit_addr_mb, + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK); + writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win)); + + /* Write the cpu & limit addr upper bits */ + high_addr_shift = + HWEIGHT32(PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK); + + cpu_addr_mb_high = cpu_addr_mb >> high_addr_shift; + tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_HI(win)); + u32p_replace_bits(&tmp, cpu_addr_mb_high, + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK); + writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_HI(win)); + + limit_addr_mb_high = limit_addr_mb >> high_addr_shift; + tmp = readl(pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win)); + u32p_replace_bits(&tmp, limit_addr_mb_high, + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK); + writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win)); +} + +/* The controller is capable of serving in both RC and EP roles */ +static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie) +{ + void __iomem *base = pcie->base; + u32 val = readl(base + PCIE_MISC_PCIE_STATUS); + + return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK, val); +} + +static bool brcm_pcie_link_up(struct brcm_pcie *pcie) +{ + u32 val = readl(pcie->base + PCIE_MISC_PCIE_STATUS); + u32 dla = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK, val); + u32 plu = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK, val); + + return dla && plu; +} + +/* Configuration space read/write support */ +static inline int brcm_pcie_cfg_index(int busnr, int devfn, int reg) +{ + return ((PCI_SLOT(devfn) & 0x1f) << PCIE_EXT_SLOT_SHIFT) + | ((PCI_FUNC(devfn) & 0x07) << PCIE_EXT_FUNC_SHIFT) + | (busnr << PCIE_EXT_BUSNUM_SHIFT) + | (reg & ~3); +} + +static void __iomem *brcm_pcie_map_conf(struct pci_bus *bus, unsigned int devfn, + int where) +{ + struct brcm_pcie *pcie = bus->sysdata; + void __iomem *base = pcie->base; + int idx; + + /* Accesses to the RC go right to the RC registers if slot==0 */ + if (pci_is_root_bus(bus)) + return PCI_SLOT(devfn) ? NULL : base + where; + + /* For devices, write to the config space index register */ + idx = brcm_pcie_cfg_index(bus->number, devfn, 0); + writel(idx, pcie->base + PCIE_EXT_CFG_INDEX); + return base + PCIE_EXT_CFG_DATA + where; +} + +static struct pci_ops brcm_pcie_ops = { + .map_bus = brcm_pcie_map_conf, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +static inline void brcm_pcie_bridge_sw_init_set(struct brcm_pcie *pcie, u32 val) +{ + u32 tmp; + + tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1); + u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_INIT_MASK); + writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1); +} + +static inline void brcm_pcie_perst_set(struct brcm_pcie *pcie, u32 val) +{ + u32 tmp; + + tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1); + u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_PERST_MASK); + writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1); +} + +static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie, + u64 *rc_bar2_size, + u64 *rc_bar2_offset) +{ + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + struct device *dev = pcie->dev; + struct resource_entry *entry; + + entry = resource_list_first_type(&bridge->dma_ranges, IORESOURCE_MEM); + if (!entry) + return -ENODEV; + + + /* + * The controller expects the inbound window offset to be calculated as + * the difference between PCIe's address space and CPU's. The offset + * provided by the firmware is calculated the opposite way, so we + * negate it. + */ + *rc_bar2_offset = -entry->offset; + *rc_bar2_size = 1ULL << fls64(entry->res->end - entry->res->start); + + /* + * We validate the inbound memory view even though we should trust + * whatever the device-tree provides. This is because of an HW issue on + * early Raspberry Pi 4's revisions (bcm2711). It turns out its + * firmware has to dynamically edit dma-ranges due to a bug on the + * PCIe controller integration, which prohibits any access above the + * lower 3GB of memory. Given this, we decided to keep the dma-ranges + * in check, avoiding hard to debug device-tree related issues in the + * future: + * + * The PCIe host controller by design must set the inbound viewport to + * be a contiguous arrangement of all of the system's memory. In + * addition, its size mut be a power of two. To further complicate + * matters, the viewport must start on a pcie-address that is aligned + * on a multiple of its size. If a portion of the viewport does not + * represent system memory -- e.g. 3GB of memory requires a 4GB + * viewport -- we can map the outbound memory in or after 3GB and even + * though the viewport will overlap the outbound memory the controller + * will know to send outbound memory downstream and everything else + * upstream. + * + * For example: + * + * - The best-case scenario, memory up to 3GB, is to place the inbound + * region in the first 4GB of pcie-space, as some legacy devices can + * only address 32bits. We would also like to put the MSI under 4GB + * as well, since some devices require a 32bit MSI target address. + * + * - If the system memory is 4GB or larger we cannot start the inbound + * region at location 0 (since we have to allow some space for + * outbound memory @ 3GB). So instead it will start at the 1x + * multiple of its size + */ + if (!*rc_bar2_size || *rc_bar2_offset % *rc_bar2_size || + (*rc_bar2_offset < SZ_4G && *rc_bar2_offset > SZ_2G)) { + dev_err(dev, "Invalid rc_bar2_offset/size: size 0x%llx, off 0x%llx\n", + *rc_bar2_size, *rc_bar2_offset); + return -EINVAL; + } + + return 0; +} + +static int brcm_pcie_setup(struct brcm_pcie *pcie) +{ + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + u64 rc_bar2_offset, rc_bar2_size; + void __iomem *base = pcie->base; + struct device *dev = pcie->dev; + struct resource_entry *entry; + unsigned int scb_size_val; + bool ssc_good = false; + struct resource *res; + int num_out_wins = 0; + u16 nlw, cls, lnksta; + int i, ret; + u32 tmp; + + /* Reset the bridge */ + brcm_pcie_bridge_sw_init_set(pcie, 1); + + usleep_range(100, 200); + + /* Take the bridge out of reset */ + brcm_pcie_bridge_sw_init_set(pcie, 0); + + tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK; + writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + /* Wait for SerDes to be stable */ + usleep_range(100, 200); + + /* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */ + u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK); + u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK); + u32p_replace_bits(&tmp, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_128, + PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK); + writel(tmp, base + PCIE_MISC_MISC_CTRL); + + ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size, + &rc_bar2_offset); + if (ret) + return ret; + + tmp = lower_32_bits(rc_bar2_offset); + u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(rc_bar2_size), + PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK); + writel(tmp, base + PCIE_MISC_RC_BAR2_CONFIG_LO); + writel(upper_32_bits(rc_bar2_offset), + base + PCIE_MISC_RC_BAR2_CONFIG_HI); + + scb_size_val = rc_bar2_size ? + ilog2(rc_bar2_size) - 15 : 0xf; /* 0xf is 1GB */ + tmp = readl(base + PCIE_MISC_MISC_CTRL); + u32p_replace_bits(&tmp, scb_size_val, + PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK); + writel(tmp, base + PCIE_MISC_MISC_CTRL); + + /* disable the PCIe->GISB memory window (RC_BAR1) */ + tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO); + tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK; + writel(tmp, base + PCIE_MISC_RC_BAR1_CONFIG_LO); + + /* disable the PCIe->SCB memory window (RC_BAR3) */ + tmp = readl(base + PCIE_MISC_RC_BAR3_CONFIG_LO); + tmp &= ~PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK; + writel(tmp, base + PCIE_MISC_RC_BAR3_CONFIG_LO); + + /* Mask all interrupts since we are not handling any yet */ + writel(0xffffffff, pcie->base + PCIE_MSI_INTR2_MASK_SET); + + /* clear any interrupts we find on boot */ + writel(0xffffffff, pcie->base + PCIE_MSI_INTR2_CLR); + + if (pcie->gen) + brcm_pcie_set_gen(pcie, pcie->gen); + + /* Unassert the fundamental reset */ + brcm_pcie_perst_set(pcie, 0); + + /* + * Give the RC/EP time to wake up, before trying to configure RC. + * Intermittently check status for link-up, up to a total of 100ms. + */ + for (i = 0; i < 100 && !brcm_pcie_link_up(pcie); i += 5) + msleep(5); + + if (!brcm_pcie_link_up(pcie)) { + dev_err(dev, "link down\n"); + return -ENODEV; + } + + if (!brcm_pcie_rc_mode(pcie)) { + dev_err(dev, "PCIe misconfigured; is in EP mode\n"); + return -EINVAL; + } + + resource_list_for_each_entry(entry, &bridge->windows) { + res = entry->res; + + if (resource_type(res) != IORESOURCE_MEM) + continue; + + if (num_out_wins >= BRCM_NUM_PCIE_OUT_WINS) { + dev_err(pcie->dev, "too many outbound wins\n"); + return -EINVAL; + } + + brcm_pcie_set_outbound_win(pcie, num_out_wins, res->start, + res->start - entry->offset, + resource_size(res)); + num_out_wins++; + } + + /* + * For config space accesses on the RC, show the right class for + * a PCIe-PCIe bridge (the default setting is to be EP mode). + */ + tmp = readl(base + PCIE_RC_CFG_PRIV1_ID_VAL3); + u32p_replace_bits(&tmp, 0x060400, + PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK); + writel(tmp, base + PCIE_RC_CFG_PRIV1_ID_VAL3); + + if (pcie->ssc) { + ret = brcm_pcie_set_ssc(pcie); + if (ret == 0) + ssc_good = true; + else + dev_err(dev, "failed attempt to enter ssc mode\n"); + } + + lnksta = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKSTA); + cls = FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta); + nlw = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta); + dev_info(dev, "link up, %s x%u %s\n", + PCIE_SPEED2STR(cls + PCI_SPEED_133MHz_PCIX_533), + nlw, ssc_good ? "(SSC)" : "(!SSC)"); + + /* PCIe->SCB endian mode for BAR */ + tmp = readl(base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1); + u32p_replace_bits(&tmp, PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN, + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK); + writel(tmp, base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1); + + /* + * Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1 + * is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1. + */ + tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK; + writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + + return 0; +} + +/* L23 is a low-power PCIe link state */ +static void brcm_pcie_enter_l23(struct brcm_pcie *pcie) +{ + void __iomem *base = pcie->base; + int l23, i; + u32 tmp; + + /* Assert request for L23 */ + tmp = readl(base + PCIE_MISC_PCIE_CTRL); + u32p_replace_bits(&tmp, 1, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK); + writel(tmp, base + PCIE_MISC_PCIE_CTRL); + + /* Wait up to 36 msec for L23 */ + tmp = readl(base + PCIE_MISC_PCIE_STATUS); + l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK, tmp); + for (i = 0; i < 15 && !l23; i++) { + usleep_range(2000, 2400); + tmp = readl(base + PCIE_MISC_PCIE_STATUS); + l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK, + tmp); + } + + if (!l23) + dev_err(pcie->dev, "failed to enter low-power link state\n"); +} + +static void brcm_pcie_turn_off(struct brcm_pcie *pcie) +{ + void __iomem *base = pcie->base; + int tmp; + + if (brcm_pcie_link_up(pcie)) + brcm_pcie_enter_l23(pcie); + /* Assert fundamental reset */ + brcm_pcie_perst_set(pcie, 1); + + /* Deassert request for L23 in case it was asserted */ + tmp = readl(base + PCIE_MISC_PCIE_CTRL); + u32p_replace_bits(&tmp, 0, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK); + writel(tmp, base + PCIE_MISC_PCIE_CTRL); + + /* Turn off SerDes */ + tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK); + writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + + /* Shutdown PCIe bridge */ + brcm_pcie_bridge_sw_init_set(pcie, 1); +} + +static void __brcm_pcie_remove(struct brcm_pcie *pcie) +{ + brcm_pcie_turn_off(pcie); + clk_disable_unprepare(pcie->clk); + clk_put(pcie->clk); +} + +static int brcm_pcie_remove(struct platform_device *pdev) +{ + struct brcm_pcie *pcie = platform_get_drvdata(pdev); + + pci_stop_root_bus(pcie->root_bus); + pci_remove_root_bus(pcie->root_bus); + __brcm_pcie_remove(pcie); + + return 0; +} + +static int brcm_pcie_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct pci_host_bridge *bridge; + struct brcm_pcie *pcie; + struct pci_bus *child; + struct resource *res; + int ret; + + bridge = devm_pci_alloc_host_bridge(&pdev->dev, sizeof(*pcie)); + if (!bridge) + return -ENOMEM; + + pcie = pci_host_bridge_priv(bridge); + pcie->dev = &pdev->dev; + pcie->np = np; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pcie->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pcie->base)) + return PTR_ERR(pcie->base); + + pcie->clk = devm_clk_get_optional(&pdev->dev, "sw_pcie"); + if (IS_ERR(pcie->clk)) + return PTR_ERR(pcie->clk); + + ret = of_pci_get_max_link_speed(np); + pcie->gen = (ret < 0) ? 0 : ret; + + pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc"); + + ret = pci_parse_request_of_pci_ranges(pcie->dev, &bridge->windows, + &bridge->dma_ranges, NULL); + if (ret) + return ret; + + ret = clk_prepare_enable(pcie->clk); + if (ret) { + dev_err(&pdev->dev, "could not enable clock\n"); + return ret; + } + + ret = brcm_pcie_setup(pcie); + if (ret) + goto fail; + + bridge->dev.parent = &pdev->dev; + bridge->busnr = 0; + bridge->ops = &brcm_pcie_ops; + bridge->sysdata = pcie; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + + ret = pci_scan_root_bus_bridge(bridge); + if (ret < 0) { + dev_err(pcie->dev, "Scanning root bridge failed\n"); + goto fail; + } + + pci_assign_unassigned_bus_resources(bridge->bus); + list_for_each_entry(child, &bridge->bus->children, node) + pcie_bus_configure_settings(child); + pci_bus_add_devices(bridge->bus); + platform_set_drvdata(pdev, pcie); + pcie->root_bus = bridge->bus; + + return 0; +fail: + __brcm_pcie_remove(pcie); + return ret; +} + +static const struct of_device_id brcm_pcie_match[] = { + { .compatible = "brcm,bcm2711-pcie" }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcm_pcie_match); + +static struct platform_driver brcm_pcie_driver = { + .probe = brcm_pcie_probe, + .remove = brcm_pcie_remove, + .driver = { + .name = "brcm-pcie", + .of_match_table = brcm_pcie_match, + }, +}; +module_platform_driver(brcm_pcie_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Broadcom STB PCIe RC driver"); +MODULE_AUTHOR("Broadcom"); From 40ca1bf580ef24df30702032ba5e40dfdcaa200b Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Mon, 16 Dec 2019 12:01:10 +0100 Subject: [PATCH 50/60] PCI: brcmstb: Add MSI support This adds MSI support to the Broadcom STB PCIe host controller. The MSI controller is physically located within the PCIe block, however, there is no reason why the MSI controller could not be moved elsewhere in the future. MSIX is not supported by the HW. Since the internal Brcmstb MSI controller is intertwined with the PCIe controller, it is not its own platform device but rather part of the PCIe platform device. Signed-off-by: Jim Quinlan Co-developed-by: Nicolas Saenz Julienne Signed-off-by: Nicolas Saenz Julienne Signed-off-by: Lorenzo Pieralisi Reviewed-by: Marc Zyngier Reviewed-by: Andrew Murray --- drivers/pci/controller/Kconfig | 1 + drivers/pci/controller/pcie-brcmstb.c | 262 +++++++++++++++++++++++++- 2 files changed, 262 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 27504f108ee5..918e283bbff1 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -257,6 +257,7 @@ config PCIE_BRCMSTB tristate "Broadcom Brcmstb PCIe host controller" depends on ARCH_BCM2835 || COMPILE_TEST depends on OF + depends on PCI_MSI_IRQ_DOMAIN help Say Y here to enable PCIe host controller support for Broadcom STB based SoCs, like the Raspberry Pi 4. diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 3250a2e6b1b4..d20aabc26273 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -2,6 +2,7 @@ /* Copyright (C) 2009 - 2019 Broadcom */ #include +#include #include #include #include @@ -9,11 +10,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -67,6 +70,12 @@ #define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c #define PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f +#define PCIE_MISC_MSI_BAR_CONFIG_LO 0x4044 +#define PCIE_MISC_MSI_BAR_CONFIG_HI 0x4048 + +#define PCIE_MISC_MSI_DATA_CONFIG 0x404c +#define PCIE_MISC_MSI_DATA_CONFIG_VAL 0xffe06540 + #define PCIE_MISC_PCIE_CTRL 0x4064 #define PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK 0x1 @@ -114,6 +123,11 @@ /* PCIe parameters */ #define BRCM_NUM_PCIE_OUT_WINS 0x4 +#define BRCM_INT_PCI_MSI_NR 32 + +/* MSI target adresses */ +#define BRCM_MSI_TARGET_ADDR_LT_4GB 0x0fffffffcULL +#define BRCM_MSI_TARGET_ADDR_GT_4GB 0xffffffffcULL /* MDIO registers */ #define MDIO_PORT0 0x0 @@ -135,6 +149,19 @@ #define SSC_STATUS_SSC_MASK 0x400 #define SSC_STATUS_PLL_LOCK_MASK 0x800 +struct brcm_msi { + struct device *dev; + void __iomem *base; + struct device_node *np; + struct irq_domain *msi_domain; + struct irq_domain *inner_domain; + struct mutex lock; /* guards the alloc/free operations */ + u64 target_addr; + int irq; + /* used indicates which MSI interrupts have been alloc'd */ + unsigned long used; +}; + /* Internal PCIe Host Controller Information.*/ struct brcm_pcie { struct device *dev; @@ -144,6 +171,8 @@ struct brcm_pcie { struct device_node *np; bool ssc; int gen; + u64 msi_target_addr; + struct brcm_msi *msi; }; /* @@ -309,6 +338,215 @@ static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie, writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win)); } +static struct irq_chip brcm_msi_irq_chip = { + .name = "BRCM STB PCIe MSI", + .irq_ack = irq_chip_ack_parent, + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, +}; + +static struct msi_domain_info brcm_msi_domain_info = { + /* Multi MSI is supported by the controller, but not by this driver */ + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), + .chip = &brcm_msi_irq_chip, +}; + +static void brcm_pcie_msi_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long status, virq; + struct brcm_msi *msi; + struct device *dev; + u32 bit; + + chained_irq_enter(chip, desc); + msi = irq_desc_get_handler_data(desc); + dev = msi->dev; + + status = readl(msi->base + PCIE_MSI_INTR2_STATUS); + for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR) { + virq = irq_find_mapping(msi->inner_domain, bit); + if (virq) + generic_handle_irq(virq); + else + dev_dbg(dev, "unexpected MSI\n"); + } + + chained_irq_exit(chip, desc); +} + +static void brcm_msi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct brcm_msi *msi = irq_data_get_irq_chip_data(data); + + msg->address_lo = lower_32_bits(msi->target_addr); + msg->address_hi = upper_32_bits(msi->target_addr); + msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL) | data->hwirq; +} + +static int brcm_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static void brcm_msi_ack_irq(struct irq_data *data) +{ + struct brcm_msi *msi = irq_data_get_irq_chip_data(data); + + writel(1 << data->hwirq, msi->base + PCIE_MSI_INTR2_CLR); +} + + +static struct irq_chip brcm_msi_bottom_irq_chip = { + .name = "BRCM STB MSI", + .irq_compose_msi_msg = brcm_msi_compose_msi_msg, + .irq_set_affinity = brcm_msi_set_affinity, + .irq_ack = brcm_msi_ack_irq, +}; + +static int brcm_msi_alloc(struct brcm_msi *msi) +{ + int hwirq; + + mutex_lock(&msi->lock); + hwirq = bitmap_find_free_region(&msi->used, BRCM_INT_PCI_MSI_NR, 0); + mutex_unlock(&msi->lock); + + return hwirq; +} + +static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq) +{ + mutex_lock(&msi->lock); + bitmap_release_region(&msi->used, hwirq, 0); + mutex_unlock(&msi->lock); +} + +static int brcm_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct brcm_msi *msi = domain->host_data; + int hwirq; + + hwirq = brcm_msi_alloc(msi); + + if (hwirq < 0) + return hwirq; + + irq_domain_set_info(domain, virq, (irq_hw_number_t)hwirq, + &brcm_msi_bottom_irq_chip, domain->host_data, + handle_edge_irq, NULL, NULL); + return 0; +} + +static void brcm_irq_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct brcm_msi *msi = irq_data_get_irq_chip_data(d); + + brcm_msi_free(msi, d->hwirq); +} + +static const struct irq_domain_ops msi_domain_ops = { + .alloc = brcm_irq_domain_alloc, + .free = brcm_irq_domain_free, +}; + +static int brcm_allocate_domains(struct brcm_msi *msi) +{ + struct fwnode_handle *fwnode = of_node_to_fwnode(msi->np); + struct device *dev = msi->dev; + + msi->inner_domain = irq_domain_add_linear(NULL, BRCM_INT_PCI_MSI_NR, + &msi_domain_ops, msi); + if (!msi->inner_domain) { + dev_err(dev, "failed to create IRQ domain\n"); + return -ENOMEM; + } + + msi->msi_domain = pci_msi_create_irq_domain(fwnode, + &brcm_msi_domain_info, + msi->inner_domain); + if (!msi->msi_domain) { + dev_err(dev, "failed to create MSI domain\n"); + irq_domain_remove(msi->inner_domain); + return -ENOMEM; + } + + return 0; +} + +static void brcm_free_domains(struct brcm_msi *msi) +{ + irq_domain_remove(msi->msi_domain); + irq_domain_remove(msi->inner_domain); +} + +static void brcm_msi_remove(struct brcm_pcie *pcie) +{ + struct brcm_msi *msi = pcie->msi; + + if (!msi) + return; + irq_set_chained_handler(msi->irq, NULL); + irq_set_handler_data(msi->irq, NULL); + brcm_free_domains(msi); +} + +static void brcm_msi_set_regs(struct brcm_msi *msi) +{ + writel(0xffffffff, msi->base + PCIE_MSI_INTR2_MASK_CLR); + + /* + * The 0 bit of PCIE_MISC_MSI_BAR_CONFIG_LO is repurposed to MSI + * enable, which we set to 1. + */ + writel(lower_32_bits(msi->target_addr) | 0x1, + msi->base + PCIE_MISC_MSI_BAR_CONFIG_LO); + writel(upper_32_bits(msi->target_addr), + msi->base + PCIE_MISC_MSI_BAR_CONFIG_HI); + + writel(PCIE_MISC_MSI_DATA_CONFIG_VAL, + msi->base + PCIE_MISC_MSI_DATA_CONFIG); +} + +static int brcm_pcie_enable_msi(struct brcm_pcie *pcie) +{ + struct brcm_msi *msi; + int irq, ret; + struct device *dev = pcie->dev; + + irq = irq_of_parse_and_map(dev->of_node, 1); + if (irq <= 0) { + dev_err(dev, "cannot map MSI interrupt\n"); + return -ENODEV; + } + + msi = devm_kzalloc(dev, sizeof(struct brcm_msi), GFP_KERNEL); + if (!msi) + return -ENOMEM; + + mutex_init(&msi->lock); + msi->dev = dev; + msi->base = pcie->base; + msi->np = pcie->np; + msi->target_addr = pcie->msi_target_addr; + msi->irq = irq; + + ret = brcm_allocate_domains(msi); + if (ret) + return ret; + + irq_set_chained_handler_and_data(msi->irq, brcm_pcie_msi_isr, msi); + + brcm_msi_set_regs(msi); + pcie->msi = msi; + + return 0; +} + /* The controller is capable of serving in both RC and EP roles */ static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie) { @@ -497,6 +735,18 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK); writel(tmp, base + PCIE_MISC_MISC_CTRL); + /* + * We ideally want the MSI target address to be located in the 32bit + * addressable memory area. Some devices might depend on it. This is + * possible either when the inbound window is located above the lower + * 4GB or when the inbound area is smaller than 4GB (taking into + * account the rounding-up we're forced to perform). + */ + if (rc_bar2_offset >= SZ_4G || (rc_bar2_size + rc_bar2_offset) < SZ_4G) + pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB; + else + pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB; + /* disable the PCIe->GISB memory window (RC_BAR1) */ tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO); tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK; @@ -646,6 +896,7 @@ static void brcm_pcie_turn_off(struct brcm_pcie *pcie) static void __brcm_pcie_remove(struct brcm_pcie *pcie) { + brcm_msi_remove(pcie); brcm_pcie_turn_off(pcie); clk_disable_unprepare(pcie->clk); clk_put(pcie->clk); @@ -664,7 +915,7 @@ static int brcm_pcie_remove(struct platform_device *pdev) static int brcm_pcie_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; + struct device_node *np = pdev->dev.of_node, *msi_np; struct pci_host_bridge *bridge; struct brcm_pcie *pcie; struct pci_bus *child; @@ -708,6 +959,15 @@ static int brcm_pcie_probe(struct platform_device *pdev) if (ret) goto fail; + msi_np = of_parse_phandle(pcie->np, "msi-parent", 0); + if (pci_msi_enabled() && msi_np == pcie->np) { + ret = brcm_pcie_enable_msi(pcie); + if (ret) { + dev_err(pcie->dev, "probe of internal MSI failed"); + goto fail; + } + } + bridge->dev.parent = &pdev->dev; bridge->busnr = 0; bridge->ops = &brcm_pcie_ops; From 3d67a2dbdbe9400523164d8557a7a5fb7ef83c7e Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Mon, 6 Jan 2020 15:45:32 +0000 Subject: [PATCH 51/60] PCI: Remove unnecessary braces Remove unnecessary braces in pci_bus_distribute_available_resources(). No functional changes. Link: https://lore.kernel.org/r/PSXP216MB0438061CB4442460BB92A75F803C0@PSXP216MB0438.KORP216.PROD.OUTLOOK.COM Signed-off-by: Nicholas Johnson Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg --- drivers/pci/setup-bus.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 591161ce0f51..16300ad5e277 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1908,11 +1908,10 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, */ if (hotplug_bridges + normal_bridges == 1) { dev = list_first_entry(&bus->devices, struct pci_dev, bus_list); - if (dev->subordinate) { + if (dev->subordinate) pci_bus_distribute_available_resources(dev->subordinate, add_list, available_io, available_mmio, available_mmio_pref); - } return; } From db51b4c85fb756f33617c1d29643e57be9bd2f1d Mon Sep 17 00:00:00 2001 From: Sushma Kalakota Date: Wed, 8 Jan 2020 15:05:10 -0700 Subject: [PATCH 52/60] PCI: vmd: Add two VMD Device IDs Add new VMD device IDs that require the bus restriction mode. Signed-off-by: Sushma Kalakota Signed-off-by: Jon Derrick Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas --- drivers/pci/controller/vmd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index fe1acb0c037b..dac91d60701d 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -718,6 +718,10 @@ static const struct pci_device_id vmd_ids[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_28C0), .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW | VMD_FEAT_HAS_BUS_RESTRICTIONS,}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x467f), + .driver_data = VMD_FEAT_HAS_BUS_RESTRICTIONS,}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4c3d), + .driver_data = VMD_FEAT_HAS_BUS_RESTRICTIONS,}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B), .driver_data = VMD_FEAT_HAS_BUS_RESTRICTIONS,}, {0,} From 053eb5c150fd732624a1ea2f456337972d52b163 Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Tue, 28 Jan 2020 14:59:23 -0600 Subject: [PATCH 53/60] PCI: Rename variables In pci_bus_distribute_available_resources(), rename: io => io_per_hp mmio => mmio_per_hp mmio_pref => mmio_pref_per_hp No functional change; this is just to make a subsequent patch smaller. [bhelgaas: extracted from https://lore.kernel.org/r/PSXP216MB0438587C47CBEDF365B1EA27803C0@PSXP216MB0438.KORP216.PROD.OUTLOOK.COM] Signed-off-by: Nicholas Johnson Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg --- drivers/pci/setup-bus.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 16300ad5e277..9d167d5b2235 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1873,6 +1873,7 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, unsigned int normal_bridges = 0, hotplug_bridges = 0; struct resource *io_res, *mmio_res, *mmio_pref_res; struct pci_dev *dev, *bridge = bus->self; + resource_size_t io_per_hp, mmio_per_hp, mmio_pref_per_hp; io_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0]; mmio_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1]; @@ -1956,7 +1957,7 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, * resource space between hotplug bridges. */ for_each_pci_bridge(dev, bus) { - resource_size_t align, io, mmio, mmio_pref; + resource_size_t align; struct pci_bus *b; b = dev->subordinate; @@ -1969,22 +1970,25 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, * account. */ align = pci_resource_alignment(bridge, io_res); - io = div64_ul(available_io, hotplug_bridges); - io = min(ALIGN(io, align), remaining_io); - remaining_io -= io; + io_per_hp = div64_ul(available_io, hotplug_bridges); + io_per_hp = min(ALIGN(io_per_hp, align), remaining_io); + remaining_io -= io_per_hp; align = pci_resource_alignment(bridge, mmio_res); - mmio = div64_ul(available_mmio, hotplug_bridges); - mmio = min(ALIGN(mmio, align), remaining_mmio); - remaining_mmio -= mmio; + mmio_per_hp = div64_ul(available_mmio, hotplug_bridges); + mmio_per_hp = min(ALIGN(mmio_per_hp, align), remaining_mmio); + remaining_mmio -= mmio_per_hp; align = pci_resource_alignment(bridge, mmio_pref_res); - mmio_pref = div64_ul(available_mmio_pref, hotplug_bridges); - mmio_pref = min(ALIGN(mmio_pref, align), remaining_mmio_pref); - remaining_mmio_pref -= mmio_pref; + mmio_pref_per_hp = div64_ul(available_mmio_pref, + hotplug_bridges); + mmio_pref_per_hp = min(ALIGN(mmio_pref_per_hp, align), + remaining_mmio_pref); + remaining_mmio_pref -= mmio_pref_per_hp; - pci_bus_distribute_available_resources(b, add_list, io, mmio, - mmio_pref); + pci_bus_distribute_available_resources(b, add_list, io_per_hp, + mmio_per_hp, + mmio_pref_per_hp); } } From d555a50fd6e0280735cabf8581feff875f3f39d7 Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Mon, 6 Jan 2020 15:45:52 +0000 Subject: [PATCH 54/60] PCI: Pass size + alignment to pci_bus_distribute_available_resources() Change pci_bus_distribute_available_resources() arguments from resource_size_t to struct resource to add more information required to get the alignment correct for bridge windows with alignment >1M. We require (size, alignment), instead of just (size) which is what is currently available. The change from resource_size_t to struct resource does just that. Note that the struct resource arguments are passed by value and not by reference. We do not want to pass by reference and change the resource size of the parent bridge window. We only want the size information. No functional change intended. Link: https://lore.kernel.org/r/PSXP216MB0438587C47CBEDF365B1EA27803C0@PSXP216MB0438.KORP216.PROD.OUTLOOK.COM [bhelgaas: split parts to other patches to reduce the size of this one] Signed-off-by: Nicholas Johnson Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg --- drivers/pci/setup-bus.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 9d167d5b2235..7fa7ad1c3407 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1865,10 +1865,11 @@ static void extend_bridge_window(struct pci_dev *bridge, struct resource *res, static void pci_bus_distribute_available_resources(struct pci_bus *bus, struct list_head *add_list, - resource_size_t available_io, - resource_size_t available_mmio, - resource_size_t available_mmio_pref) + struct resource io, + struct resource mmio, + struct resource mmio_pref) { + resource_size_t available_io, available_mmio, available_mmio_pref; resource_size_t remaining_io, remaining_mmio, remaining_mmio_pref; unsigned int normal_bridges = 0, hotplug_bridges = 0; struct resource *io_res, *mmio_res, *mmio_pref_res; @@ -1885,6 +1886,10 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, * calculated in __pci_bus_size_bridges() which covers all the * devices currently connected to the port and below. */ + available_io = resource_size(&io); + available_mmio = resource_size(&mmio); + available_mmio_pref = resource_size(&mmio_pref); + extend_bridge_window(bridge, io_res, add_list, available_io); extend_bridge_window(bridge, mmio_res, add_list, available_mmio); extend_bridge_window(bridge, mmio_pref_res, add_list, @@ -1911,8 +1916,7 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, dev = list_first_entry(&bus->devices, struct pci_dev, bus_list); if (dev->subordinate) pci_bus_distribute_available_resources(dev->subordinate, - add_list, available_io, available_mmio, - available_mmio_pref); + add_list, io, mmio, mmio_pref); return; } @@ -1986,28 +1990,27 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, remaining_mmio_pref); remaining_mmio_pref -= mmio_pref_per_hp; - pci_bus_distribute_available_resources(b, add_list, io_per_hp, - mmio_per_hp, - mmio_pref_per_hp); + io.end = io.start + io_per_hp - 1; + mmio.end = mmio.start + mmio_per_hp - 1; + mmio_pref.end = mmio_pref.start + mmio_pref_per_hp - 1; + + pci_bus_distribute_available_resources(b, add_list, io, mmio, + mmio_pref); } } static void pci_bridge_distribute_available_resources(struct pci_dev *bridge, struct list_head *add_list) { - resource_size_t available_io, available_mmio, available_mmio_pref; - const struct resource *res; + struct resource available_io, available_mmio, available_mmio_pref; if (!bridge->is_hotplug_bridge) return; /* Take the initial extra resources from the hotplug port */ - res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0]; - available_io = resource_size(res); - res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1]; - available_mmio = resource_size(res); - res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2]; - available_mmio_pref = resource_size(res); + available_io = bridge->resource[PCI_BRIDGE_RESOURCES + 0]; + available_mmio = bridge->resource[PCI_BRIDGE_RESOURCES + 1]; + available_mmio_pref = bridge->resource[PCI_BRIDGE_RESOURCES + 2]; pci_bus_distribute_available_resources(bridge->subordinate, add_list, available_io, From 7779385484dad7c95b624f7c9ee1aa07ab8cf43b Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Tue, 28 Jan 2020 15:57:09 -0600 Subject: [PATCH 55/60] PCI: Remove local variable usage in pci_bus_distribute_available_resources() In pci_bus_distribute_available_resources(), use resource_size() rather than the local available_io, etc. No functional change intended; this just makes the preceding patch smaller. [bhelgaas: extracted from https://lore.kernel.org/r/PSXP216MB0438587C47CBEDF365B1EA27803C0@PSXP216MB0438.KORP216.PROD.OUTLOOK.COM] Signed-off-by: Nicholas Johnson Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg --- drivers/pci/setup-bus.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 7fa7ad1c3407..336c96c1d2d5 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1886,14 +1886,10 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, * calculated in __pci_bus_size_bridges() which covers all the * devices currently connected to the port and below. */ - available_io = resource_size(&io); - available_mmio = resource_size(&mmio); - available_mmio_pref = resource_size(&mmio_pref); - - extend_bridge_window(bridge, io_res, add_list, available_io); - extend_bridge_window(bridge, mmio_res, add_list, available_mmio); + extend_bridge_window(bridge, io_res, add_list, resource_size(&io)); + extend_bridge_window(bridge, mmio_res, add_list, resource_size(&mmio)); extend_bridge_window(bridge, mmio_pref_res, add_list, - available_mmio_pref); + resource_size(&mmio_pref)); /* * Calculate how many hotplug bridges and normal bridges there @@ -1929,9 +1925,9 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, * extra space reduced by the minimal required space for the * non-hotplug bridges. */ - remaining_io = available_io; - remaining_mmio = available_mmio; - remaining_mmio_pref = available_mmio_pref; + remaining_io = available_io = resource_size(&io); + remaining_mmio = available_mmio = resource_size(&mmio); + remaining_mmio_pref = available_mmio_pref = resource_size(&mmio_pref); for_each_pci_bridge(dev, bus) { const struct resource *res; From f924c26e4ee651493f602da2a3d7ff628824e636 Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Mon, 6 Jan 2020 15:46:13 +0000 Subject: [PATCH 56/60] PCI: Consider alignment of hot-added bridges when assigning resources Change pci_bus_distribute_available_resources() to better handle bridges with different resource alignment requirements. The arguments io, mmio and mmio_pref represent the start and end addresses of resource, into which we must fit the current bridge window. The steps taken by pci_bus_distribute_available_resources(): - For io, mmio and mmio_pref, increase .start to align with the alignment of the current bridge window (otherwise the current bridge window may not fit within the available range). - For io, mmio and mmio_pref, adjust the current bridge window to the size after the above. - Count the number of hotplug bridges and normal bridges on this bus. - If the total number of bridges is one, give that bridge all of the resources and return. - If there are no hotplug bridges, return. - For io, mmio and mmio_pref, increase .start by the amount required for each bridge resource on the bus for non hotplug bridges, giving extra room to make up for alignment of those resources. - For io, mmio and mmio_pref, calculate the resource size per hotplug bridge which is available after the previous steps. - For io, mmio and mmio_pref, distribute the resources to each hotplug bridge, with the sizes calculated above. The motivation for fixing this is enabling devices that require greater than 1MB alignment. This fixes the case where the user hot-adds devices with BAR alignment >1MB and Linux fails to assign resources to it. Link: https://bugzilla.kernel.org/show_bug.cgi?id=199581 Link: https://lore.kernel.org/r/PSXP216MB0438C2BFD0FD3691ED9C83F4803C0@PSXP216MB0438.KORP216.PROD.OUTLOOK.COM Reported-by: Mika Westerberg Signed-off-by: Nicholas Johnson Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg --- drivers/pci/setup-bus.c | 78 ++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 336c96c1d2d5..d76674ec712c 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1869,17 +1869,32 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, struct resource mmio, struct resource mmio_pref) { - resource_size_t available_io, available_mmio, available_mmio_pref; - resource_size_t remaining_io, remaining_mmio, remaining_mmio_pref; unsigned int normal_bridges = 0, hotplug_bridges = 0; struct resource *io_res, *mmio_res, *mmio_pref_res; struct pci_dev *dev, *bridge = bus->self; - resource_size_t io_per_hp, mmio_per_hp, mmio_pref_per_hp; + resource_size_t io_per_hp, mmio_per_hp, mmio_pref_per_hp, align; io_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0]; mmio_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1]; mmio_pref_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2]; + /* + * The alignment of this bridge is yet to be considered, hence it must + * be done now before extending its bridge window. + */ + align = pci_resource_alignment(bridge, io_res); + if (!io_res->parent && align) + io.start = min(ALIGN(io.start, align), io.end + 1); + + align = pci_resource_alignment(bridge, mmio_res); + if (!mmio_res->parent && align) + mmio.start = min(ALIGN(mmio.start, align), mmio.end + 1); + + align = pci_resource_alignment(bridge, mmio_pref_res); + if (!mmio_pref_res->parent && align) + mmio_pref.start = min(ALIGN(mmio_pref.start, align), + mmio_pref.end + 1); + /* * Update additional resource list (add_list) to fill all the * extra resource space available for this port except the space @@ -1925,12 +1940,9 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, * extra space reduced by the minimal required space for the * non-hotplug bridges. */ - remaining_io = available_io = resource_size(&io); - remaining_mmio = available_mmio = resource_size(&mmio); - remaining_mmio_pref = available_mmio_pref = resource_size(&mmio_pref); - for_each_pci_bridge(dev, bus) { - const struct resource *res; + resource_size_t used_size; + struct resource *res; if (dev->is_hotplug_bridge) continue; @@ -1940,24 +1952,39 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, * bridge and devices below it occupy. */ res = &dev->resource[PCI_BRIDGE_RESOURCES + 0]; - if (!res->parent && available_io > resource_size(res)) - remaining_io -= resource_size(res); + align = pci_resource_alignment(dev, res); + align = align ? ALIGN(io.start, align) - io.start : 0; + used_size = align + resource_size(res); + if (!res->parent) + io.start = min(io.start + used_size, io.end + 1); res = &dev->resource[PCI_BRIDGE_RESOURCES + 1]; - if (!res->parent && available_mmio > resource_size(res)) - remaining_mmio -= resource_size(res); + align = pci_resource_alignment(dev, res); + align = align ? ALIGN(mmio.start, align) - mmio.start : 0; + used_size = align + resource_size(res); + if (!res->parent) + mmio.start = min(mmio.start + used_size, mmio.end + 1); res = &dev->resource[PCI_BRIDGE_RESOURCES + 2]; - if (!res->parent && available_mmio_pref > resource_size(res)) - remaining_mmio_pref -= resource_size(res); + align = pci_resource_alignment(dev, res); + align = align ? ALIGN(mmio_pref.start, align) - + mmio_pref.start : 0; + used_size = align + resource_size(res); + if (!res->parent) + mmio_pref.start = min(mmio_pref.start + used_size, + mmio_pref.end + 1); } + io_per_hp = div64_ul(resource_size(&io), hotplug_bridges); + mmio_per_hp = div64_ul(resource_size(&mmio), hotplug_bridges); + mmio_pref_per_hp = div64_ul(resource_size(&mmio_pref), + hotplug_bridges); + /* * Go over devices on this bus and distribute the remaining * resource space between hotplug bridges. */ for_each_pci_bridge(dev, bus) { - resource_size_t align; struct pci_bus *b; b = dev->subordinate; @@ -1969,29 +1996,16 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, * hotplug-capable downstream ports taking alignment into * account. */ - align = pci_resource_alignment(bridge, io_res); - io_per_hp = div64_ul(available_io, hotplug_bridges); - io_per_hp = min(ALIGN(io_per_hp, align), remaining_io); - remaining_io -= io_per_hp; - - align = pci_resource_alignment(bridge, mmio_res); - mmio_per_hp = div64_ul(available_mmio, hotplug_bridges); - mmio_per_hp = min(ALIGN(mmio_per_hp, align), remaining_mmio); - remaining_mmio -= mmio_per_hp; - - align = pci_resource_alignment(bridge, mmio_pref_res); - mmio_pref_per_hp = div64_ul(available_mmio_pref, - hotplug_bridges); - mmio_pref_per_hp = min(ALIGN(mmio_pref_per_hp, align), - remaining_mmio_pref); - remaining_mmio_pref -= mmio_pref_per_hp; - io.end = io.start + io_per_hp - 1; mmio.end = mmio.start + mmio_per_hp - 1; mmio_pref.end = mmio_pref.start + mmio_pref_per_hp - 1; pci_bus_distribute_available_resources(b, add_list, io, mmio, mmio_pref); + + io.start += io_per_hp; + mmio.start += mmio_per_hp; + mmio_pref.start += mmio_pref_per_hp; } } From 3d264da9b741fc6503ea54e3bb65c08a77dabca4 Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Mon, 6 Jan 2020 15:47:05 +0000 Subject: [PATCH 57/60] PCI: Rename extend_bridge_window() parameter In extend_bridge_window(), change "available" parameter name to "new_size". This makes more sense as this parameter represents the new size for the window. No functional change intended. Link: https://lore.kernel.org/r/PSXP216MB043853617ECA4118C472A417803C0@PSXP216MB0438.KORP216.PROD.OUTLOOK.COM Signed-off-by: Nicholas Johnson Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg --- drivers/pci/setup-bus.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index d76674ec712c..85439adb747a 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1840,14 +1840,14 @@ void __init pci_assign_unassigned_resources(void) static void extend_bridge_window(struct pci_dev *bridge, struct resource *res, struct list_head *add_list, - resource_size_t available) + resource_size_t new_size) { struct pci_dev_resource *dev_res; if (res->parent) return; - if (resource_size(res) >= available) + if (resource_size(res) >= new_size) return; dev_res = res_to_dev_res(add_list, res); @@ -1855,10 +1855,10 @@ static void extend_bridge_window(struct pci_dev *bridge, struct resource *res, return; /* Is there room to extend the window? */ - if (available - resource_size(res) <= dev_res->add_size) + if (new_size - resource_size(res) <= dev_res->add_size) return; - dev_res->add_size = available - resource_size(res); + dev_res->add_size = new_size - resource_size(res); pci_dbg(bridge, "bridge window %pR extended by %pa\n", res, &dev_res->add_size); } From 1e58f4e1cb47de03988f8b14f07f6941acc7e669 Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Mon, 6 Jan 2020 15:47:26 +0000 Subject: [PATCH 58/60] PCI: Rename extend_bridge_window() to adjust_bridge_window() Rename extend_bridge_window() to adjust_bridge_window() to prepare for the fact that the window will be able to shrink. No functional change intended. Link: https://lore.kernel.org/r/PSXP216MB0438C47B3473D0C9DE531F18803C0@PSXP216MB0438.KORP216.PROD.OUTLOOK.COM Signed-off-by: Nicholas Johnson Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg --- drivers/pci/setup-bus.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 85439adb747a..12fe926e32b4 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1838,7 +1838,7 @@ void __init pci_assign_unassigned_resources(void) } } -static void extend_bridge_window(struct pci_dev *bridge, struct resource *res, +static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res, struct list_head *add_list, resource_size_t new_size) { @@ -1901,9 +1901,9 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, * calculated in __pci_bus_size_bridges() which covers all the * devices currently connected to the port and below. */ - extend_bridge_window(bridge, io_res, add_list, resource_size(&io)); - extend_bridge_window(bridge, mmio_res, add_list, resource_size(&mmio)); - extend_bridge_window(bridge, mmio_pref_res, add_list, + adjust_bridge_window(bridge, io_res, add_list, resource_size(&io)); + adjust_bridge_window(bridge, mmio_res, add_list, resource_size(&mmio)); + adjust_bridge_window(bridge, mmio_pref_res, add_list, resource_size(&mmio_pref)); /* From ae4611f1d7e99eda6916bbc5fc8df26516edf95e Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Mon, 6 Jan 2020 15:47:46 +0000 Subject: [PATCH 59/60] PCI: Set resource size directly in adjust_bridge_window() Change adjust_bridge_window() to set resource size directly instead of using additional resource lists. Because additional resource lists are optional resources, any algorithm that requires guaranteed allocation that uses them cannot be guaranteed to work. Remove the resource from add_list, as a zero-sized additional resource is redundant. Update comment in pci_bus_distribute_available_resources() to reflect the above changes. Link: https://lore.kernel.org/r/PSXP216MB04386BA48874B56BC5CB0292803C0@PSXP216MB0438.KORP216.PROD.OUTLOOK.COM Signed-off-by: Nicholas Johnson Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg --- drivers/pci/setup-bus.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 12fe926e32b4..742055908656 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1842,7 +1842,7 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res, struct list_head *add_list, resource_size_t new_size) { - struct pci_dev_resource *dev_res; + resource_size_t add_size; if (res->parent) return; @@ -1850,17 +1850,10 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res, if (resource_size(res) >= new_size) return; - dev_res = res_to_dev_res(add_list, res); - if (!dev_res) - return; - - /* Is there room to extend the window? */ - if (new_size - resource_size(res) <= dev_res->add_size) - return; - - dev_res->add_size = new_size - resource_size(res); - pci_dbg(bridge, "bridge window %pR extended by %pa\n", res, - &dev_res->add_size); + add_size = new_size - resource_size(res); + pci_dbg(bridge, "bridge window %pR extended by %pa\n", res, &add_size); + res->end = res->start + new_size - 1; + remove_from_list(add_list, res); } static void pci_bus_distribute_available_resources(struct pci_bus *bus, @@ -1896,10 +1889,8 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, mmio_pref.end + 1); /* - * Update additional resource list (add_list) to fill all the - * extra resource space available for this port except the space - * calculated in __pci_bus_size_bridges() which covers all the - * devices currently connected to the port and below. + * Now that we have adjusted for alignment, update the bridge window + * resources to fill as much remaining resource space as possible. */ adjust_bridge_window(bridge, io_res, add_list, resource_size(&io)); adjust_bridge_window(bridge, mmio_res, add_list, resource_size(&mmio)); From 948675736a77cb951b40bcb646d22dd794f8ed14 Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Mon, 6 Jan 2020 15:48:06 +0000 Subject: [PATCH 60/60] PCI: Allow adjust_bridge_window() to shrink resource if necessary Remove checks for resource size in adjust_bridge_window(). This is necessary to allow pci_bus_distribute_available_resources() to function when the kernel parameter "pci=hpmemsize=nn[KMG]" is used to allocate resources. Because the kernel parameter sets the size of all hotplug bridges to be the same, there are problems when nested hotplug bridges are encountered. Fitting a downstream hotplug bridge with size X and normal bridges with non-zero size Y into parent hotplug bridge with size X is impossible, and hence the downstream hotplug bridge needs to shrink to fit into its parent. Add check for if bridge is extended or shrunken and reflect that in the call to pci_dbg(). Reset the resource if its new size is zero (if we have run out of a bridge window resource) to prevent the PCI resource assignment code from attempting to assign a zero-sized resource. Link: https://lore.kernel.org/r/PSXP216MB0438D3E2CFE64EBAA32AF691803C0@PSXP216MB0438.KORP216.PROD.OUTLOOK.COM Signed-off-by: Nicholas Johnson Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg --- drivers/pci/setup-bus.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 742055908656..f2461bf9243d 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1842,16 +1842,24 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res, struct list_head *add_list, resource_size_t new_size) { - resource_size_t add_size; + resource_size_t add_size, size = resource_size(res); if (res->parent) return; - if (resource_size(res) >= new_size) + if (!new_size) return; - add_size = new_size - resource_size(res); - pci_dbg(bridge, "bridge window %pR extended by %pa\n", res, &add_size); + if (new_size > size) { + add_size = new_size - size; + pci_dbg(bridge, "bridge window %pR extended by %pa\n", res, + &add_size); + } else if (new_size < size) { + add_size = size - new_size; + pci_dbg(bridge, "bridge window %pR shrunken by %pa\n", res, + &add_size); + } + res->end = res->start + new_size - 1; remove_from_list(add_list, res); }