Merge tag 'pci-v5.6-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 31 Jan 2020 22:48:54 +0000 (14:48 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 31 Jan 2020 22:48:54 +0000 (14:48 -0800)
Pull PCI updates from Bjorn Helgaas:

 "Resource management:

   - Improve resource assignment for hot-added nested bridges, e.g.,
     Thunderbolt (Nicholas Johnson)

  Power management:

   - Optionally print config space of devices before suspend (Chen Yu)

   - Increase D3 delay for AMD Ryzen5/7 XHCI controllers (Daniel Drake)

  Virtualization:

   - Generalize DMA alias quirks (James Sewart)

   - Add DMA alias quirk for PLX PEX NTB (James Sewart)

   - Fix IOV memory leak (Navid Emamdoost)

  AER:

   - Log which device prevents error recovery (Yicong Yang)

  Peer-to-peer DMA:

   - Whitelist Intel SkyLake-E (Armen Baloyan)

  Broadcom iProc host bridge driver:

   - Apply PAXC quirk whether driver is built-in or module (Wei Liu)

  Broadcom STB host bridge driver:

   - Add Broadcom STB PCIe host controller driver (Jim Quinlan)

  Intel Gateway SoC host bridge driver:

   - Add driver for Intel Gateway SoC (Dilip Kota)

  Intel VMD host bridge driver:

   - Add support for DMA aliases on other buses (Jon Derrick)

   - Remove dma_map_ops overrides (Jon Derrick)

   - Remove now-unused X86_DEV_DMA_OPS (Christoph Hellwig)

  NVIDIA Tegra host bridge driver:

   - Fix Tegra30 afi_pex2_ctrl register offset (Marcel Ziswiler)

  Panasonic UniPhier host bridge driver:

   - Remove module code since driver can't be built as a module
     (Masahiro Yamada)

  Qualcomm host bridge driver:

   - Add support for SDM845 PCIe controller (Bjorn Andersson)

  TI Keystone host bridge driver:

   - Fix "num-viewport" DT property error handling (Kishon Vijay Abraham I)

   - Fix link training retries initiation (Yurii Monakov)

   - Fix outbound region mapping (Yurii Monakov)

  Misc:

   - Add Switchtec Gen4 support (Kelvin Cao)

   - Add Switchtec Intercomm Notify and Upstream Error Containment
     support (Logan Gunthorpe)

   - Use dma_set_mask_and_coherent() since Switchtec supports 64-bit
     addressing (Wesley Sheng)"

* tag 'pci-v5.6-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (60 commits)
  PCI: Allow adjust_bridge_window() to shrink resource if necessary
  PCI: Set resource size directly in adjust_bridge_window()
  PCI: Rename extend_bridge_window() to adjust_bridge_window()
  PCI: Rename extend_bridge_window() parameter
  PCI: Consider alignment of hot-added bridges when assigning resources
  PCI: Remove local variable usage in pci_bus_distribute_available_resources()
  PCI: Pass size + alignment to pci_bus_distribute_available_resources()
  PCI: Rename variables
  PCI: vmd: Add two VMD Device IDs
  PCI: Remove unnecessary braces
  PCI: brcmstb: Add MSI support
  PCI: brcmstb: Add Broadcom STB PCIe host controller driver
  x86/PCI: Remove X86_DEV_DMA_OPS
  PCI: vmd: Remove dma_map_ops overrides
  iommu/vt-d: Remove VMD child device sanity check
  iommu/vt-d: Use pci_real_dma_dev() for mapping
  PCI: Introduce pci_real_dma_dev()
  x86/PCI: Expose VMD's pci_dev in struct pci_sysdata
  x86/PCI: Add to_pci_sysdata() helper
  PCI/AER: Initialize aer_fifo
  ...

42 files changed:
.mailmap
Documentation/PCI/msi-howto.rst
Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/pci/qcom,pcie.txt
MAINTAINERS
arch/x86/Kconfig
arch/x86/include/asm/device.h
arch/x86/include/asm/pci.h
arch/x86/pci/common.c
drivers/iommu/amd_iommu.c
drivers/iommu/intel-iommu.c
drivers/pci/controller/Kconfig
drivers/pci/controller/Makefile
drivers/pci/controller/dwc/Kconfig
drivers/pci/controller/dwc/Makefile
drivers/pci/controller/dwc/pci-exynos.c
drivers/pci/controller/dwc/pci-keystone.c
drivers/pci/controller/dwc/pcie-artpec6.c
drivers/pci/controller/dwc/pcie-designware.c
drivers/pci/controller/dwc/pcie-designware.h
drivers/pci/controller/dwc/pcie-intel-gw.c [new file with mode: 0644]
drivers/pci/controller/dwc/pcie-qcom.c
drivers/pci/controller/dwc/pcie-uniphier.c
drivers/pci/controller/pci-tegra.c
drivers/pci/controller/pcie-brcmstb.c [new file with mode: 0644]
drivers/pci/controller/pcie-iproc.c
drivers/pci/controller/vmd.c
drivers/pci/iov.c
drivers/pci/p2pdma.c
drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/pcie/aer.c
drivers/pci/pcie/err.c
drivers/pci/quirks.c
drivers/pci/search.c
drivers/pci/setup-bus.c
drivers/pci/switch/switchtec.c
include/linux/pci.h
include/linux/switchtec.h
include/uapi/linux/pci_regs.h
include/uapi/linux/switchtec_ioctl.h

index bf58162..de36dce 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -27,6 +27,8 @@ Andi Shyti <andi@etezian.org> <andi.shyti@samsung.com>
 Andreas Herrmann <aherrman@de.ibm.com>
 Andrey Ryabinin <ryabinin.a.a@gmail.com> <a.ryabinin@samsung.com>
 Andrew Morton <akpm@linux-foundation.org>
+Andrew Murray <amurray@thegoodpenguin.co.uk> <andrew.murray@arm.com>
+Andrew Murray <amurray@thegoodpenguin.co.uk> <amurray@embedded-bits.co.uk>
 Andrew Vasquez <andrew.vasquez@qlogic.com>
 Andy Adamson <andros@citi.umich.edu>
 Antoine Tenart <antoine.tenart@free-electrons.com>
index 994cbb6..aa2046a 100644 (file)
@@ -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.
diff --git a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml
new file mode 100644 (file)
index 0000000..77d3e81
--- /dev/null
@@ -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 <nsaenzjulienne@suse.de>
+
+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 <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    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 = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
+                                 <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+                    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;
+            };
+    };
diff --git a/Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml b/Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml
new file mode 100644 (file)
index 0000000..db605d8
--- /dev/null
@@ -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 <eswara.kota@linux.intel.com>
+
+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 <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/clock/intel,lgm-clk.h>
+    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>;
+    };
index ada80b0..981b4de 100644 (file)
@@ -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
                        - "master_bus"  AXI Master clock
                        - "slave_bus"   AXI Slave clock
 
+-clock-names:
+       Usage: required for sdm845
+       Value type: <stringlist>
+       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: <prop-encoded-array>
                        - "pwr"                 PWR reset
                        - "ahb"                 AHB reset
 
+- reset-names:
+       Usage: required for sdm845
+       Value type: <stringlist>
+       Definition: Should contain the following entries
+                       - "pci"                 PCIe core reset
+
 - power-domains:
        Usage: required for apq8084 and msm8996/apq8096
        Value type: <prop-encoded-array>
index d3ee1ad..1f77fb8 100644 (file)
@@ -12914,7 +12914,7 @@ F:      arch/x86/kernel/early-quirks.c
 
 PCI NATIVE HOST BRIDGE AND ENDPOINT DRIVERS
 M:     Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-R:     Andrew Murray <andrew.murray@arm.com>
+R:     Andrew Murray <amurray@thegoodpenguin.co.uk>
 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/
index 90288ab..44d2796 100644 (file)
@@ -2931,9 +2931,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"
index 5e12c63..7e31f7f 100644 (file)
@@ -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 {
 };
 
index c1fdd43..40ac133 100644 (file)
@@ -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
 };
 
@@ -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_dev != NULL;
 }
+#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 *
index 1e59df0..df1d959 100644 (file)
@@ -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;
 }
@@ -736,3 +698,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
index bd25674..7a6c056 100644 (file)
@@ -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);
 
index 932267f..35a4a3a 100644 (file)
@@ -774,13 +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(to_pci_dev(dev));
 
                /* VFs aren't listed in scope tables; we need to look up
                 * the PF instead to find the IOMMU. */
@@ -2428,6 +2422,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))
index c77069c..20bf00f 100644 (file)
@@ -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
@@ -253,6 +252,15 @@ 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
+       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.
+
 config PCI_HYPERV_INTERFACE
        tristate "Hyper-V PCI Interface"
        depends on X86 && HYPERV && PCI_MSI && PCI_MSI_IRQ_DOMAIN && X86_64
index 3d4f597..01b2502 100644 (file)
@@ -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/
 
index 625a031..0830dfc 100644 (file)
@@ -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"
index 69faff3..8a637cf 100644 (file)
@@ -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
index 14a6ba4..c5043d9 100644 (file)
@@ -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
index af67725..c8c702c 100644 (file)
@@ -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);
@@ -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)
@@ -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;
                }
 
                /*
index 9e2482b..28d5a10 100644 (file)
@@ -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)
index 820488d..681548c 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/of.h>
 #include <linux/types.h>
 
+#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;
index 5accdd6..a22ea59 100644 (file)
 #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 (file)
index 0000000..fc2a122
--- /dev/null
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe host controller driver for Intel Gateway SoCs
+ *
+ * Copyright (c) 2019 Intel Corporation.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iopoll.h>
+#include <linux/pci_regs.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#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);
index 7e58174..5ea527a 100644 (file)
@@ -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 },
        { }
 };
 
index 8fd7bad..a5401a0 100644 (file)
@@ -9,11 +9,11 @@
 #include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/iopoll.h>
 #include <linux/irqchip/chained_irq.h>
 #include <linux/irqdomain.h>
-#include <linux/module.h>
 #include <linux/of_irq.h>
 #include <linux/pci.h>
 #include <linux/phy/phy.h>
@@ -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 <hayashi.kunihiko@socionext.com>");
-MODULE_DESCRIPTION("UniPhier PCIe host controller driver");
-MODULE_LICENSE("GPL v2");
index 673a172..ac93f5a 100644 (file)
@@ -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,
@@ -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;
        }
diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c
new file mode 100644 (file)
index 0000000..d20aabc
--- /dev/null
@@ -0,0 +1,1015 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2009 - 2019 Broadcom */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/pci.h>
+#include <linux/printk.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#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_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
+
+#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
+#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
+#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
+
+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;
+       void __iomem            *base;
+       struct clk              *clk;
+       struct pci_bus          *root_bus;
+       struct device_node      *np;
+       bool                    ssc;
+       int                     gen;
+       u64                     msi_target_addr;
+       struct brcm_msi         *msi;
+};
+
+/*
+ * 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));
+}
+
+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)
+{
+       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);
+
+       /*
+        * 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;
+       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_msi_remove(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, *msi_np;
+       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;
+
+       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;
+       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");
index 0a468c7..8c7f875 100644 (file)
@@ -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 <rjui@broadcom.com>");
 MODULE_DESCRIPTION("Broadcom iPROC PCIe common driver");
 MODULE_LICENSE("GPL v2");
index 2128422..dac91d6 100644 (file)
@@ -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)
 {
@@ -679,7 +531,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;
@@ -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);
 }
@@ -868,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,}
index 1e88fd4..4d1f392 100644 (file)
@@ -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);
index 0608aae..9a8a383 100644 (file)
@@ -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},
        {}
 };
 
index df21e32..3c30e72 100644 (file)
@@ -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);
@@ -5998,7 +6001,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,18 +6018,29 @@ 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(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;
        }
 
-       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)
@@ -6033,7 +6048,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 +6074,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;
index a0a53bd..6394e77 100644 (file)
@@ -4,6 +4,9 @@
 
 #include <linux/pci.h>
 
+/* 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 */
index 1ca86f2..4a818b0 100644 (file)
@@ -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,
index b0e6048..01dfc8b 100644 (file)
@@ -10,6 +10,8 @@
  *     Zhang Yanmin (yanmin.zhang@intel.com)
  */
 
+#define dev_fmt(fmt) "AER: " fmt
+
 #include <linux/pci.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -61,10 +63,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, "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);
@@ -233,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");
 }
index a3a1a0e..29f473e 100644 (file)
@@ -1871,19 +1871,40 @@ 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);
 
+/*
+ * 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)
 {
@@ -2381,32 +2402,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
@@ -3932,7 +3927,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 +3941,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 +4026,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 +4067,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 +4093,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);
@@ -5339,7 +5329,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);
                }
        }
 
@@ -5380,6 +5370,39 @@ 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  */
+
+/*
+ * 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
index bade140..2061672 100644 (file)
@@ -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;
@@ -41,9 +47,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)
index f279826..f2461bf 100644 (file)
@@ -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);
 
@@ -1832,56 +1838,72 @@ 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 available)
+                                resource_size_t new_size)
 {
-       struct pci_dev_resource *dev_res;
+       resource_size_t add_size, size = resource_size(res);
 
        if (res->parent)
                return;
 
-       if (resource_size(res) >= available)
+       if (!new_size)
                return;
 
-       dev_res = res_to_dev_res(add_list, res);
-       if (!dev_res)
-               return;
-
-       /* Is there room to extend the window? */
-       if (available - resource_size(res) <= dev_res->add_size)
-               return;
+       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);
+       }
 
-       dev_res->add_size = available - resource_size(res);
-       pci_dbg(bridge, "bridge window %pR extended by %pa\n", res,
-               &dev_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,
                                            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 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, 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];
 
        /*
-        * 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.
+        * 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);
+
+       /*
+        * Now that we have adjusted for alignment, update the bridge window
+        * resources to fill as much remaining resource space as possible.
         */
-       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,
-                            available_mmio_pref);
+       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));
 
        /*
         * Calculate how many hotplug bridges and normal bridges there
@@ -1902,11 +1924,9 @@ 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);
-               }
+                               add_list, io, mmio, mmio_pref);
                return;
        }
 
@@ -1919,12 +1939,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;
-
        for_each_pci_bridge(dev, bus) {
-               const struct resource *res;
+               resource_size_t used_size;
+               struct resource *res;
 
                if (dev->is_hotplug_bridge)
                        continue;
@@ -1934,24 +1951,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, io, mmio, mmio_pref;
                struct pci_bus *b;
 
                b = dev->subordinate;
@@ -1963,42 +1995,31 @@ 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 = div64_ul(available_io, hotplug_bridges);
-               io = min(ALIGN(io, align), remaining_io);
-               remaining_io -= io;
-
-               align = pci_resource_alignment(bridge, mmio_res);
-               mmio = div64_ul(available_mmio, hotplug_bridges);
-               mmio = min(ALIGN(mmio, align), remaining_mmio);
-               remaining_mmio -= mmio;
-
-               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;
+               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;
        }
 }
 
 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,
@@ -2055,12 +2076,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);
 
index 88091bb..a823b4b 100644 (file)
@@ -317,8 +317,15 @@ 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 if (stdev->gen == SWITCHTEC_GEN4) \
+               return io_string_show(buf, &si->gen4.field, \
+                                     sizeof(si->gen4.field)); \
+       else \
+               return -ENOTSUPP; \
 } \
 \
 static DEVICE_ATTR_RO(field)
@@ -326,13 +333,31 @@ 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->gen3.component_vendor,
+                             sizeof(si->gen3.component_vendor));
+}
+static DEVICE_ATTR_RO(component_vendor);
 
 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)
+               return sprintf(buf, "none\n");
 
        return sprintf(buf, "PM%04X\n", id);
 }
@@ -342,7 +367,11 @@ 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)
+               return sprintf(buf, "255\n");
 
        return sprintf(buf, "%d\n", rev);
 }
@@ -450,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));
@@ -568,8 +603,15 @@ 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_IOCTL_NUM_PARTITIONS;
+       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;
+       }
 
        if (copy_to_user(uinfo, &info, sizeof(info)))
                return -EFAULT;
@@ -584,75 +626,200 @@ 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;
+       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;
 
-       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);
-               if (ioread16(&si->cfg_running) == SWITCHTEC_CFG0_RUNNING)
-                       info.active |= SWITCHTEC_IOCTL_PART_RUNNING;
+               set_fw_info_part(info, &fi->cfg0);
+               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)
-                       info.active |= SWITCHTEC_IOCTL_PART_RUNNING;
+               set_fw_info_part(info, &fi->cfg1);
+               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)
-                       info.active |= SWITCHTEC_IOCTL_PART_RUNNING;
+               set_fw_info_part(info, &fi->img0);
+               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)
-                       info.active |= SWITCHTEC_IOCTL_PART_RUNNING;
+               set_fw_info_part(info, &fi->img1);
+               if (ioread16(&si->img_running) == SWITCHTEC_GEN3_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;
+       }
+
+       if (info->address == active_addr)
+               info->active |= SWITCHTEC_IOCTL_PART_ACTIVE;
+
+       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);
+               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;
+       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 if (stdev->gen == SWITCHTEC_GEN4) {
+               ret = flash_part_info_gen4(stdev, &info);
+               if (ret)
+                       return ret;
+       } else {
+               return -ENOTSUPP;
+       }
 
        if (copy_to_user(uinfo, &info, sizeof(info)))
                return -EFAULT;
@@ -683,11 +850,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;
        }
@@ -751,10 +914,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),
@@ -1181,10 +1347,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);
@@ -1231,8 +1393,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);
@@ -1276,7 +1443,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;
 
@@ -1324,16 +1491,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;
        }
 }
@@ -1344,12 +1511,13 @@ 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)
                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;
 
@@ -1378,7 +1546,15 @@ 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 if (stdev->gen == SWITCHTEC_GEN4)
+               part_id = &stdev->mmio_sys_info->gen4.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];
@@ -1420,6 +1596,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 +1645,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 +1653,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 +1662,58 @@ 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
+       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);
index c393dff..3840a54 100644 (file)
@@ -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,
@@ -2310,7 +2311,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,
index e295515..082f1d5 100644 (file)
 #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,
@@ -32,6 +37,11 @@ enum {
        SWITCHTEC_GAS_PFF_CSR_OFFSET    = 0x134000,
 };
 
+enum switchtec_gen {
+       SWITCHTEC_GEN3,
+       SWITCHTEC_GEN4,
+};
+
 struct mrpc_regs {
        u8 input_data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
        u8 output_data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
@@ -98,16 +108,37 @@ 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 {
-       u32 device_id;
-       u32 device_version;
-       u32 firmware_version;
+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;
        u32 table_format_version;
@@ -124,26 +155,105 @@ struct sys_info_regs {
        u8 component_revision;
 } __packed;
 
-struct flash_info_regs {
+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;
+
+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;
+       struct partition_info nvlog;
+       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;
@@ -151,6 +261,13 @@ struct flash_info_regs {
        struct partition_info vendor[8];
 };
 
+struct flash_info_regs {
+       union {
+               struct flash_info_regs_gen3 gen3;
+               struct flash_info_regs_gen4 gen4;
+       };
+};
+
 enum {
        SWITCHTEC_NTB_REG_INFO_OFFSET   = 0x0000,
        SWITCHTEC_NTB_REG_CTRL_OFFSET   = 0x4000,
@@ -196,7 +313,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 +439,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;
@@ -355,6 +475,8 @@ struct switchtec_dev {
        struct device dev;
        struct cdev cdev;
 
+       enum switchtec_gen gen;
+
        int partition;
        int partition_count;
        int pff_csr_count;
index acb7d2b..5437690 100644 (file)
 #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 */
index c912b5a..2c661a3 100644 (file)
 #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_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
 
 struct switchtec_ioctl_flash_info {
        __u64 flash_length;
@@ -98,7 +109,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