linux-pci.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v21 0/4] PCI: microchip: Add host driver for Microchip PCIe controller
@ 2021-01-25 16:29 daire.mcnamara
  2021-01-25 16:29 ` [PATCH v21 1/4] PCI: Call platform_set_drvdata earlier in devm_pci_alloc_host_bridge daire.mcnamara
                   ` (4 more replies)
  0 siblings, 5 replies; 15+ messages in thread
From: daire.mcnamara @ 2021-01-25 16:29 UTC (permalink / raw)
  To: lorenzo.pieralisi, bhelgaas, robh, linux-pci, robh+dt, devicetree
  Cc: david.abdurachmanov, cyril.jean, Daire McNamara

From: Daire McNamara <daire.mcnamara@microchip.com>

This patchset adds support for the Microchip PCIe PolarFire PCIe
controller when configured in host (Root Complex) mode.

Updates since v20:
* Changed module license from GPLv2 to GPL
* Removed bus-shift for mc_ecam_ops

Updates since v19:
* Fixes for typos

Updates since v18:
* Upgraded interrupt handling as per Lorenzo Pieralisi's suggestions.
  Gathered disparate registers onto an event register, created a new event domain,
  and split out end-of-interrupt functionality.

Updates since v17:
* Regenerated to apply to v5.10rc1
* Added self as maintainer
* Added clock enables and extra interrupt handling

Updates since v16:
* Patch needs CONFIG_PCI_HOST_COMMON.  Add this to Kconfig stanza

Updates since v15:
* Call platform_set_drvdata earlier in devm_pci_alloc_host_bridge()
* Use host_common_probe() and an init function to set up hw windows
* status is u32 in mc_pcie_isr()
* Removed mask var in mc_mask_intx_irq(), mc_unmask_intx_irq()
* irq var is now signed in mc_platform_init()

Updates since v14:
* Removed cfg_read/cfg_write inline functions
* Updated to irq_data_get_irq_chip_data()
* Updated to use devm_platform_ioremap_resource()
* Replaced of_pci_range parsing to setup windows via bridge pointer.

Updates since v13:
* Refactored to use pci_host_common_probe()

Updates since v12:
* Capitalised commit messages.  Use specific subject line for dt-bindings

Updates since v11:
* Adjusted so yaml file passses make dt_binding_check

Updates since v10:
* Adjusted driver as per Rob Herring's comments, notably:
  - use common PCI_MSI_FLAGS defines
  - reduce storage of unnecessary vars in mc_pcie struct
  - switched to read/write relaxed variants
  - extended lock in msi_domain_alloc routine
  - improved 32bit safety, switched from find_first_bit() to ilog2()
  - removed unnecessary twiddle of eCAM config space

Updates since v9:
* Adjusted commit logs
* make dt_bindings_check passes

Updates since v8:
* Refactored as per Rob Herring's comments:
  - bindings in schema format
  - Adjusted licence to GPLv2.0
  - Refactored access to config space between driver and common eCAM code
  - Adopted pci_host_probe()
  - Miscellanous other improvements

Updates since v7:
* Build for 64bit RISCV architecture only

Updates since v6:
* Refactored to use common eCAM driver
* Updated to CONFIG_PCIE_MICROCHIP_HOST etc
* Formatting improvements
* Removed code for selection between bridge 0 and 1

Updates since v5:
* Fixed Kconfig typo noted by Randy Dunlap
* Updated with comments from Bjorn Helgaas

Updates since v4:
* Fix compile issues.

Updates since v3:
* Update all references to Microsemi to Microchip
* Separate MSI functionality from legacy PCIe interrupt handling functionality

Updates since v2:
* Split out DT bindings and Vendor ID updates into their own patch
  from PCIe driver.
* Updated Change Log

Updates since v1:
* Incorporate feedback from Bjorn Helgaas

Daire McNamara (4):
  PCI: Call platform_set_drvdata earlier in devm_pci_alloc_host_bridge
  dt-bindings: PCI: microchip: Add Microchip PolarFire host binding
  PCI: microchip: Add host driver for Microchip PCIe controller
  MAINTAINERS: Add Daire McNamara as maintainer for the Microchip PCIe
    driver

 .../bindings/pci/microchip,pcie-host.yaml     |   92 ++
 MAINTAINERS                                   |    7 +
 drivers/pci/controller/Kconfig                |   10 +
 drivers/pci/controller/Makefile               |    1 +
 drivers/pci/controller/pci-host-common.c      |    4 +-
 drivers/pci/controller/pcie-microchip-host.c  | 1119 +++++++++++++++++
 6 files changed, 1231 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml
 create mode 100644 drivers/pci/controller/pcie-microchip-host.c


base-commit: 3650b228f83adda7e5ee532e2b90429c03f7b9ec
-- 
2.25.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v21 1/4] PCI: Call platform_set_drvdata earlier in devm_pci_alloc_host_bridge
  2021-01-25 16:29 [PATCH v21 0/4] PCI: microchip: Add host driver for Microchip PCIe controller daire.mcnamara
@ 2021-01-25 16:29 ` daire.mcnamara
  2021-01-25 16:29 ` [PATCH v21 2/4] dt-bindings: PCI: microchip: Add Microchip PolarFire host binding daire.mcnamara
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 15+ messages in thread
From: daire.mcnamara @ 2021-01-25 16:29 UTC (permalink / raw)
  To: lorenzo.pieralisi, bhelgaas, robh, linux-pci, robh+dt, devicetree
  Cc: david.abdurachmanov, cyril.jean, Daire McNamara

From: Daire McNamara <daire.mcnamara@microchip.com>

Many drivers can now use pci_host_common_probe() directly.
Their hardware window setup can be moved from their 'custom' probe
functions to individual driver init functions.

Signed-off-by: Daire McNamara <daire.mcnamara@microchip.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 drivers/pci/controller/pci-host-common.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c
index 6ce34a1deecb..6ab694f8d283 100644
--- a/drivers/pci/controller/pci-host-common.c
+++ b/drivers/pci/controller/pci-host-common.c
@@ -64,6 +64,8 @@ int pci_host_common_probe(struct platform_device *pdev)
 	if (!bridge)
 		return -ENOMEM;
 
+	platform_set_drvdata(pdev, bridge);
+
 	of_pci_check_probe_only();
 
 	/* Parse and map our Configuration Space windows */
@@ -78,8 +80,6 @@ int pci_host_common_probe(struct platform_device *pdev)
 	bridge->sysdata = cfg;
 	bridge->ops = (struct pci_ops *)&ops->pci_ops;
 
-	platform_set_drvdata(pdev, bridge);
-
 	return pci_host_probe(bridge);
 }
 EXPORT_SYMBOL_GPL(pci_host_common_probe);
-- 
2.25.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v21 2/4] dt-bindings: PCI: microchip: Add Microchip PolarFire host binding
  2021-01-25 16:29 [PATCH v21 0/4] PCI: microchip: Add host driver for Microchip PCIe controller daire.mcnamara
  2021-01-25 16:29 ` [PATCH v21 1/4] PCI: Call platform_set_drvdata earlier in devm_pci_alloc_host_bridge daire.mcnamara
@ 2021-01-25 16:29 ` daire.mcnamara
  2021-01-25 16:29 ` [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller daire.mcnamara
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 15+ messages in thread
From: daire.mcnamara @ 2021-01-25 16:29 UTC (permalink / raw)
  To: lorenzo.pieralisi, bhelgaas, robh, linux-pci, robh+dt, devicetree
  Cc: david.abdurachmanov, cyril.jean, Daire McNamara

From: Daire McNamara <daire.mcnamara@microchip.com>

Add device tree bindings for the Microchip PolarFire PCIe controller
when configured in host (Root Complex) mode.

Signed-off-by: Daire McNamara <daire.mcnamara@microchip.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/pci/microchip,pcie-host.yaml     | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml

diff --git a/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml b/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml
new file mode 100644
index 000000000000..04251d71f56b
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml
@@ -0,0 +1,92 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/microchip,pcie-host.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip PCIe Root Port Bridge Controller Device Tree Bindings
+
+maintainers:
+  - Daire McNamara <daire.mcnamara@microchip.com>
+
+allOf:
+  - $ref: /schemas/pci/pci-bus.yaml#
+
+properties:
+  compatible:
+    const: microchip,pcie-host-1.0 # PolarFire
+
+  reg:
+    maxItems: 2
+
+  reg-names:
+    items:
+      - const: cfg
+      - const: apb
+
+  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
+
+  msi-controller:
+    description: Identifies the node as an MSI controller.
+
+  msi-parent:
+    description: MSI controller the device is capable of using.
+
+required:
+  - reg
+  - reg-names
+  - "#interrupt-cells"
+  - interrupts
+  - interrupt-map-mask
+  - interrupt-map
+  - msi-controller
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    soc {
+            #address-cells = <2>;
+            #size-cells = <2>;
+            pcie0: pcie@2030000000 {
+                    compatible = "microchip,pcie-host-1.0";
+                    reg = <0x0 0x70000000 0x0 0x08000000>,
+                          <0x0 0x43000000 0x0 0x00010000>;
+                    reg-names = "cfg", "apb";
+                    device_type = "pci";
+                    #address-cells = <3>;
+                    #size-cells = <2>;
+                    #interrupt-cells = <1>;
+                    interrupts = <119>;
+                    interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+                    interrupt-map = <0 0 0 1 &pcie_intc0 0>,
+                                    <0 0 0 2 &pcie_intc0 1>,
+                                    <0 0 0 3 &pcie_intc0 2>,
+                                    <0 0 0 4 &pcie_intc0 3>;
+                    interrupt-parent = <&plic0>;
+                    msi-parent = <&pcie0>;
+                    msi-controller;
+                    bus-range = <0x00 0x7f>;
+                    ranges = <0x03000000 0x0 0x78000000 0x0 0x78000000 0x0 0x04000000>;
+                    pcie_intc0: interrupt-controller {
+                        #address-cells = <0>;
+                        #interrupt-cells = <1>;
+                        interrupt-controller;
+                    };
+            };
+    };
-- 
2.25.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller
  2021-01-25 16:29 [PATCH v21 0/4] PCI: microchip: Add host driver for Microchip PCIe controller daire.mcnamara
  2021-01-25 16:29 ` [PATCH v21 1/4] PCI: Call platform_set_drvdata earlier in devm_pci_alloc_host_bridge daire.mcnamara
  2021-01-25 16:29 ` [PATCH v21 2/4] dt-bindings: PCI: microchip: Add Microchip PolarFire host binding daire.mcnamara
@ 2021-01-25 16:29 ` daire.mcnamara
  2021-02-01 19:05   ` Lorenzo Pieralisi
                     ` (2 more replies)
  2021-01-25 16:29 ` [PATCH v21 4/4] MAINTAINERS: Add Daire McNamara as maintainer for the Microchip PCIe driver daire.mcnamara
  2021-02-04 11:42 ` [PATCH v21 0/4] PCI: microchip: Add host driver for Microchip PCIe controller Lorenzo Pieralisi
  4 siblings, 3 replies; 15+ messages in thread
From: daire.mcnamara @ 2021-01-25 16:29 UTC (permalink / raw)
  To: lorenzo.pieralisi, bhelgaas, robh, linux-pci, robh+dt, devicetree
  Cc: david.abdurachmanov, cyril.jean, Daire McNamara

From: Daire McNamara <daire.mcnamara@microchip.com>

Add support for the Microchip PolarFire PCIe controller when
configured in host (Root Complex) mode.

Signed-off-by: Daire McNamara <daire.mcnamara@microchip.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 drivers/pci/controller/Kconfig               |   10 +
 drivers/pci/controller/Makefile              |    1 +
 drivers/pci/controller/pcie-microchip-host.c | 1114 ++++++++++++++++++
 3 files changed, 1125 insertions(+)
 create mode 100644 drivers/pci/controller/pcie-microchip-host.c

diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index 64e2f5e379aa..bca2f8949510 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -298,6 +298,16 @@ config PCI_LOONGSON
 	  Say Y here if you want to enable PCI controller support on
 	  Loongson systems.
 
+config PCIE_MICROCHIP_HOST
+	bool "Microchip AXI PCIe host bridge support"
+	depends on PCI_MSI && OF
+	select PCI_MSI_IRQ_DOMAIN
+	select GENERIC_MSI_IRQ_DOMAIN
+	select PCI_HOST_COMMON
+	help
+	  Say Y here if you want kernel to support the Microchip AXI PCIe
+	  Host Bridge driver.
+
 config PCIE_HISI_ERR
 	depends on ACPI_APEI_GHES && (ARM64 || COMPILE_TEST)
 	bool "HiSilicon HIP PCIe controller error handling driver"
diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
index 04c6edc285c5..b85fcd574ff6 100644
--- a/drivers/pci/controller/Makefile
+++ b/drivers/pci/controller/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-rockchip-ep.o
 obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o
 obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o
 obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
+obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o
 obj-$(CONFIG_VMD) += vmd.o
 obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
 obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o
diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c
new file mode 100644
index 000000000000..0ef2beab185b
--- /dev/null
+++ b/drivers/pci/controller/pcie-microchip-host.c
@@ -0,0 +1,1114 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip AXI PCIe Bridge host controller driver
+ *
+ * Copyright (c) 2018 - 2020 Microchip Corporation. All rights reserved.
+ *
+ * Author: Daire McNamara <daire.mcnamara@microchip.com>
+ *
+ * Based on:
+ *	pcie-rcar.c
+ *	pcie-xilinx.c
+ *	pcie-altera.c
+ */
+
+#include <linux/clk.h>
+#include <linux/irqchip/chained_irq.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/pci-ecam.h>
+#include <linux/platform_device.h>
+
+#include "../pci.h"
+
+/* Number of MSI IRQs */
+#define MC_NUM_MSI_IRQS				32
+#define MC_NUM_MSI_IRQS_CODED			5
+
+/* PCIe Bridge Phy and Controller Phy offsets */
+#define MC_PCIE1_BRIDGE_ADDR			0x00008000u
+#define MC_PCIE1_CTRL_ADDR			0x0000a000u
+
+#define MC_PCIE_BRIDGE_ADDR			(MC_PCIE1_BRIDGE_ADDR)
+#define MC_PCIE_CTRL_ADDR			(MC_PCIE1_CTRL_ADDR)
+
+/* PCIe Controller Phy Regs */
+#define SEC_ERROR_CNT				0x20
+#define DED_ERROR_CNT				0x24
+#define SEC_ERROR_INT				0x28
+#define  SEC_ERROR_INT_TX_RAM_SEC_ERR_INT	GENMASK(3, 0)
+#define  SEC_ERROR_INT_RX_RAM_SEC_ERR_INT	GENMASK(7, 4)
+#define  SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT	GENMASK(11, 8)
+#define  SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT	GENMASK(15, 12)
+#define  NUM_SEC_ERROR_INTS			(4)
+#define SEC_ERROR_INT_MASK			0x2c
+#define DED_ERROR_INT				0x30
+#define  DED_ERROR_INT_TX_RAM_DED_ERR_INT	GENMASK(3, 0)
+#define  DED_ERROR_INT_RX_RAM_DED_ERR_INT	GENMASK(7, 4)
+#define  DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT	GENMASK(11, 8)
+#define  DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT	GENMASK(15, 12)
+#define  NUM_DED_ERROR_INTS			(4)
+#define DED_ERROR_INT_MASK			0x34
+#define ECC_CONTROL				0x38
+#define  ECC_CONTROL_TX_RAM_INJ_ERROR_0		BIT(0)
+#define  ECC_CONTROL_TX_RAM_INJ_ERROR_1		BIT(1)
+#define  ECC_CONTROL_TX_RAM_INJ_ERROR_2		BIT(2)
+#define  ECC_CONTROL_TX_RAM_INJ_ERROR_3		BIT(3)
+#define  ECC_CONTROL_RX_RAM_INJ_ERROR_0		BIT(4)
+#define  ECC_CONTROL_RX_RAM_INJ_ERROR_1		BIT(5)
+#define  ECC_CONTROL_RX_RAM_INJ_ERROR_2		BIT(6)
+#define  ECC_CONTROL_RX_RAM_INJ_ERROR_3		BIT(7)
+#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_0	BIT(8)
+#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_1	BIT(9)
+#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_2	BIT(10)
+#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_3	BIT(11)
+#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_0	BIT(12)
+#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_1	BIT(13)
+#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_2	BIT(14)
+#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_3	BIT(15)
+#define  ECC_CONTROL_TX_RAM_ECC_BYPASS		BIT(24)
+#define  ECC_CONTROL_RX_RAM_ECC_BYPASS		BIT(25)
+#define  ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS	BIT(26)
+#define  ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS	BIT(27)
+#define LTSSM_STATE				0x5c
+#define  LTSSM_L0_STATE				0x10
+#define PCIE_EVENT_INT				0x14c
+#define  PCIE_EVENT_INT_L2_EXIT_INT		BIT(0)
+#define  PCIE_EVENT_INT_HOTRST_EXIT_INT		BIT(1)
+#define  PCIE_EVENT_INT_DLUP_EXIT_INT		BIT(2)
+#define  PCIE_EVENT_INT_MASK			GENMASK(2, 0)
+#define  PCIE_EVENT_INT_L2_EXIT_INT_MASK	BIT(16)
+#define  PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK	BIT(17)
+#define  PCIE_EVENT_INT_DLUP_EXIT_INT_MASK	BIT(18)
+#define  PCIE_EVENT_INT_ENB_MASK		GENMASK(18, 16)
+#define  PCIE_EVENT_INT_ENB_SHIFT		16
+#define  NUM_PCIE_EVENTS			(3)
+
+/* PCIe Bridge Phy Regs */
+#define PCIE_PCI_IDS_DW1			0x9c
+
+/* PCIe Config space MSI capability structure */
+#define MC_MSI_CAP_CTRL_OFFSET			0xe0u
+#define  MC_MSI_MAX_Q_AVAIL			(MC_NUM_MSI_IRQS_CODED << 1)
+#define  MC_MSI_Q_SIZE				(MC_NUM_MSI_IRQS_CODED << 4)
+
+#define IMASK_LOCAL				0x180
+#define  DMA_END_ENGINE_0_MASK			0x00000000u
+#define  DMA_END_ENGINE_0_SHIFT			0
+#define  DMA_END_ENGINE_1_MASK			0x00000000u
+#define  DMA_END_ENGINE_1_SHIFT			1
+#define  DMA_ERROR_ENGINE_0_MASK		0x00000100u
+#define  DMA_ERROR_ENGINE_0_SHIFT		8
+#define  DMA_ERROR_ENGINE_1_MASK		0x00000200u
+#define  DMA_ERROR_ENGINE_1_SHIFT		9
+#define  A_ATR_EVT_POST_ERR_MASK		0x00010000u
+#define  A_ATR_EVT_POST_ERR_SHIFT		16
+#define  A_ATR_EVT_FETCH_ERR_MASK		0x00020000u
+#define  A_ATR_EVT_FETCH_ERR_SHIFT		17
+#define  A_ATR_EVT_DISCARD_ERR_MASK		0x00040000u
+#define  A_ATR_EVT_DISCARD_ERR_SHIFT		18
+#define  A_ATR_EVT_DOORBELL_MASK		0x00000000u
+#define  A_ATR_EVT_DOORBELL_SHIFT		19
+#define  P_ATR_EVT_POST_ERR_MASK		0x00100000u
+#define  P_ATR_EVT_POST_ERR_SHIFT		20
+#define  P_ATR_EVT_FETCH_ERR_MASK		0x00200000u
+#define  P_ATR_EVT_FETCH_ERR_SHIFT		21
+#define  P_ATR_EVT_DISCARD_ERR_MASK		0x00400000u
+#define  P_ATR_EVT_DISCARD_ERR_SHIFT		22
+#define  P_ATR_EVT_DOORBELL_MASK		0x00000000u
+#define  P_ATR_EVT_DOORBELL_SHIFT		23
+#define  PM_MSI_INT_INTA_MASK			0x01000000u
+#define  PM_MSI_INT_INTA_SHIFT			24
+#define  PM_MSI_INT_INTB_MASK			0x02000000u
+#define  PM_MSI_INT_INTB_SHIFT			25
+#define  PM_MSI_INT_INTC_MASK			0x04000000u
+#define  PM_MSI_INT_INTC_SHIFT			26
+#define  PM_MSI_INT_INTD_MASK			0x08000000u
+#define  PM_MSI_INT_INTD_SHIFT			27
+#define  PM_MSI_INT_INTX_MASK			0x0f000000u
+#define  PM_MSI_INT_INTX_SHIFT			24
+#define  PM_MSI_INT_MSI_MASK			0x10000000u
+#define  PM_MSI_INT_MSI_SHIFT			28
+#define  PM_MSI_INT_AER_EVT_MASK		0x20000000u
+#define  PM_MSI_INT_AER_EVT_SHIFT		29
+#define  PM_MSI_INT_EVENTS_MASK			0x40000000u
+#define  PM_MSI_INT_EVENTS_SHIFT		30
+#define  PM_MSI_INT_SYS_ERR_MASK		0x80000000u
+#define  PM_MSI_INT_SYS_ERR_SHIFT		31
+#define  NUM_LOCAL_EVENTS			15
+#define ISTATUS_LOCAL				0x184
+#define IMASK_HOST				0x188
+#define ISTATUS_HOST				0x18c
+#define MSI_ADDR				0x190
+#define ISTATUS_MSI				0x194
+
+/* PCIe Master table init defines */
+#define ATR0_PCIE_WIN0_SRCADDR_PARAM		0x600u
+#define  ATR0_PCIE_ATR_SIZE			0x25
+#define  ATR0_PCIE_ATR_SIZE_SHIFT		1
+#define ATR0_PCIE_WIN0_SRC_ADDR			0x604u
+#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB		0x608u
+#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW		0x60cu
+#define ATR0_PCIE_WIN0_TRSL_PARAM		0x610u
+
+/* PCIe AXI slave table init defines */
+#define ATR0_AXI4_SLV0_SRCADDR_PARAM		0x800u
+#define  ATR_SIZE_SHIFT				1
+#define  ATR_IMPL_ENABLE			1
+#define ATR0_AXI4_SLV0_SRC_ADDR			0x804u
+#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB		0x808u
+#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW		0x80cu
+#define ATR0_AXI4_SLV0_TRSL_PARAM		0x810u
+#define  PCIE_TX_RX_INTERFACE			0x00000000u
+#define  PCIE_CONFIG_INTERFACE			0x00000001u
+
+#define ATR_ENTRY_SIZE				32
+
+#define EVENT_PCIE_L2_EXIT			0
+#define EVENT_PCIE_HOTRST_EXIT			1
+#define EVENT_PCIE_DLUP_EXIT			2
+#define EVENT_SEC_TX_RAM_SEC_ERR		3
+#define EVENT_SEC_RX_RAM_SEC_ERR		4
+#define EVENT_SEC_AXI2PCIE_RAM_SEC_ERR		5
+#define EVENT_SEC_PCIE2AXI_RAM_SEC_ERR		6
+#define EVENT_DED_TX_RAM_DED_ERR		7
+#define EVENT_DED_RX_RAM_DED_ERR		8
+#define EVENT_DED_AXI2PCIE_RAM_DED_ERR		9
+#define EVENT_DED_PCIE2AXI_RAM_DED_ERR		10
+#define EVENT_LOCAL_DMA_END_ENGINE_0		11
+#define EVENT_LOCAL_DMA_END_ENGINE_1		12
+#define EVENT_LOCAL_DMA_ERROR_ENGINE_0		13
+#define EVENT_LOCAL_DMA_ERROR_ENGINE_1		14
+#define EVENT_LOCAL_A_ATR_EVT_POST_ERR		15
+#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR		16
+#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR	17
+#define EVENT_LOCAL_A_ATR_EVT_DOORBELL		18
+#define EVENT_LOCAL_P_ATR_EVT_POST_ERR		19
+#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR		20
+#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR	21
+#define EVENT_LOCAL_P_ATR_EVT_DOORBELL		22
+#define EVENT_LOCAL_PM_MSI_INT_INTX		23
+#define EVENT_LOCAL_PM_MSI_INT_MSI		24
+#define EVENT_LOCAL_PM_MSI_INT_AER_EVT		25
+#define EVENT_LOCAL_PM_MSI_INT_EVENTS		26
+#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR		27
+#define NUM_EVENTS				28
+
+#define PCIE_EVENT_CAUSE(x, s)	\
+	[EVENT_PCIE_ ## x] = { __stringify(x), s }
+
+#define SEC_ERROR_CAUSE(x, s) \
+	[EVENT_SEC_ ## x] = { __stringify(x), s }
+
+#define DED_ERROR_CAUSE(x, s) \
+	[EVENT_DED_ ## x] = { __stringify(x), s }
+
+#define LOCAL_EVENT_CAUSE(x, s) \
+	[EVENT_LOCAL_ ## x] = { __stringify(x), s }
+
+#define PCIE_EVENT(x) \
+	.base = MC_PCIE_CTRL_ADDR, \
+	.offset = PCIE_EVENT_INT, \
+	.mask_offset = PCIE_EVENT_INT, \
+	.mask_high = 1, \
+	.mask = PCIE_EVENT_INT_ ## x ## _INT, \
+	.enb_mask = PCIE_EVENT_INT_ENB_MASK
+
+#define SEC_EVENT(x) \
+	.base = MC_PCIE_CTRL_ADDR, \
+	.offset = SEC_ERROR_INT, \
+	.mask_offset = SEC_ERROR_INT_MASK, \
+	.mask = SEC_ERROR_INT_ ## x ## _INT, \
+	.mask_high = 1, \
+	.enb_mask = 0
+
+#define DED_EVENT(x) \
+	.base = MC_PCIE_CTRL_ADDR, \
+	.offset = DED_ERROR_INT, \
+	.mask_offset = DED_ERROR_INT_MASK, \
+	.mask_high = 1, \
+	.mask = DED_ERROR_INT_ ## x ## _INT, \
+	.enb_mask = 0
+
+#define LOCAL_EVENT(x) \
+	.base = MC_PCIE_BRIDGE_ADDR, \
+	.offset = ISTATUS_LOCAL, \
+	.mask_offset = IMASK_LOCAL, \
+	.mask_high = 0, \
+	.mask = x ## _MASK, \
+	.enb_mask = 0
+
+#define PCIE_EVENT_TO_EVENT_MAP(x) \
+	{ PCIE_EVENT_INT_ ## x ## _INT, EVENT_PCIE_ ## x }
+
+#define SEC_ERROR_TO_EVENT_MAP(x) \
+	{ SEC_ERROR_INT_ ## x ## _INT, EVENT_SEC_ ## x }
+
+#define DED_ERROR_TO_EVENT_MAP(x) \
+	{ DED_ERROR_INT_ ## x ## _INT, EVENT_DED_ ## x }
+
+#define LOCAL_STATUS_TO_EVENT_MAP(x) \
+	{ x ## _MASK, EVENT_LOCAL_ ## x }
+
+struct event_map {
+	u32 reg_mask;
+	u32 event_bit;
+};
+
+struct mc_msi {
+	struct mutex lock;		/* Protect used bitmap */
+	struct irq_domain *msi_domain;
+	struct irq_domain *dev_domain;
+	u32 num_vectors;
+	u64 vector_phy;
+	DECLARE_BITMAP(used, MC_NUM_MSI_IRQS);
+};
+
+struct mc_port {
+	void __iomem *axi_base_addr;
+	struct device *dev;
+	struct irq_domain *intx_domain;
+	struct irq_domain *event_domain;
+	raw_spinlock_t lock;
+	struct mc_msi msi;
+};
+
+struct cause {
+	const char *sym;
+	const char *str;
+};
+
+static const struct cause event_cause[NUM_EVENTS] = {
+	PCIE_EVENT_CAUSE(L2_EXIT, "L2 exit event"),
+	PCIE_EVENT_CAUSE(HOTRST_EXIT, "Hot reset exit event"),
+	PCIE_EVENT_CAUSE(DLUP_EXIT, "DLUP exit event"),
+	SEC_ERROR_CAUSE(TX_RAM_SEC_ERR,  "sec error in tx buffer"),
+	SEC_ERROR_CAUSE(RX_RAM_SEC_ERR,  "sec error in rx buffer"),
+	SEC_ERROR_CAUSE(PCIE2AXI_RAM_SEC_ERR,  "sec error in pcie2axi buffer"),
+	SEC_ERROR_CAUSE(AXI2PCIE_RAM_SEC_ERR,  "sec error in axi2pcie buffer"),
+	DED_ERROR_CAUSE(TX_RAM_DED_ERR,  "ded error in tx buffer"),
+	DED_ERROR_CAUSE(RX_RAM_DED_ERR,  "ded error in rx buffer"),
+	DED_ERROR_CAUSE(PCIE2AXI_RAM_DED_ERR,  "ded error in pcie2axi buffer"),
+	DED_ERROR_CAUSE(AXI2PCIE_RAM_DED_ERR,  "ded error in axi2pcie buffer"),
+	LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_0, "dma engine 0 error"),
+	LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_1, "dma engine 1 error"),
+	LOCAL_EVENT_CAUSE(A_ATR_EVT_POST_ERR, "axi write request error"),
+	LOCAL_EVENT_CAUSE(A_ATR_EVT_FETCH_ERR, "axi read request error"),
+	LOCAL_EVENT_CAUSE(A_ATR_EVT_DISCARD_ERR, "axi read timeout"),
+	LOCAL_EVENT_CAUSE(P_ATR_EVT_POST_ERR, "pcie write request error"),
+	LOCAL_EVENT_CAUSE(P_ATR_EVT_FETCH_ERR, "pcie read request error"),
+	LOCAL_EVENT_CAUSE(P_ATR_EVT_DISCARD_ERR, "pcie read timeout"),
+	LOCAL_EVENT_CAUSE(PM_MSI_INT_AER_EVT, "aer event"),
+	LOCAL_EVENT_CAUSE(PM_MSI_INT_EVENTS, "pm/ltr/hotplug event"),
+	LOCAL_EVENT_CAUSE(PM_MSI_INT_SYS_ERR, "system error"),
+};
+
+struct event_map pcie_event_to_event[] = {
+	PCIE_EVENT_TO_EVENT_MAP(L2_EXIT),
+	PCIE_EVENT_TO_EVENT_MAP(HOTRST_EXIT),
+	PCIE_EVENT_TO_EVENT_MAP(DLUP_EXIT)
+};
+
+struct event_map sec_error_to_event[] = {
+	SEC_ERROR_TO_EVENT_MAP(TX_RAM_SEC_ERR),
+	SEC_ERROR_TO_EVENT_MAP(RX_RAM_SEC_ERR),
+	SEC_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_SEC_ERR),
+	SEC_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_SEC_ERR)
+};
+
+struct event_map ded_error_to_event[] = {
+	DED_ERROR_TO_EVENT_MAP(TX_RAM_DED_ERR),
+	DED_ERROR_TO_EVENT_MAP(RX_RAM_DED_ERR),
+	DED_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_DED_ERR),
+	DED_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_DED_ERR)
+};
+
+struct event_map local_status_to_event[] = {
+	LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_0),
+	LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_1),
+	LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_0),
+	LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_1),
+	LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_POST_ERR),
+	LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_FETCH_ERR),
+	LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DISCARD_ERR),
+	LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DOORBELL),
+	LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_POST_ERR),
+	LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_FETCH_ERR),
+	LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DISCARD_ERR),
+	LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DOORBELL),
+	LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_INTX),
+	LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_MSI),
+	LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_AER_EVT),
+	LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_EVENTS),
+	LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_SYS_ERR),
+};
+
+struct {
+	u32 base;
+	u32 offset;
+	u32 mask;
+	u32 shift;
+	u32 enb_mask;
+	u32 mask_high;
+	u32 mask_offset;
+} event_descs[] = {
+	{ PCIE_EVENT(L2_EXIT) },
+	{ PCIE_EVENT(HOTRST_EXIT) },
+	{ PCIE_EVENT(DLUP_EXIT) },
+	{ SEC_EVENT(TX_RAM_SEC_ERR) },
+	{ SEC_EVENT(RX_RAM_SEC_ERR) },
+	{ SEC_EVENT(PCIE2AXI_RAM_SEC_ERR) },
+	{ SEC_EVENT(AXI2PCIE_RAM_SEC_ERR) },
+	{ DED_EVENT(TX_RAM_DED_ERR) },
+	{ DED_EVENT(RX_RAM_DED_ERR) },
+	{ DED_EVENT(PCIE2AXI_RAM_DED_ERR) },
+	{ DED_EVENT(AXI2PCIE_RAM_DED_ERR) },
+	{ LOCAL_EVENT(DMA_END_ENGINE_0) },
+	{ LOCAL_EVENT(DMA_END_ENGINE_1) },
+	{ LOCAL_EVENT(DMA_ERROR_ENGINE_0) },
+	{ LOCAL_EVENT(DMA_ERROR_ENGINE_1) },
+	{ LOCAL_EVENT(A_ATR_EVT_POST_ERR) },
+	{ LOCAL_EVENT(A_ATR_EVT_FETCH_ERR) },
+	{ LOCAL_EVENT(A_ATR_EVT_DISCARD_ERR) },
+	{ LOCAL_EVENT(A_ATR_EVT_DOORBELL) },
+	{ LOCAL_EVENT(P_ATR_EVT_POST_ERR) },
+	{ LOCAL_EVENT(P_ATR_EVT_FETCH_ERR) },
+	{ LOCAL_EVENT(P_ATR_EVT_DISCARD_ERR) },
+	{ LOCAL_EVENT(P_ATR_EVT_DOORBELL) },
+	{ LOCAL_EVENT(PM_MSI_INT_INTX) },
+	{ LOCAL_EVENT(PM_MSI_INT_MSI) },
+	{ LOCAL_EVENT(PM_MSI_INT_AER_EVT) },
+	{ LOCAL_EVENT(PM_MSI_INT_EVENTS) },
+	{ LOCAL_EVENT(PM_MSI_INT_SYS_ERR) }
+};
+
+static char poss_clks[][5] = { "fic0", "fic1", "fic2", "fic3" };
+
+static void mc_pcie_enable_msi(struct mc_port *port, void __iomem *base)
+{
+	struct mc_msi *msi = &port->msi;
+	u32 cap_offset = MC_MSI_CAP_CTRL_OFFSET;
+	u16 msg_ctrl = readw_relaxed(base + cap_offset + PCI_MSI_FLAGS);
+
+	msg_ctrl |= PCI_MSI_FLAGS_ENABLE;
+	msg_ctrl &= ~PCI_MSI_FLAGS_QMASK;
+	msg_ctrl |= MC_MSI_MAX_Q_AVAIL;
+	msg_ctrl &= ~PCI_MSI_FLAGS_QSIZE;
+	msg_ctrl |= MC_MSI_Q_SIZE;
+	msg_ctrl |= PCI_MSI_FLAGS_64BIT;
+
+	writew_relaxed(msg_ctrl, base + cap_offset + PCI_MSI_FLAGS);
+
+	writel_relaxed(lower_32_bits(msi->vector_phy), base + cap_offset + PCI_MSI_ADDRESS_LO);
+	writel_relaxed(upper_32_bits(msi->vector_phy), base + cap_offset + PCI_MSI_ADDRESS_HI);
+}
+
+static void mc_handle_msi(struct irq_desc *desc)
+{
+	struct mc_port *port = irq_desc_get_handler_data(desc);
+	struct device *dev = port->dev;
+	struct mc_msi *msi = &port->msi;
+	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+	unsigned long status;
+	u32 bit;
+	u32 virq;
+
+	status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
+	if (status & PM_MSI_INT_MSI_MASK) {
+		status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
+		for_each_set_bit(bit, &status, msi->num_vectors) {
+			virq = irq_find_mapping(msi->dev_domain, bit);
+			if (virq)
+				generic_handle_irq(virq);
+			else
+				dev_err_ratelimited(dev, "bad MSI IRQ %d\n", bit);
+		}
+	}
+}
+
+static void mc_msi_bottom_irq_ack(struct irq_data *data)
+{
+	struct mc_port *port = irq_data_get_irq_chip_data(data);
+	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+	u32 bitpos = data->hwirq;
+	unsigned long status;
+
+	writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
+	status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
+	if (!status)
+		writel_relaxed(BIT(PM_MSI_INT_MSI_SHIFT), bridge_base_addr + ISTATUS_LOCAL);
+}
+
+static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct mc_port *port = irq_data_get_irq_chip_data(data);
+	phys_addr_t addr = port->msi.vector_phy;
+
+	msg->address_lo = lower_32_bits(addr);
+	msg->address_hi = upper_32_bits(addr);
+	msg->data = data->hwirq;
+
+	dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n", (int)data->hwirq,
+		msg->address_hi, msg->address_lo);
+}
+
+static int mc_msi_set_affinity(struct irq_data *irq_data, const struct cpumask *mask, bool force)
+{
+	return -EINVAL;
+}
+
+static struct irq_chip mc_msi_bottom_irq_chip = {
+	.name = "Microchip MSI",
+	.irq_ack = mc_msi_bottom_irq_ack,
+	.irq_compose_msi_msg = mc_compose_msi_msg,
+	.irq_set_affinity = mc_msi_set_affinity,
+};
+
+static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				   unsigned int nr_irqs, void *args)
+{
+	struct mc_port *port = domain->host_data;
+	struct mc_msi *msi = &port->msi;
+	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+	unsigned long bit;
+	u32 val;
+
+	mutex_lock(&msi->lock);
+	bit = find_first_zero_bit(msi->used, msi->num_vectors);
+	if (bit >= msi->num_vectors) {
+		mutex_unlock(&msi->lock);
+		return -ENOSPC;
+	}
+
+	set_bit(bit, msi->used);
+
+	irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip, domain->host_data,
+			    handle_edge_irq, NULL, NULL);
+
+	/* Enable MSI interrupts */
+	val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
+	val |= PM_MSI_INT_MSI_MASK;
+	writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
+
+	mutex_unlock(&msi->lock);
+
+	return 0;
+}
+
+static void mc_irq_msi_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 mc_port *port = irq_data_get_irq_chip_data(d);
+	struct mc_msi *msi = &port->msi;
+
+	mutex_lock(&msi->lock);
+
+	if (test_bit(d->hwirq, msi->used))
+		__clear_bit(d->hwirq, msi->used);
+	else
+		dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq);
+
+	mutex_unlock(&msi->lock);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc	= mc_irq_msi_domain_alloc,
+	.free	= mc_irq_msi_domain_free,
+};
+
+static struct irq_chip mc_msi_irq_chip = {
+	.name = "Microchip 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 mc_msi_domain_info = {
+	.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | MSI_FLAG_PCI_MSIX),
+	.chip = &mc_msi_irq_chip,
+};
+
+static int mc_allocate_msi_domains(struct mc_port *port)
+{
+	struct device *dev = port->dev;
+	struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
+	struct mc_msi *msi = &port->msi;
+
+	mutex_init(&port->msi.lock);
+
+	msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors, &msi_domain_ops, port);
+	if (!msi->dev_domain) {
+		dev_err(dev, "failed to create IRQ domain\n");
+		return -ENOMEM;
+	}
+
+	msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info, msi->dev_domain);
+	if (!msi->msi_domain) {
+		dev_err(dev, "failed to create MSI domain\n");
+		irq_domain_remove(msi->dev_domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void mc_handle_intx(struct irq_desc *desc)
+{
+	struct mc_port *port = irq_desc_get_handler_data(desc);
+	struct device *dev = port->dev;
+	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+	unsigned long status;
+	u32 bit;
+	u32 virq;
+
+	status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
+	if (status & PM_MSI_INT_INTX_MASK) {
+		status &= PM_MSI_INT_INTX_MASK;
+		status >>= PM_MSI_INT_INTX_SHIFT;
+		for_each_set_bit(bit, &status, PCI_NUM_INTX) {
+			virq = irq_find_mapping(port->intx_domain, bit);
+			if (virq)
+				generic_handle_irq(virq);
+			else
+				dev_err_ratelimited(dev, "bad INTx IRQ %d\n", bit);
+		}
+	}
+}
+
+static void mc_ack_intx_irq(struct irq_data *data)
+{
+	struct mc_port *port = irq_data_get_irq_chip_data(data);
+	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+	u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+
+	writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
+}
+
+static void mc_mask_intx_irq(struct irq_data *data)
+{
+	struct mc_port *port = irq_data_get_irq_chip_data(data);
+	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+	unsigned long flags;
+	u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+	u32 val;
+
+	raw_spin_lock_irqsave(&port->lock, flags);
+	val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
+	val &= ~mask;
+	writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
+	raw_spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void mc_unmask_intx_irq(struct irq_data *data)
+{
+	struct mc_port *port = irq_data_get_irq_chip_data(data);
+	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+	unsigned long flags;
+	u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+	u32 val;
+
+	raw_spin_lock_irqsave(&port->lock, flags);
+	val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
+	val |= mask;
+	writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
+	raw_spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static struct irq_chip mc_intx_irq_chip = {
+	.name = "Microchip PCIe INTx",
+	.irq_ack = mc_ack_intx_irq,
+	.irq_mask = mc_mask_intx_irq,
+	.irq_unmask = mc_unmask_intx_irq,
+};
+
+static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+			    irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq);
+	irq_set_chip_data(irq, domain->host_data);
+
+	return 0;
+}
+
+static const struct irq_domain_ops intx_domain_ops = {
+	.map = mc_pcie_intx_map,
+};
+
+static inline u32 reg_to_event(u32 reg, struct event_map field)
+{
+	u32 val;
+
+	val = (reg & field.reg_mask) ? BIT(field.event_bit) : 0;
+
+	return val;
+}
+
+static u32 pcie_events(void __iomem *addr)
+{
+	u32 reg = readl_relaxed(addr);
+	u32 val = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pcie_event_to_event); i++)
+		val |= reg_to_event(reg, pcie_event_to_event[i]);
+
+	return val;
+}
+
+static u32 sec_errors(void __iomem *addr)
+{
+	u32 reg = readl_relaxed(addr);
+	u32 val = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(sec_error_to_event); i++)
+		val |= reg_to_event(reg, sec_error_to_event[i]);
+
+	return val;
+}
+
+static u32 ded_errors(void __iomem *addr)
+{
+	u32 reg = readl_relaxed(addr);
+	u32 val = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ded_error_to_event); i++)
+		val |= reg_to_event(reg, ded_error_to_event[i]);
+
+	return val;
+}
+
+static u32 local_events(void __iomem *addr)
+{
+	u32 reg = readl_relaxed(addr);
+	u32 val = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(local_status_to_event); i++)
+		val |= reg_to_event(reg, local_status_to_event[i]);
+
+	return val;
+}
+
+static u32 get_events(struct mc_port *port)
+{
+	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+	void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+	u32 events = 0;
+
+	events |= pcie_events(ctrl_base_addr + PCIE_EVENT_INT);
+	events |= sec_errors(ctrl_base_addr + SEC_ERROR_INT);
+	events |= ded_errors(ctrl_base_addr + DED_ERROR_INT);
+	events |= local_events(bridge_base_addr + ISTATUS_LOCAL);
+
+	return events;
+}
+
+static irqreturn_t mc_event_handler(int irq, void *dev_id)
+{
+	struct mc_port *port = dev_id;
+	struct device *dev = port->dev;
+	struct irq_data *data;
+
+	data = irq_domain_get_irq_data(port->event_domain, irq);
+
+	if (event_cause[data->hwirq].str)
+		dev_err_ratelimited(dev, "%s\n", event_cause[data->hwirq].str);
+	else
+		dev_err_ratelimited(dev, "bad event IRQ %ld\n", data->hwirq);
+
+	return IRQ_HANDLED;
+}
+
+static void mc_handle_event(struct irq_desc *desc)
+{
+	struct mc_port *port = irq_desc_get_handler_data(desc);
+	unsigned long events;
+	u32 bit;
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+
+	chained_irq_enter(chip, desc);
+
+	events = get_events(port);
+
+	for_each_set_bit(bit, &events, NUM_EVENTS)
+		generic_handle_irq(irq_find_mapping(port->event_domain, bit));
+
+	chained_irq_exit(chip, desc);
+}
+
+static void mc_ack_event_irq(struct irq_data *data)
+{
+	struct mc_port *port = irq_data_get_irq_chip_data(data);
+	u32 event = data->hwirq;
+	void __iomem *addr;
+	u32 mask;
+
+	addr = port->axi_base_addr + event_descs[event].base + event_descs[event].offset;
+	mask = event_descs[event].mask;
+	mask |= event_descs[event].enb_mask;
+
+	writel_relaxed(mask, addr);
+}
+
+static void mc_mask_event_irq(struct irq_data *data)
+{
+	struct mc_port *port = irq_data_get_irq_chip_data(data);
+	u32 event = data->hwirq;
+	void __iomem *addr;
+	u32 mask;
+	u32 val;
+
+	addr = port->axi_base_addr + event_descs[event].base + event_descs[event].mask_offset;
+	mask = event_descs[event].mask;
+	if (event_descs[event].enb_mask) {
+		mask <<= PCIE_EVENT_INT_ENB_SHIFT;
+		mask &= PCIE_EVENT_INT_ENB_MASK;
+	}
+
+	if (!event_descs[event].mask_high)
+		mask = ~mask;
+
+	raw_spin_lock(&port->lock);
+	val = readl_relaxed(addr);
+	if (event_descs[event].mask_high)
+		val |= mask;
+	else
+		val &= mask;
+
+	writel_relaxed(val, addr);
+	raw_spin_unlock(&port->lock);
+}
+
+static void mc_unmask_event_irq(struct irq_data *data)
+{
+	struct mc_port *port = irq_data_get_irq_chip_data(data);
+	u32 event = data->hwirq;
+	void __iomem *addr;
+	u32 mask;
+	u32 val;
+
+	addr = port->axi_base_addr + event_descs[event].base + event_descs[event].mask_offset;
+	mask = event_descs[event].mask;
+
+	if (event_descs[event].enb_mask)
+		mask <<= PCIE_EVENT_INT_ENB_SHIFT;
+
+	if (event_descs[event].mask_high)
+		mask = ~mask;
+
+	if (event_descs[event].enb_mask)
+		mask &= PCIE_EVENT_INT_ENB_MASK;
+
+	raw_spin_lock(&port->lock);
+	val = readl_relaxed(addr);
+	if (event_descs[event].mask_high)
+		val &= mask;
+	else
+		val |= mask;
+	writel_relaxed(val, addr);
+	raw_spin_unlock(&port->lock);
+}
+
+static struct irq_chip mc_event_irq_chip = {
+	.name = "Microchip PCIe EVENT",
+	.irq_ack = mc_ack_event_irq,
+	.irq_mask = mc_mask_event_irq,
+	.irq_unmask = mc_unmask_event_irq,
+};
+
+static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq,
+			     irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq);
+	irq_set_chip_data(irq, domain->host_data);
+
+	return 0;
+}
+
+static const struct irq_domain_ops event_domain_ops = {
+	.map = mc_pcie_event_map,
+};
+
+static inline struct clk *mc_pcie_init_clk(struct device *dev, const char *id)
+{
+	struct clk *clk;
+	int ret;
+
+	clk = devm_clk_get_optional(dev, id);
+	if (IS_ERR(clk))
+		return clk;
+	if (!clk)
+		return clk;
+
+	ret = clk_prepare_enable(clk);
+	if (ret)
+		return ERR_PTR(ret);
+
+	devm_add_action_or_reset(dev, (void (*) (void *))clk_disable_unprepare, clk);
+
+	return clk;
+}
+
+static int mc_pcie_init_clks(struct device *dev)
+{
+	struct clk *fic;
+	int i;
+
+	/*
+	 * PCIe may be clocked via Fabric Interface
+	 * using between 1 and 4 clocks. Scan DT for
+	 * clocks and enable them if present
+	 */
+	for (i = 0; i < ARRAY_SIZE(poss_clks); i++) {
+		fic = mc_pcie_init_clk(dev, poss_clks[i]);
+		if (IS_ERR(fic))
+			return PTR_ERR(fic);
+	}
+
+	return 0;
+}
+
+static int mc_pcie_init_irq_domains(struct mc_port *port)
+{
+	struct device *dev = port->dev;
+	struct device_node *node = dev->of_node;
+	struct device_node *pcie_intc_node;
+
+	/* Setup INTx */
+	pcie_intc_node = of_get_next_child(node, NULL);
+	if (!pcie_intc_node) {
+		dev_err(dev, "failed to find PCIe Intc node\n");
+		return -EINVAL;
+	}
+
+	port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS, &event_domain_ops,
+						   port);
+	if (!port->event_domain) {
+		dev_err(dev, "failed to get event domain\n");
+		return -ENOMEM;
+	}
+
+	irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS);
+
+	port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, &intx_domain_ops,
+						  port);
+	if (!port->intx_domain) {
+		dev_err(dev, "failed to get an INTx IRQ domain\n");
+		return -ENOMEM;
+	}
+
+	irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);
+
+	of_node_put(pcie_intc_node);
+	raw_spin_lock_init(&port->lock);
+
+	return mc_allocate_msi_domains(port);
+}
+
+static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, phys_addr_t axi_addr,
+				 phys_addr_t pci_addr, size_t size)
+{
+	u32 atr_sz = ilog2(size) - 1;
+	u32 val;
+
+	if (index == 0)
+		val = PCIE_CONFIG_INTERFACE;
+	else
+		val = PCIE_TX_RX_INTERFACE;
+
+	writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ATR0_AXI4_SLV0_TRSL_PARAM);
+
+	val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) | ATR_IMPL_ENABLE;
+	writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+	       ATR0_AXI4_SLV0_SRCADDR_PARAM);
+
+	val = upper_32_bits(axi_addr);
+	writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+	       ATR0_AXI4_SLV0_SRC_ADDR);
+
+	val = lower_32_bits(pci_addr);
+	writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+	       ATR0_AXI4_SLV0_TRSL_ADDR_LSB);
+
+	val = upper_32_bits(pci_addr);
+	writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+	       ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
+
+	val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
+	val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT);
+	writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
+	writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
+}
+
+static int mc_pcie_setup_windows(struct platform_device *pdev, struct mc_port *port)
+{
+	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+	struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
+	struct resource_entry *entry;
+	u64 pci_addr;
+	u32 index = 1;
+
+	resource_list_for_each_entry(entry, &bridge->windows) {
+		if (resource_type(entry->res) == IORESOURCE_MEM) {
+			pci_addr = entry->res->start - entry->offset;
+			mc_pcie_setup_window(bridge_base_addr, index, entry->res->start,
+					     pci_addr, resource_size(entry->res));
+			index++;
+		}
+	}
+
+	return 0;
+}
+
+static int mc_platform_init(struct pci_config_window *cfg)
+{
+	struct device *dev = cfg->parent;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mc_port *port;
+	void __iomem *bridge_base_addr;
+	void __iomem *ctrl_base_addr;
+	int ret;
+	int irq;
+	int i, intx_irq, msi_irq, event_irq;
+	u32 val;
+	int err;
+
+	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+	port->dev = dev;
+
+	ret = mc_pcie_init_clks(dev);
+	if (ret) {
+		dev_err(dev, "failed to get clock resources, error %d\n", ret);
+		return -ENODEV;
+	}
+
+	port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1);
+	if (IS_ERR(port->axi_base_addr))
+		return PTR_ERR(port->axi_base_addr);
+
+	bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+	ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+
+	port->msi.vector_phy = MSI_ADDR;
+	port->msi.num_vectors = MC_NUM_MSI_IRQS;
+	ret = mc_pcie_init_irq_domains(port);
+	if (ret) {
+		dev_err(dev, "failed creating IRQ domains\n");
+		return ret;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "unable to request IRQ%d\n", irq);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < NUM_EVENTS; i++) {
+		event_irq = irq_create_mapping(port->event_domain, i);
+		if (!event_irq) {
+			dev_err(dev, "failed to map interrupt\n");
+			return -ENXIO;
+		}
+
+		err = devm_request_irq(dev, event_irq, mc_event_handler,
+				       0, event_cause[i].sym, port);
+		if (err) {
+			dev_err(dev, "failed to request IRQ %d\n", event_irq);
+			return err;
+		}
+	}
+
+	intx_irq = irq_create_mapping(port->event_domain, EVENT_LOCAL_PM_MSI_INT_INTX);
+	if (!intx_irq) {
+		dev_err(dev, "failed to map INTx interrupt\n");
+		return -ENXIO;
+	}
+
+	/* Plug the INTx chained handler */
+	irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port);
+
+	msi_irq = irq_create_mapping(port->event_domain, EVENT_LOCAL_PM_MSI_INT_MSI);
+	if (!msi_irq)
+		return -ENXIO;
+
+	/* Plug the MSI chained handler */
+	irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port);
+
+	/* Plug the main event chained handler */
+	irq_set_chained_handler_and_data(irq, mc_handle_event, port);
+
+	/* Hardware doesn't setup MSI by default */
+	mc_pcie_enable_msi(port, cfg->win);
+
+	val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
+	val |= PM_MSI_INT_INTX_MASK;
+	writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
+
+	writel_relaxed(val, ctrl_base_addr + ECC_CONTROL);
+
+	val = PCIE_EVENT_INT_L2_EXIT_INT | PCIE_EVENT_INT_HOTRST_EXIT_INT |
+	      PCIE_EVENT_INT_DLUP_EXIT_INT;
+	writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT);
+
+	val = SEC_ERROR_INT_TX_RAM_SEC_ERR_INT | SEC_ERROR_INT_RX_RAM_SEC_ERR_INT |
+	      SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT | SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT;
+	writel_relaxed(val, ctrl_base_addr + SEC_ERROR_INT);
+	writel_relaxed(0, ctrl_base_addr + SEC_ERROR_INT_MASK);
+	writel_relaxed(0, ctrl_base_addr + SEC_ERROR_CNT);
+
+	val = DED_ERROR_INT_TX_RAM_DED_ERR_INT | DED_ERROR_INT_RX_RAM_DED_ERR_INT |
+	      DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT | DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT;
+	writel_relaxed(val, ctrl_base_addr + DED_ERROR_INT);
+	writel_relaxed(0, ctrl_base_addr + DED_ERROR_INT_MASK);
+	writel_relaxed(0, ctrl_base_addr + DED_ERROR_CNT);
+
+	writel_relaxed(0, bridge_base_addr + IMASK_HOST);
+	writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
+
+	/* Configure Address Translation Table 0 for PCIe config space */
+	mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start & 0xffffffff, cfg->res.start,
+			     resource_size(&cfg->res));
+
+	return mc_pcie_setup_windows(pdev, port);
+}
+
+static const struct pci_ecam_ops mc_ecam_ops = {
+	.init = mc_platform_init,
+	.pci_ops = {
+		.map_bus = pci_ecam_map_bus,
+		.read = pci_generic_config_read,
+		.write = pci_generic_config_write,
+	}
+};
+
+static const struct of_device_id mc_pcie_of_match[] = {
+	{
+		.compatible = "microchip,pcie-host-1.0",
+		.data = &mc_ecam_ops,
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, mc_pcie_of_match)
+
+static struct platform_driver mc_pcie_driver = {
+	.probe = pci_host_common_probe,
+	.driver = {
+		.name = "microchip-pcie",
+		.of_match_table = mc_pcie_of_match,
+		.suppress_bind_attrs = true,
+	},
+};
+
+builtin_platform_driver(mc_pcie_driver);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Microchip PCIe host controller driver");
+MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
-- 
2.25.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v21 4/4] MAINTAINERS: Add Daire McNamara as maintainer for the Microchip PCIe driver
  2021-01-25 16:29 [PATCH v21 0/4] PCI: microchip: Add host driver for Microchip PCIe controller daire.mcnamara
                   ` (2 preceding siblings ...)
  2021-01-25 16:29 ` [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller daire.mcnamara
@ 2021-01-25 16:29 ` daire.mcnamara
  2021-02-04 11:42 ` [PATCH v21 0/4] PCI: microchip: Add host driver for Microchip PCIe controller Lorenzo Pieralisi
  4 siblings, 0 replies; 15+ messages in thread
From: daire.mcnamara @ 2021-01-25 16:29 UTC (permalink / raw)
  To: lorenzo.pieralisi, bhelgaas, robh, linux-pci, robh+dt, devicetree
  Cc: david.abdurachmanov, cyril.jean, Daire McNamara

From: Daire McNamara <daire.mcnamara@microchip.com>

Signed-off-by: Daire McNamara <daire.mcnamara@microchip.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index e73636b75f29..f2dafbf3393c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13628,6 +13628,13 @@ S:	Supported
 F:	Documentation/devicetree/bindings/pci/mediatek*
 F:	drivers/pci/controller/*mediatek*
 
+PCIE DRIVER FOR MICROCHIP
+M:	Daire McNamara <daire.mcnamara@microchip.com>
+L:	linux-pci@vger.kernel.org
+S:	Supported
+F:	Documentation/devicetree/bindings/pci/microchip*
+F:	drivers/pci/controller/*microchip*
+
 PCIE DRIVER FOR QUALCOMM MSM
 M:	Stanimir Varbanov <svarbanov@mm-sol.com>
 L:	linux-pci@vger.kernel.org
-- 
2.25.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller
  2021-01-25 16:29 ` [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller daire.mcnamara
@ 2021-02-01 19:05   ` Lorenzo Pieralisi
       [not found]     ` <MN2PR11MB42691AE1B54DEAB5C1BAA11D96B59@MN2PR11MB4269.namprd11.prod.outlook.com>
  2021-02-10 13:07   ` Geert Uytterhoeven
  2022-01-27 20:20   ` Bjorn Helgaas
  2 siblings, 1 reply; 15+ messages in thread
From: Lorenzo Pieralisi @ 2021-02-01 19:05 UTC (permalink / raw)
  To: daire.mcnamara
  Cc: bhelgaas, robh, linux-pci, robh+dt, devicetree,
	david.abdurachmanov, cyril.jean

On Mon, Jan 25, 2021 at 04:29:33PM +0000, daire.mcnamara@microchip.com wrote:
> From: Daire McNamara <daire.mcnamara@microchip.com>
> 
> Add support for the Microchip PolarFire PCIe controller when
> configured in host (Root Complex) mode.
> 
> Signed-off-by: Daire McNamara <daire.mcnamara@microchip.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
> ---
>  drivers/pci/controller/Kconfig               |   10 +
>  drivers/pci/controller/Makefile              |    1 +
>  drivers/pci/controller/pcie-microchip-host.c | 1114 ++++++++++++++++++
>  3 files changed, 1125 insertions(+)
>  create mode 100644 drivers/pci/controller/pcie-microchip-host.c
> 
> diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
> index 64e2f5e379aa..bca2f8949510 100644
> --- a/drivers/pci/controller/Kconfig
> +++ b/drivers/pci/controller/Kconfig
> @@ -298,6 +298,16 @@ config PCI_LOONGSON
>  	  Say Y here if you want to enable PCI controller support on
>  	  Loongson systems.
>  
> +config PCIE_MICROCHIP_HOST
> +	bool "Microchip AXI PCIe host bridge support"
> +	depends on PCI_MSI && OF
> +	select PCI_MSI_IRQ_DOMAIN
> +	select GENERIC_MSI_IRQ_DOMAIN
> +	select PCI_HOST_COMMON
> +	help
> +	  Say Y here if you want kernel to support the Microchip AXI PCIe
> +	  Host Bridge driver.
> +
>  config PCIE_HISI_ERR
>  	depends on ACPI_APEI_GHES && (ARM64 || COMPILE_TEST)
>  	bool "HiSilicon HIP PCIe controller error handling driver"
> diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
> index 04c6edc285c5..b85fcd574ff6 100644
> --- a/drivers/pci/controller/Makefile
> +++ b/drivers/pci/controller/Makefile
> @@ -28,6 +28,7 @@ obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-rockchip-ep.o
>  obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o
>  obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o
>  obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
> +obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o
>  obj-$(CONFIG_VMD) += vmd.o
>  obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
>  obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o
> diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c
> new file mode 100644
> index 000000000000..0ef2beab185b
> --- /dev/null
> +++ b/drivers/pci/controller/pcie-microchip-host.c
> @@ -0,0 +1,1114 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Microchip AXI PCIe Bridge host controller driver
> + *
> + * Copyright (c) 2018 - 2020 Microchip Corporation. All rights reserved.
2021 - I will update it for you if that's OK.

> + * Author: Daire McNamara <daire.mcnamara@microchip.com>
> + *
> + * Based on:
> + *	pcie-rcar.c
> + *	pcie-xilinx.c
> + *	pcie-altera.c

Can I remove these references please ? I said that before, let me know
please, do not respost another version.

Thanks,
Lorenzo

> + */
> +
> +#include <linux/clk.h>
> +#include <linux/irqchip/chained_irq.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/pci-ecam.h>
> +#include <linux/platform_device.h>
> +
> +#include "../pci.h"
> +
> +/* Number of MSI IRQs */
> +#define MC_NUM_MSI_IRQS				32
> +#define MC_NUM_MSI_IRQS_CODED			5
> +
> +/* PCIe Bridge Phy and Controller Phy offsets */
> +#define MC_PCIE1_BRIDGE_ADDR			0x00008000u
> +#define MC_PCIE1_CTRL_ADDR			0x0000a000u
> +
> +#define MC_PCIE_BRIDGE_ADDR			(MC_PCIE1_BRIDGE_ADDR)
> +#define MC_PCIE_CTRL_ADDR			(MC_PCIE1_CTRL_ADDR)
> +
> +/* PCIe Controller Phy Regs */
> +#define SEC_ERROR_CNT				0x20
> +#define DED_ERROR_CNT				0x24
> +#define SEC_ERROR_INT				0x28
> +#define  SEC_ERROR_INT_TX_RAM_SEC_ERR_INT	GENMASK(3, 0)
> +#define  SEC_ERROR_INT_RX_RAM_SEC_ERR_INT	GENMASK(7, 4)
> +#define  SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT	GENMASK(11, 8)
> +#define  SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT	GENMASK(15, 12)
> +#define  NUM_SEC_ERROR_INTS			(4)
> +#define SEC_ERROR_INT_MASK			0x2c
> +#define DED_ERROR_INT				0x30
> +#define  DED_ERROR_INT_TX_RAM_DED_ERR_INT	GENMASK(3, 0)
> +#define  DED_ERROR_INT_RX_RAM_DED_ERR_INT	GENMASK(7, 4)
> +#define  DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT	GENMASK(11, 8)
> +#define  DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT	GENMASK(15, 12)
> +#define  NUM_DED_ERROR_INTS			(4)
> +#define DED_ERROR_INT_MASK			0x34
> +#define ECC_CONTROL				0x38
> +#define  ECC_CONTROL_TX_RAM_INJ_ERROR_0		BIT(0)
> +#define  ECC_CONTROL_TX_RAM_INJ_ERROR_1		BIT(1)
> +#define  ECC_CONTROL_TX_RAM_INJ_ERROR_2		BIT(2)
> +#define  ECC_CONTROL_TX_RAM_INJ_ERROR_3		BIT(3)
> +#define  ECC_CONTROL_RX_RAM_INJ_ERROR_0		BIT(4)
> +#define  ECC_CONTROL_RX_RAM_INJ_ERROR_1		BIT(5)
> +#define  ECC_CONTROL_RX_RAM_INJ_ERROR_2		BIT(6)
> +#define  ECC_CONTROL_RX_RAM_INJ_ERROR_3		BIT(7)
> +#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_0	BIT(8)
> +#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_1	BIT(9)
> +#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_2	BIT(10)
> +#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_3	BIT(11)
> +#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_0	BIT(12)
> +#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_1	BIT(13)
> +#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_2	BIT(14)
> +#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_3	BIT(15)
> +#define  ECC_CONTROL_TX_RAM_ECC_BYPASS		BIT(24)
> +#define  ECC_CONTROL_RX_RAM_ECC_BYPASS		BIT(25)
> +#define  ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS	BIT(26)
> +#define  ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS	BIT(27)
> +#define LTSSM_STATE				0x5c
> +#define  LTSSM_L0_STATE				0x10
> +#define PCIE_EVENT_INT				0x14c
> +#define  PCIE_EVENT_INT_L2_EXIT_INT		BIT(0)
> +#define  PCIE_EVENT_INT_HOTRST_EXIT_INT		BIT(1)
> +#define  PCIE_EVENT_INT_DLUP_EXIT_INT		BIT(2)
> +#define  PCIE_EVENT_INT_MASK			GENMASK(2, 0)
> +#define  PCIE_EVENT_INT_L2_EXIT_INT_MASK	BIT(16)
> +#define  PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK	BIT(17)
> +#define  PCIE_EVENT_INT_DLUP_EXIT_INT_MASK	BIT(18)
> +#define  PCIE_EVENT_INT_ENB_MASK		GENMASK(18, 16)
> +#define  PCIE_EVENT_INT_ENB_SHIFT		16
> +#define  NUM_PCIE_EVENTS			(3)
> +
> +/* PCIe Bridge Phy Regs */
> +#define PCIE_PCI_IDS_DW1			0x9c
> +
> +/* PCIe Config space MSI capability structure */
> +#define MC_MSI_CAP_CTRL_OFFSET			0xe0u
> +#define  MC_MSI_MAX_Q_AVAIL			(MC_NUM_MSI_IRQS_CODED << 1)
> +#define  MC_MSI_Q_SIZE				(MC_NUM_MSI_IRQS_CODED << 4)
> +
> +#define IMASK_LOCAL				0x180
> +#define  DMA_END_ENGINE_0_MASK			0x00000000u
> +#define  DMA_END_ENGINE_0_SHIFT			0
> +#define  DMA_END_ENGINE_1_MASK			0x00000000u
> +#define  DMA_END_ENGINE_1_SHIFT			1
> +#define  DMA_ERROR_ENGINE_0_MASK		0x00000100u
> +#define  DMA_ERROR_ENGINE_0_SHIFT		8
> +#define  DMA_ERROR_ENGINE_1_MASK		0x00000200u
> +#define  DMA_ERROR_ENGINE_1_SHIFT		9
> +#define  A_ATR_EVT_POST_ERR_MASK		0x00010000u
> +#define  A_ATR_EVT_POST_ERR_SHIFT		16
> +#define  A_ATR_EVT_FETCH_ERR_MASK		0x00020000u
> +#define  A_ATR_EVT_FETCH_ERR_SHIFT		17
> +#define  A_ATR_EVT_DISCARD_ERR_MASK		0x00040000u
> +#define  A_ATR_EVT_DISCARD_ERR_SHIFT		18
> +#define  A_ATR_EVT_DOORBELL_MASK		0x00000000u
> +#define  A_ATR_EVT_DOORBELL_SHIFT		19
> +#define  P_ATR_EVT_POST_ERR_MASK		0x00100000u
> +#define  P_ATR_EVT_POST_ERR_SHIFT		20
> +#define  P_ATR_EVT_FETCH_ERR_MASK		0x00200000u
> +#define  P_ATR_EVT_FETCH_ERR_SHIFT		21
> +#define  P_ATR_EVT_DISCARD_ERR_MASK		0x00400000u
> +#define  P_ATR_EVT_DISCARD_ERR_SHIFT		22
> +#define  P_ATR_EVT_DOORBELL_MASK		0x00000000u
> +#define  P_ATR_EVT_DOORBELL_SHIFT		23
> +#define  PM_MSI_INT_INTA_MASK			0x01000000u
> +#define  PM_MSI_INT_INTA_SHIFT			24
> +#define  PM_MSI_INT_INTB_MASK			0x02000000u
> +#define  PM_MSI_INT_INTB_SHIFT			25
> +#define  PM_MSI_INT_INTC_MASK			0x04000000u
> +#define  PM_MSI_INT_INTC_SHIFT			26
> +#define  PM_MSI_INT_INTD_MASK			0x08000000u
> +#define  PM_MSI_INT_INTD_SHIFT			27
> +#define  PM_MSI_INT_INTX_MASK			0x0f000000u
> +#define  PM_MSI_INT_INTX_SHIFT			24
> +#define  PM_MSI_INT_MSI_MASK			0x10000000u
> +#define  PM_MSI_INT_MSI_SHIFT			28
> +#define  PM_MSI_INT_AER_EVT_MASK		0x20000000u
> +#define  PM_MSI_INT_AER_EVT_SHIFT		29
> +#define  PM_MSI_INT_EVENTS_MASK			0x40000000u
> +#define  PM_MSI_INT_EVENTS_SHIFT		30
> +#define  PM_MSI_INT_SYS_ERR_MASK		0x80000000u
> +#define  PM_MSI_INT_SYS_ERR_SHIFT		31
> +#define  NUM_LOCAL_EVENTS			15
> +#define ISTATUS_LOCAL				0x184
> +#define IMASK_HOST				0x188
> +#define ISTATUS_HOST				0x18c
> +#define MSI_ADDR				0x190
> +#define ISTATUS_MSI				0x194
> +
> +/* PCIe Master table init defines */
> +#define ATR0_PCIE_WIN0_SRCADDR_PARAM		0x600u
> +#define  ATR0_PCIE_ATR_SIZE			0x25
> +#define  ATR0_PCIE_ATR_SIZE_SHIFT		1
> +#define ATR0_PCIE_WIN0_SRC_ADDR			0x604u
> +#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB		0x608u
> +#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW		0x60cu
> +#define ATR0_PCIE_WIN0_TRSL_PARAM		0x610u
> +
> +/* PCIe AXI slave table init defines */
> +#define ATR0_AXI4_SLV0_SRCADDR_PARAM		0x800u
> +#define  ATR_SIZE_SHIFT				1
> +#define  ATR_IMPL_ENABLE			1
> +#define ATR0_AXI4_SLV0_SRC_ADDR			0x804u
> +#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB		0x808u
> +#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW		0x80cu
> +#define ATR0_AXI4_SLV0_TRSL_PARAM		0x810u
> +#define  PCIE_TX_RX_INTERFACE			0x00000000u
> +#define  PCIE_CONFIG_INTERFACE			0x00000001u
> +
> +#define ATR_ENTRY_SIZE				32
> +
> +#define EVENT_PCIE_L2_EXIT			0
> +#define EVENT_PCIE_HOTRST_EXIT			1
> +#define EVENT_PCIE_DLUP_EXIT			2
> +#define EVENT_SEC_TX_RAM_SEC_ERR		3
> +#define EVENT_SEC_RX_RAM_SEC_ERR		4
> +#define EVENT_SEC_AXI2PCIE_RAM_SEC_ERR		5
> +#define EVENT_SEC_PCIE2AXI_RAM_SEC_ERR		6
> +#define EVENT_DED_TX_RAM_DED_ERR		7
> +#define EVENT_DED_RX_RAM_DED_ERR		8
> +#define EVENT_DED_AXI2PCIE_RAM_DED_ERR		9
> +#define EVENT_DED_PCIE2AXI_RAM_DED_ERR		10
> +#define EVENT_LOCAL_DMA_END_ENGINE_0		11
> +#define EVENT_LOCAL_DMA_END_ENGINE_1		12
> +#define EVENT_LOCAL_DMA_ERROR_ENGINE_0		13
> +#define EVENT_LOCAL_DMA_ERROR_ENGINE_1		14
> +#define EVENT_LOCAL_A_ATR_EVT_POST_ERR		15
> +#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR		16
> +#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR	17
> +#define EVENT_LOCAL_A_ATR_EVT_DOORBELL		18
> +#define EVENT_LOCAL_P_ATR_EVT_POST_ERR		19
> +#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR		20
> +#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR	21
> +#define EVENT_LOCAL_P_ATR_EVT_DOORBELL		22
> +#define EVENT_LOCAL_PM_MSI_INT_INTX		23
> +#define EVENT_LOCAL_PM_MSI_INT_MSI		24
> +#define EVENT_LOCAL_PM_MSI_INT_AER_EVT		25
> +#define EVENT_LOCAL_PM_MSI_INT_EVENTS		26
> +#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR		27
> +#define NUM_EVENTS				28
> +
> +#define PCIE_EVENT_CAUSE(x, s)	\
> +	[EVENT_PCIE_ ## x] = { __stringify(x), s }
> +
> +#define SEC_ERROR_CAUSE(x, s) \
> +	[EVENT_SEC_ ## x] = { __stringify(x), s }
> +
> +#define DED_ERROR_CAUSE(x, s) \
> +	[EVENT_DED_ ## x] = { __stringify(x), s }
> +
> +#define LOCAL_EVENT_CAUSE(x, s) \
> +	[EVENT_LOCAL_ ## x] = { __stringify(x), s }
> +
> +#define PCIE_EVENT(x) \
> +	.base = MC_PCIE_CTRL_ADDR, \
> +	.offset = PCIE_EVENT_INT, \
> +	.mask_offset = PCIE_EVENT_INT, \
> +	.mask_high = 1, \
> +	.mask = PCIE_EVENT_INT_ ## x ## _INT, \
> +	.enb_mask = PCIE_EVENT_INT_ENB_MASK
> +
> +#define SEC_EVENT(x) \
> +	.base = MC_PCIE_CTRL_ADDR, \
> +	.offset = SEC_ERROR_INT, \
> +	.mask_offset = SEC_ERROR_INT_MASK, \
> +	.mask = SEC_ERROR_INT_ ## x ## _INT, \
> +	.mask_high = 1, \
> +	.enb_mask = 0
> +
> +#define DED_EVENT(x) \
> +	.base = MC_PCIE_CTRL_ADDR, \
> +	.offset = DED_ERROR_INT, \
> +	.mask_offset = DED_ERROR_INT_MASK, \
> +	.mask_high = 1, \
> +	.mask = DED_ERROR_INT_ ## x ## _INT, \
> +	.enb_mask = 0
> +
> +#define LOCAL_EVENT(x) \
> +	.base = MC_PCIE_BRIDGE_ADDR, \
> +	.offset = ISTATUS_LOCAL, \
> +	.mask_offset = IMASK_LOCAL, \
> +	.mask_high = 0, \
> +	.mask = x ## _MASK, \
> +	.enb_mask = 0
> +
> +#define PCIE_EVENT_TO_EVENT_MAP(x) \
> +	{ PCIE_EVENT_INT_ ## x ## _INT, EVENT_PCIE_ ## x }
> +
> +#define SEC_ERROR_TO_EVENT_MAP(x) \
> +	{ SEC_ERROR_INT_ ## x ## _INT, EVENT_SEC_ ## x }
> +
> +#define DED_ERROR_TO_EVENT_MAP(x) \
> +	{ DED_ERROR_INT_ ## x ## _INT, EVENT_DED_ ## x }
> +
> +#define LOCAL_STATUS_TO_EVENT_MAP(x) \
> +	{ x ## _MASK, EVENT_LOCAL_ ## x }
> +
> +struct event_map {
> +	u32 reg_mask;
> +	u32 event_bit;
> +};
> +
> +struct mc_msi {
> +	struct mutex lock;		/* Protect used bitmap */
> +	struct irq_domain *msi_domain;
> +	struct irq_domain *dev_domain;
> +	u32 num_vectors;
> +	u64 vector_phy;
> +	DECLARE_BITMAP(used, MC_NUM_MSI_IRQS);
> +};
> +
> +struct mc_port {
> +	void __iomem *axi_base_addr;
> +	struct device *dev;
> +	struct irq_domain *intx_domain;
> +	struct irq_domain *event_domain;
> +	raw_spinlock_t lock;
> +	struct mc_msi msi;
> +};
> +
> +struct cause {
> +	const char *sym;
> +	const char *str;
> +};
> +
> +static const struct cause event_cause[NUM_EVENTS] = {
> +	PCIE_EVENT_CAUSE(L2_EXIT, "L2 exit event"),
> +	PCIE_EVENT_CAUSE(HOTRST_EXIT, "Hot reset exit event"),
> +	PCIE_EVENT_CAUSE(DLUP_EXIT, "DLUP exit event"),
> +	SEC_ERROR_CAUSE(TX_RAM_SEC_ERR,  "sec error in tx buffer"),
> +	SEC_ERROR_CAUSE(RX_RAM_SEC_ERR,  "sec error in rx buffer"),
> +	SEC_ERROR_CAUSE(PCIE2AXI_RAM_SEC_ERR,  "sec error in pcie2axi buffer"),
> +	SEC_ERROR_CAUSE(AXI2PCIE_RAM_SEC_ERR,  "sec error in axi2pcie buffer"),
> +	DED_ERROR_CAUSE(TX_RAM_DED_ERR,  "ded error in tx buffer"),
> +	DED_ERROR_CAUSE(RX_RAM_DED_ERR,  "ded error in rx buffer"),
> +	DED_ERROR_CAUSE(PCIE2AXI_RAM_DED_ERR,  "ded error in pcie2axi buffer"),
> +	DED_ERROR_CAUSE(AXI2PCIE_RAM_DED_ERR,  "ded error in axi2pcie buffer"),
> +	LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_0, "dma engine 0 error"),
> +	LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_1, "dma engine 1 error"),
> +	LOCAL_EVENT_CAUSE(A_ATR_EVT_POST_ERR, "axi write request error"),
> +	LOCAL_EVENT_CAUSE(A_ATR_EVT_FETCH_ERR, "axi read request error"),
> +	LOCAL_EVENT_CAUSE(A_ATR_EVT_DISCARD_ERR, "axi read timeout"),
> +	LOCAL_EVENT_CAUSE(P_ATR_EVT_POST_ERR, "pcie write request error"),
> +	LOCAL_EVENT_CAUSE(P_ATR_EVT_FETCH_ERR, "pcie read request error"),
> +	LOCAL_EVENT_CAUSE(P_ATR_EVT_DISCARD_ERR, "pcie read timeout"),
> +	LOCAL_EVENT_CAUSE(PM_MSI_INT_AER_EVT, "aer event"),
> +	LOCAL_EVENT_CAUSE(PM_MSI_INT_EVENTS, "pm/ltr/hotplug event"),
> +	LOCAL_EVENT_CAUSE(PM_MSI_INT_SYS_ERR, "system error"),
> +};
> +
> +struct event_map pcie_event_to_event[] = {
> +	PCIE_EVENT_TO_EVENT_MAP(L2_EXIT),
> +	PCIE_EVENT_TO_EVENT_MAP(HOTRST_EXIT),
> +	PCIE_EVENT_TO_EVENT_MAP(DLUP_EXIT)
> +};
> +
> +struct event_map sec_error_to_event[] = {
> +	SEC_ERROR_TO_EVENT_MAP(TX_RAM_SEC_ERR),
> +	SEC_ERROR_TO_EVENT_MAP(RX_RAM_SEC_ERR),
> +	SEC_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_SEC_ERR),
> +	SEC_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_SEC_ERR)
> +};
> +
> +struct event_map ded_error_to_event[] = {
> +	DED_ERROR_TO_EVENT_MAP(TX_RAM_DED_ERR),
> +	DED_ERROR_TO_EVENT_MAP(RX_RAM_DED_ERR),
> +	DED_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_DED_ERR),
> +	DED_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_DED_ERR)
> +};
> +
> +struct event_map local_status_to_event[] = {
> +	LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_0),
> +	LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_1),
> +	LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_0),
> +	LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_1),
> +	LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_POST_ERR),
> +	LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_FETCH_ERR),
> +	LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DISCARD_ERR),
> +	LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DOORBELL),
> +	LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_POST_ERR),
> +	LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_FETCH_ERR),
> +	LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DISCARD_ERR),
> +	LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DOORBELL),
> +	LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_INTX),
> +	LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_MSI),
> +	LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_AER_EVT),
> +	LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_EVENTS),
> +	LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_SYS_ERR),
> +};
> +
> +struct {
> +	u32 base;
> +	u32 offset;
> +	u32 mask;
> +	u32 shift;
> +	u32 enb_mask;
> +	u32 mask_high;
> +	u32 mask_offset;
> +} event_descs[] = {
> +	{ PCIE_EVENT(L2_EXIT) },
> +	{ PCIE_EVENT(HOTRST_EXIT) },
> +	{ PCIE_EVENT(DLUP_EXIT) },
> +	{ SEC_EVENT(TX_RAM_SEC_ERR) },
> +	{ SEC_EVENT(RX_RAM_SEC_ERR) },
> +	{ SEC_EVENT(PCIE2AXI_RAM_SEC_ERR) },
> +	{ SEC_EVENT(AXI2PCIE_RAM_SEC_ERR) },
> +	{ DED_EVENT(TX_RAM_DED_ERR) },
> +	{ DED_EVENT(RX_RAM_DED_ERR) },
> +	{ DED_EVENT(PCIE2AXI_RAM_DED_ERR) },
> +	{ DED_EVENT(AXI2PCIE_RAM_DED_ERR) },
> +	{ LOCAL_EVENT(DMA_END_ENGINE_0) },
> +	{ LOCAL_EVENT(DMA_END_ENGINE_1) },
> +	{ LOCAL_EVENT(DMA_ERROR_ENGINE_0) },
> +	{ LOCAL_EVENT(DMA_ERROR_ENGINE_1) },
> +	{ LOCAL_EVENT(A_ATR_EVT_POST_ERR) },
> +	{ LOCAL_EVENT(A_ATR_EVT_FETCH_ERR) },
> +	{ LOCAL_EVENT(A_ATR_EVT_DISCARD_ERR) },
> +	{ LOCAL_EVENT(A_ATR_EVT_DOORBELL) },
> +	{ LOCAL_EVENT(P_ATR_EVT_POST_ERR) },
> +	{ LOCAL_EVENT(P_ATR_EVT_FETCH_ERR) },
> +	{ LOCAL_EVENT(P_ATR_EVT_DISCARD_ERR) },
> +	{ LOCAL_EVENT(P_ATR_EVT_DOORBELL) },
> +	{ LOCAL_EVENT(PM_MSI_INT_INTX) },
> +	{ LOCAL_EVENT(PM_MSI_INT_MSI) },
> +	{ LOCAL_EVENT(PM_MSI_INT_AER_EVT) },
> +	{ LOCAL_EVENT(PM_MSI_INT_EVENTS) },
> +	{ LOCAL_EVENT(PM_MSI_INT_SYS_ERR) }
> +};
> +
> +static char poss_clks[][5] = { "fic0", "fic1", "fic2", "fic3" };
> +
> +static void mc_pcie_enable_msi(struct mc_port *port, void __iomem *base)
> +{
> +	struct mc_msi *msi = &port->msi;
> +	u32 cap_offset = MC_MSI_CAP_CTRL_OFFSET;
> +	u16 msg_ctrl = readw_relaxed(base + cap_offset + PCI_MSI_FLAGS);
> +
> +	msg_ctrl |= PCI_MSI_FLAGS_ENABLE;
> +	msg_ctrl &= ~PCI_MSI_FLAGS_QMASK;
> +	msg_ctrl |= MC_MSI_MAX_Q_AVAIL;
> +	msg_ctrl &= ~PCI_MSI_FLAGS_QSIZE;
> +	msg_ctrl |= MC_MSI_Q_SIZE;
> +	msg_ctrl |= PCI_MSI_FLAGS_64BIT;
> +
> +	writew_relaxed(msg_ctrl, base + cap_offset + PCI_MSI_FLAGS);
> +
> +	writel_relaxed(lower_32_bits(msi->vector_phy), base + cap_offset + PCI_MSI_ADDRESS_LO);
> +	writel_relaxed(upper_32_bits(msi->vector_phy), base + cap_offset + PCI_MSI_ADDRESS_HI);
> +}
> +
> +static void mc_handle_msi(struct irq_desc *desc)
> +{
> +	struct mc_port *port = irq_desc_get_handler_data(desc);
> +	struct device *dev = port->dev;
> +	struct mc_msi *msi = &port->msi;
> +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> +	unsigned long status;
> +	u32 bit;
> +	u32 virq;
> +
> +	status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
> +	if (status & PM_MSI_INT_MSI_MASK) {
> +		status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
> +		for_each_set_bit(bit, &status, msi->num_vectors) {
> +			virq = irq_find_mapping(msi->dev_domain, bit);
> +			if (virq)
> +				generic_handle_irq(virq);
> +			else
> +				dev_err_ratelimited(dev, "bad MSI IRQ %d\n", bit);
> +		}
> +	}
> +}
> +
> +static void mc_msi_bottom_irq_ack(struct irq_data *data)
> +{
> +	struct mc_port *port = irq_data_get_irq_chip_data(data);
> +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> +	u32 bitpos = data->hwirq;
> +	unsigned long status;
> +
> +	writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
> +	status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
> +	if (!status)
> +		writel_relaxed(BIT(PM_MSI_INT_MSI_SHIFT), bridge_base_addr + ISTATUS_LOCAL);
> +}
> +
> +static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +	struct mc_port *port = irq_data_get_irq_chip_data(data);
> +	phys_addr_t addr = port->msi.vector_phy;
> +
> +	msg->address_lo = lower_32_bits(addr);
> +	msg->address_hi = upper_32_bits(addr);
> +	msg->data = data->hwirq;
> +
> +	dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n", (int)data->hwirq,
> +		msg->address_hi, msg->address_lo);
> +}
> +
> +static int mc_msi_set_affinity(struct irq_data *irq_data, const struct cpumask *mask, bool force)
> +{
> +	return -EINVAL;
> +}
> +
> +static struct irq_chip mc_msi_bottom_irq_chip = {
> +	.name = "Microchip MSI",
> +	.irq_ack = mc_msi_bottom_irq_ack,
> +	.irq_compose_msi_msg = mc_compose_msi_msg,
> +	.irq_set_affinity = mc_msi_set_affinity,
> +};
> +
> +static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +				   unsigned int nr_irqs, void *args)
> +{
> +	struct mc_port *port = domain->host_data;
> +	struct mc_msi *msi = &port->msi;
> +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> +	unsigned long bit;
> +	u32 val;
> +
> +	mutex_lock(&msi->lock);
> +	bit = find_first_zero_bit(msi->used, msi->num_vectors);
> +	if (bit >= msi->num_vectors) {
> +		mutex_unlock(&msi->lock);
> +		return -ENOSPC;
> +	}
> +
> +	set_bit(bit, msi->used);
> +
> +	irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip, domain->host_data,
> +			    handle_edge_irq, NULL, NULL);
> +
> +	/* Enable MSI interrupts */
> +	val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
> +	val |= PM_MSI_INT_MSI_MASK;
> +	writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
> +
> +	mutex_unlock(&msi->lock);
> +
> +	return 0;
> +}
> +
> +static void mc_irq_msi_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 mc_port *port = irq_data_get_irq_chip_data(d);
> +	struct mc_msi *msi = &port->msi;
> +
> +	mutex_lock(&msi->lock);
> +
> +	if (test_bit(d->hwirq, msi->used))
> +		__clear_bit(d->hwirq, msi->used);
> +	else
> +		dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq);
> +
> +	mutex_unlock(&msi->lock);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +	.alloc	= mc_irq_msi_domain_alloc,
> +	.free	= mc_irq_msi_domain_free,
> +};
> +
> +static struct irq_chip mc_msi_irq_chip = {
> +	.name = "Microchip 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 mc_msi_domain_info = {
> +	.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | MSI_FLAG_PCI_MSIX),
> +	.chip = &mc_msi_irq_chip,
> +};
> +
> +static int mc_allocate_msi_domains(struct mc_port *port)
> +{
> +	struct device *dev = port->dev;
> +	struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
> +	struct mc_msi *msi = &port->msi;
> +
> +	mutex_init(&port->msi.lock);
> +
> +	msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors, &msi_domain_ops, port);
> +	if (!msi->dev_domain) {
> +		dev_err(dev, "failed to create IRQ domain\n");
> +		return -ENOMEM;
> +	}
> +
> +	msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info, msi->dev_domain);
> +	if (!msi->msi_domain) {
> +		dev_err(dev, "failed to create MSI domain\n");
> +		irq_domain_remove(msi->dev_domain);
> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mc_handle_intx(struct irq_desc *desc)
> +{
> +	struct mc_port *port = irq_desc_get_handler_data(desc);
> +	struct device *dev = port->dev;
> +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> +	unsigned long status;
> +	u32 bit;
> +	u32 virq;
> +
> +	status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
> +	if (status & PM_MSI_INT_INTX_MASK) {
> +		status &= PM_MSI_INT_INTX_MASK;
> +		status >>= PM_MSI_INT_INTX_SHIFT;
> +		for_each_set_bit(bit, &status, PCI_NUM_INTX) {
> +			virq = irq_find_mapping(port->intx_domain, bit);
> +			if (virq)
> +				generic_handle_irq(virq);
> +			else
> +				dev_err_ratelimited(dev, "bad INTx IRQ %d\n", bit);
> +		}
> +	}
> +}
> +
> +static void mc_ack_intx_irq(struct irq_data *data)
> +{
> +	struct mc_port *port = irq_data_get_irq_chip_data(data);
> +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> +	u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
> +
> +	writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
> +}
> +
> +static void mc_mask_intx_irq(struct irq_data *data)
> +{
> +	struct mc_port *port = irq_data_get_irq_chip_data(data);
> +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> +	unsigned long flags;
> +	u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
> +	u32 val;
> +
> +	raw_spin_lock_irqsave(&port->lock, flags);
> +	val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
> +	val &= ~mask;
> +	writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
> +	raw_spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +static void mc_unmask_intx_irq(struct irq_data *data)
> +{
> +	struct mc_port *port = irq_data_get_irq_chip_data(data);
> +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> +	unsigned long flags;
> +	u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
> +	u32 val;
> +
> +	raw_spin_lock_irqsave(&port->lock, flags);
> +	val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
> +	val |= mask;
> +	writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
> +	raw_spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +static struct irq_chip mc_intx_irq_chip = {
> +	.name = "Microchip PCIe INTx",
> +	.irq_ack = mc_ack_intx_irq,
> +	.irq_mask = mc_mask_intx_irq,
> +	.irq_unmask = mc_unmask_intx_irq,
> +};
> +
> +static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
> +			    irq_hw_number_t hwirq)
> +{
> +	irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq);
> +	irq_set_chip_data(irq, domain->host_data);
> +
> +	return 0;
> +}
> +
> +static const struct irq_domain_ops intx_domain_ops = {
> +	.map = mc_pcie_intx_map,
> +};
> +
> +static inline u32 reg_to_event(u32 reg, struct event_map field)
> +{
> +	u32 val;
> +
> +	val = (reg & field.reg_mask) ? BIT(field.event_bit) : 0;
> +
> +	return val;
> +}
> +
> +static u32 pcie_events(void __iomem *addr)
> +{
> +	u32 reg = readl_relaxed(addr);
> +	u32 val = 0;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(pcie_event_to_event); i++)
> +		val |= reg_to_event(reg, pcie_event_to_event[i]);
> +
> +	return val;
> +}
> +
> +static u32 sec_errors(void __iomem *addr)
> +{
> +	u32 reg = readl_relaxed(addr);
> +	u32 val = 0;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(sec_error_to_event); i++)
> +		val |= reg_to_event(reg, sec_error_to_event[i]);
> +
> +	return val;
> +}
> +
> +static u32 ded_errors(void __iomem *addr)
> +{
> +	u32 reg = readl_relaxed(addr);
> +	u32 val = 0;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(ded_error_to_event); i++)
> +		val |= reg_to_event(reg, ded_error_to_event[i]);
> +
> +	return val;
> +}
> +
> +static u32 local_events(void __iomem *addr)
> +{
> +	u32 reg = readl_relaxed(addr);
> +	u32 val = 0;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(local_status_to_event); i++)
> +		val |= reg_to_event(reg, local_status_to_event[i]);
> +
> +	return val;
> +}
> +
> +static u32 get_events(struct mc_port *port)
> +{
> +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> +	void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
> +	u32 events = 0;
> +
> +	events |= pcie_events(ctrl_base_addr + PCIE_EVENT_INT);
> +	events |= sec_errors(ctrl_base_addr + SEC_ERROR_INT);
> +	events |= ded_errors(ctrl_base_addr + DED_ERROR_INT);
> +	events |= local_events(bridge_base_addr + ISTATUS_LOCAL);
> +
> +	return events;
> +}
> +
> +static irqreturn_t mc_event_handler(int irq, void *dev_id)
> +{
> +	struct mc_port *port = dev_id;
> +	struct device *dev = port->dev;
> +	struct irq_data *data;
> +
> +	data = irq_domain_get_irq_data(port->event_domain, irq);
> +
> +	if (event_cause[data->hwirq].str)
> +		dev_err_ratelimited(dev, "%s\n", event_cause[data->hwirq].str);
> +	else
> +		dev_err_ratelimited(dev, "bad event IRQ %ld\n", data->hwirq);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void mc_handle_event(struct irq_desc *desc)
> +{
> +	struct mc_port *port = irq_desc_get_handler_data(desc);
> +	unsigned long events;
> +	u32 bit;
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +
> +	chained_irq_enter(chip, desc);
> +
> +	events = get_events(port);
> +
> +	for_each_set_bit(bit, &events, NUM_EVENTS)
> +		generic_handle_irq(irq_find_mapping(port->event_domain, bit));
> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static void mc_ack_event_irq(struct irq_data *data)
> +{
> +	struct mc_port *port = irq_data_get_irq_chip_data(data);
> +	u32 event = data->hwirq;
> +	void __iomem *addr;
> +	u32 mask;
> +
> +	addr = port->axi_base_addr + event_descs[event].base + event_descs[event].offset;
> +	mask = event_descs[event].mask;
> +	mask |= event_descs[event].enb_mask;
> +
> +	writel_relaxed(mask, addr);
> +}
> +
> +static void mc_mask_event_irq(struct irq_data *data)
> +{
> +	struct mc_port *port = irq_data_get_irq_chip_data(data);
> +	u32 event = data->hwirq;
> +	void __iomem *addr;
> +	u32 mask;
> +	u32 val;
> +
> +	addr = port->axi_base_addr + event_descs[event].base + event_descs[event].mask_offset;
> +	mask = event_descs[event].mask;
> +	if (event_descs[event].enb_mask) {
> +		mask <<= PCIE_EVENT_INT_ENB_SHIFT;
> +		mask &= PCIE_EVENT_INT_ENB_MASK;
> +	}
> +
> +	if (!event_descs[event].mask_high)
> +		mask = ~mask;
> +
> +	raw_spin_lock(&port->lock);
> +	val = readl_relaxed(addr);
> +	if (event_descs[event].mask_high)
> +		val |= mask;
> +	else
> +		val &= mask;
> +
> +	writel_relaxed(val, addr);
> +	raw_spin_unlock(&port->lock);
> +}
> +
> +static void mc_unmask_event_irq(struct irq_data *data)
> +{
> +	struct mc_port *port = irq_data_get_irq_chip_data(data);
> +	u32 event = data->hwirq;
> +	void __iomem *addr;
> +	u32 mask;
> +	u32 val;
> +
> +	addr = port->axi_base_addr + event_descs[event].base + event_descs[event].mask_offset;
> +	mask = event_descs[event].mask;
> +
> +	if (event_descs[event].enb_mask)
> +		mask <<= PCIE_EVENT_INT_ENB_SHIFT;
> +
> +	if (event_descs[event].mask_high)
> +		mask = ~mask;
> +
> +	if (event_descs[event].enb_mask)
> +		mask &= PCIE_EVENT_INT_ENB_MASK;
> +
> +	raw_spin_lock(&port->lock);
> +	val = readl_relaxed(addr);
> +	if (event_descs[event].mask_high)
> +		val &= mask;
> +	else
> +		val |= mask;
> +	writel_relaxed(val, addr);
> +	raw_spin_unlock(&port->lock);
> +}
> +
> +static struct irq_chip mc_event_irq_chip = {
> +	.name = "Microchip PCIe EVENT",
> +	.irq_ack = mc_ack_event_irq,
> +	.irq_mask = mc_mask_event_irq,
> +	.irq_unmask = mc_unmask_event_irq,
> +};
> +
> +static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq,
> +			     irq_hw_number_t hwirq)
> +{
> +	irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq);
> +	irq_set_chip_data(irq, domain->host_data);
> +
> +	return 0;
> +}
> +
> +static const struct irq_domain_ops event_domain_ops = {
> +	.map = mc_pcie_event_map,
> +};
> +
> +static inline struct clk *mc_pcie_init_clk(struct device *dev, const char *id)
> +{
> +	struct clk *clk;
> +	int ret;
> +
> +	clk = devm_clk_get_optional(dev, id);
> +	if (IS_ERR(clk))
> +		return clk;
> +	if (!clk)
> +		return clk;
> +
> +	ret = clk_prepare_enable(clk);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
> +	devm_add_action_or_reset(dev, (void (*) (void *))clk_disable_unprepare, clk);
> +
> +	return clk;
> +}
> +
> +static int mc_pcie_init_clks(struct device *dev)
> +{
> +	struct clk *fic;
> +	int i;
> +
> +	/*
> +	 * PCIe may be clocked via Fabric Interface
> +	 * using between 1 and 4 clocks. Scan DT for
> +	 * clocks and enable them if present
> +	 */
> +	for (i = 0; i < ARRAY_SIZE(poss_clks); i++) {
> +		fic = mc_pcie_init_clk(dev, poss_clks[i]);
> +		if (IS_ERR(fic))
> +			return PTR_ERR(fic);
> +	}
> +
> +	return 0;
> +}
> +
> +static int mc_pcie_init_irq_domains(struct mc_port *port)
> +{
> +	struct device *dev = port->dev;
> +	struct device_node *node = dev->of_node;
> +	struct device_node *pcie_intc_node;
> +
> +	/* Setup INTx */
> +	pcie_intc_node = of_get_next_child(node, NULL);
> +	if (!pcie_intc_node) {
> +		dev_err(dev, "failed to find PCIe Intc node\n");
> +		return -EINVAL;
> +	}
> +
> +	port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS, &event_domain_ops,
> +						   port);
> +	if (!port->event_domain) {
> +		dev_err(dev, "failed to get event domain\n");
> +		return -ENOMEM;
> +	}
> +
> +	irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS);
> +
> +	port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, &intx_domain_ops,
> +						  port);
> +	if (!port->intx_domain) {
> +		dev_err(dev, "failed to get an INTx IRQ domain\n");
> +		return -ENOMEM;
> +	}
> +
> +	irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);
> +
> +	of_node_put(pcie_intc_node);
> +	raw_spin_lock_init(&port->lock);
> +
> +	return mc_allocate_msi_domains(port);
> +}
> +
> +static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, phys_addr_t axi_addr,
> +				 phys_addr_t pci_addr, size_t size)
> +{
> +	u32 atr_sz = ilog2(size) - 1;
> +	u32 val;
> +
> +	if (index == 0)
> +		val = PCIE_CONFIG_INTERFACE;
> +	else
> +		val = PCIE_TX_RX_INTERFACE;
> +
> +	writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ATR0_AXI4_SLV0_TRSL_PARAM);
> +
> +	val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) | ATR_IMPL_ENABLE;
> +	writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
> +	       ATR0_AXI4_SLV0_SRCADDR_PARAM);
> +
> +	val = upper_32_bits(axi_addr);
> +	writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
> +	       ATR0_AXI4_SLV0_SRC_ADDR);
> +
> +	val = lower_32_bits(pci_addr);
> +	writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
> +	       ATR0_AXI4_SLV0_TRSL_ADDR_LSB);
> +
> +	val = upper_32_bits(pci_addr);
> +	writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
> +	       ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
> +
> +	val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
> +	val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT);
> +	writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
> +	writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
> +}
> +
> +static int mc_pcie_setup_windows(struct platform_device *pdev, struct mc_port *port)
> +{
> +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> +	struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
> +	struct resource_entry *entry;
> +	u64 pci_addr;
> +	u32 index = 1;
> +
> +	resource_list_for_each_entry(entry, &bridge->windows) {
> +		if (resource_type(entry->res) == IORESOURCE_MEM) {
> +			pci_addr = entry->res->start - entry->offset;
> +			mc_pcie_setup_window(bridge_base_addr, index, entry->res->start,
> +					     pci_addr, resource_size(entry->res));
> +			index++;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int mc_platform_init(struct pci_config_window *cfg)
> +{
> +	struct device *dev = cfg->parent;
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct mc_port *port;
> +	void __iomem *bridge_base_addr;
> +	void __iomem *ctrl_base_addr;
> +	int ret;
> +	int irq;
> +	int i, intx_irq, msi_irq, event_irq;
> +	u32 val;
> +	int err;
> +
> +	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> +	if (!port)
> +		return -ENOMEM;
> +	port->dev = dev;
> +
> +	ret = mc_pcie_init_clks(dev);
> +	if (ret) {
> +		dev_err(dev, "failed to get clock resources, error %d\n", ret);
> +		return -ENODEV;
> +	}
> +
> +	port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1);
> +	if (IS_ERR(port->axi_base_addr))
> +		return PTR_ERR(port->axi_base_addr);
> +
> +	bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> +	ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
> +
> +	port->msi.vector_phy = MSI_ADDR;
> +	port->msi.num_vectors = MC_NUM_MSI_IRQS;
> +	ret = mc_pcie_init_irq_domains(port);
> +	if (ret) {
> +		dev_err(dev, "failed creating IRQ domains\n");
> +		return ret;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(dev, "unable to request IRQ%d\n", irq);
> +		return -ENODEV;
> +	}
> +
> +	for (i = 0; i < NUM_EVENTS; i++) {
> +		event_irq = irq_create_mapping(port->event_domain, i);
> +		if (!event_irq) {
> +			dev_err(dev, "failed to map interrupt\n");
> +			return -ENXIO;
> +		}
> +
> +		err = devm_request_irq(dev, event_irq, mc_event_handler,
> +				       0, event_cause[i].sym, port);
> +		if (err) {
> +			dev_err(dev, "failed to request IRQ %d\n", event_irq);
> +			return err;
> +		}
> +	}
> +
> +	intx_irq = irq_create_mapping(port->event_domain, EVENT_LOCAL_PM_MSI_INT_INTX);
> +	if (!intx_irq) {
> +		dev_err(dev, "failed to map INTx interrupt\n");
> +		return -ENXIO;
> +	}
> +
> +	/* Plug the INTx chained handler */
> +	irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port);
> +
> +	msi_irq = irq_create_mapping(port->event_domain, EVENT_LOCAL_PM_MSI_INT_MSI);
> +	if (!msi_irq)
> +		return -ENXIO;
> +
> +	/* Plug the MSI chained handler */
> +	irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port);
> +
> +	/* Plug the main event chained handler */
> +	irq_set_chained_handler_and_data(irq, mc_handle_event, port);
> +
> +	/* Hardware doesn't setup MSI by default */
> +	mc_pcie_enable_msi(port, cfg->win);
> +
> +	val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
> +	val |= PM_MSI_INT_INTX_MASK;
> +	writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
> +
> +	writel_relaxed(val, ctrl_base_addr + ECC_CONTROL);
> +
> +	val = PCIE_EVENT_INT_L2_EXIT_INT | PCIE_EVENT_INT_HOTRST_EXIT_INT |
> +	      PCIE_EVENT_INT_DLUP_EXIT_INT;
> +	writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT);
> +
> +	val = SEC_ERROR_INT_TX_RAM_SEC_ERR_INT | SEC_ERROR_INT_RX_RAM_SEC_ERR_INT |
> +	      SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT | SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT;
> +	writel_relaxed(val, ctrl_base_addr + SEC_ERROR_INT);
> +	writel_relaxed(0, ctrl_base_addr + SEC_ERROR_INT_MASK);
> +	writel_relaxed(0, ctrl_base_addr + SEC_ERROR_CNT);
> +
> +	val = DED_ERROR_INT_TX_RAM_DED_ERR_INT | DED_ERROR_INT_RX_RAM_DED_ERR_INT |
> +	      DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT | DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT;
> +	writel_relaxed(val, ctrl_base_addr + DED_ERROR_INT);
> +	writel_relaxed(0, ctrl_base_addr + DED_ERROR_INT_MASK);
> +	writel_relaxed(0, ctrl_base_addr + DED_ERROR_CNT);
> +
> +	writel_relaxed(0, bridge_base_addr + IMASK_HOST);
> +	writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
> +
> +	/* Configure Address Translation Table 0 for PCIe config space */
> +	mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start & 0xffffffff, cfg->res.start,
> +			     resource_size(&cfg->res));
> +
> +	return mc_pcie_setup_windows(pdev, port);
> +}
> +
> +static const struct pci_ecam_ops mc_ecam_ops = {
> +	.init = mc_platform_init,
> +	.pci_ops = {
> +		.map_bus = pci_ecam_map_bus,
> +		.read = pci_generic_config_read,
> +		.write = pci_generic_config_write,
> +	}
> +};
> +
> +static const struct of_device_id mc_pcie_of_match[] = {
> +	{
> +		.compatible = "microchip,pcie-host-1.0",
> +		.data = &mc_ecam_ops,
> +	},
> +	{},
> +};
> +
> +MODULE_DEVICE_TABLE(of, mc_pcie_of_match)
> +
> +static struct platform_driver mc_pcie_driver = {
> +	.probe = pci_host_common_probe,
> +	.driver = {
> +		.name = "microchip-pcie",
> +		.of_match_table = mc_pcie_of_match,
> +		.suppress_bind_attrs = true,
> +	},
> +};
> +
> +builtin_platform_driver(mc_pcie_driver);
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Microchip PCIe host controller driver");
> +MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
> -- 
> 2.25.1
> 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller
       [not found]     ` <MN2PR11MB42691AE1B54DEAB5C1BAA11D96B59@MN2PR11MB4269.namprd11.prod.outlook.com>
@ 2021-02-04 10:20       ` Lorenzo Pieralisi
  0 siblings, 0 replies; 15+ messages in thread
From: Lorenzo Pieralisi @ 2021-02-04 10:20 UTC (permalink / raw)
  To: Daire.McNamara
  Cc: bhelgaas, robh, linux-pci, robh+dt, devicetree,
	david.abdurachmanov, Cyril.Jean

On Tue, Feb 02, 2021 at 09:06:31AM +0000, Daire.McNamara@microchip.com wrote:
> 
> 
> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
> From: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> Sent: Monday 1 February 2021 19:05
> To: Daire McNamara - X61553 <Daire.McNamara@microchip.com>
> Cc: bhelgaas@google.com <bhelgaas@google.com>; robh@kernel.org
> <robh@kernel.org>; linux-pci@vger.kernel.org <linux-pci@vger.kernel.org>;
> robh+dt@kernel.org <robh+dt@kernel.org>; devicetree@vger.kernel.org
> <devicetree@vger.kernel.org>; david.abdurachmanov@gmail.com
> <david.abdurachmanov@gmail.com>; Cyril Jean - M31571 <Cyril.Jean@microchip.com>
> Subject: Re: [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe
> controller
>  
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the
> content is safe
> 
> On Mon, Jan 25, 2021 at 04:29:33PM +0000, daire.mcnamara@microchip.com wrote:
> > From: Daire McNamara <daire.mcnamara@microchip.com>
> >
> > Add support for the Microchip PolarFire PCIe controller when
> > configured in host (Root Complex) mode.
> >
> > Signed-off-by: Daire McNamara <daire.mcnamara@microchip.com>
> > Reviewed-by: Rob Herring <robh@kernel.org>
> > ---
> >  drivers/pci/controller/Kconfig               |   10 +
> >  drivers/pci/controller/Makefile              |    1 +
> >  drivers/pci/controller/pcie-microchip-host.c | 1114 ++++++++++++++++++
> >  3 files changed, 1125 insertions(+)
> >  create mode 100644 drivers/pci/controller/pcie-microchip-host.c
> >
> > diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
> > index 64e2f5e379aa..bca2f8949510 100644
> > --- a/drivers/pci/controller/Kconfig
> > +++ b/drivers/pci/controller/Kconfig
> > @@ -298,6 +298,16 @@ config PCI_LOONGSON
> >         Say Y here if you want to enable PCI controller support on
> >         Loongson systems.
> >
> > +config PCIE_MICROCHIP_HOST
> > +     bool "Microchip AXI PCIe host bridge support"
> > +     depends on PCI_MSI && OF
> > +     select PCI_MSI_IRQ_DOMAIN
> > +     select GENERIC_MSI_IRQ_DOMAIN
> > +     select PCI_HOST_COMMON
> > +     help
> > +       Say Y here if you want kernel to support the Microchip AXI PCIe
> > +       Host Bridge driver.
> > +
> >  config PCIE_HISI_ERR
> >       depends on ACPI_APEI_GHES && (ARM64 || COMPILE_TEST)
> >       bool "HiSilicon HIP PCIe controller error handling driver"
> > diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/
> Makefile
> > index 04c6edc285c5..b85fcd574ff6 100644
> > --- a/drivers/pci/controller/Makefile
> > +++ b/drivers/pci/controller/Makefile
> > @@ -28,6 +28,7 @@ obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-rockchip-ep.o
> >  obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o
> >  obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o
> >  obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
> > +obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o
> >  obj-$(CONFIG_VMD) += vmd.o
> >  obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
> >  obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o
> > diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/
> controller/pcie-microchip-host.c
> > new file mode 100644
> > index 000000000000..0ef2beab185b
> > --- /dev/null
> > +++ b/drivers/pci/controller/pcie-microchip-host.c
> > @@ -0,0 +1,1114 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Microchip AXI PCIe Bridge host controller driver
> > + *
> > + * Copyright (c) 2018 - 2020 Microchip Corporation. All rights reserved.
> 2021 - I will update it for you if that's OK.
> 
> Yes, please, that would be great.
> 
> > + * Author: Daire McNamara <daire.mcnamara@microchip.com>
> > + *
> > + * Based on:
> > + *   pcie-rcar.c
> > + *   pcie-xilinx.c
> > + *   pcie-altera.c
> 
> Can I remove these references please ? I said that before, let me know
> please, do not respost another version.
> 
> Yes, please.  Sorry I must have missed the question before.

Replies must be quoted and you must not send HTML format on ML,
(this was rejected by the ML and all the related tools) and that's
not the first time I mentioned that.

You must NOT do it and that's the last time I am saying that.

Please read:

https://kernelnewbies.org/PatchCulture

It is your maintainer responsibility to follow the ML and act
responsibly following the email etiquette, I don't have this HW and you
are the one that from now onwards is in charge of maintaining this code
which implies reviewing patches and actively following mailing lists.

Having said that, let's proceed with upstreaming the code, keep this
in mind please.

Thanks,
Lorenzo

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v21 0/4] PCI: microchip: Add host driver for Microchip PCIe controller
  2021-01-25 16:29 [PATCH v21 0/4] PCI: microchip: Add host driver for Microchip PCIe controller daire.mcnamara
                   ` (3 preceding siblings ...)
  2021-01-25 16:29 ` [PATCH v21 4/4] MAINTAINERS: Add Daire McNamara as maintainer for the Microchip PCIe driver daire.mcnamara
@ 2021-02-04 11:42 ` Lorenzo Pieralisi
  4 siblings, 0 replies; 15+ messages in thread
From: Lorenzo Pieralisi @ 2021-02-04 11:42 UTC (permalink / raw)
  To: robh, linux-pci, robh+dt, bhelgaas, daire.mcnamara, devicetree
  Cc: Lorenzo Pieralisi, cyril.jean, david.abdurachmanov

On Mon, 25 Jan 2021 16:29:30 +0000, daire.mcnamara@microchip.com wrote:
> This patchset adds support for the Microchip PCIe PolarFire PCIe
> controller when configured in host (Root Complex) mode.
> 
> Updates since v20:
> * Changed module license from GPLv2 to GPL
> * Removed bus-shift for mc_ecam_ops
> 
> [...]

Applied to pci/microchip, tentatively for v5.12, thanks !

[1/4] PCI: Call platform_set_drvdata earlier in devm_pci_alloc_host_bridge
      https://git.kernel.org/lpieralisi/pci/c/5aa5282680
[2/4] dt-bindings: PCI: microchip: Add Microchip PolarFire host binding
      https://git.kernel.org/lpieralisi/pci/c/1d8b748536
[3/4] PCI: microchip: Add host driver for Microchip PCIe controller
      https://git.kernel.org/lpieralisi/pci/c/8a09a17d15
[4/4] MAINTAINERS: Add Daire McNamara as maintainer for the Microchip PCIe driver
      https://git.kernel.org/lpieralisi/pci/c/e9ddffa6a4

Thanks,
Lorenzo

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller
  2021-01-25 16:29 ` [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller daire.mcnamara
  2021-02-01 19:05   ` Lorenzo Pieralisi
@ 2021-02-10 13:07   ` Geert Uytterhoeven
  2021-02-11 13:03     ` Ben Dooks
  2022-01-27 20:20   ` Bjorn Helgaas
  2 siblings, 1 reply; 15+ messages in thread
From: Geert Uytterhoeven @ 2021-02-10 13:07 UTC (permalink / raw)
  To: daire.mcnamara
  Cc: Lorenzo Pieralisi, Bjorn Helgaas, Rob Herring, linux-pci,
	Rob Herring,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	david.abdurachmanov, cyril.jean

Hi Daire,

On Mon, Jan 25, 2021 at 5:33 PM <daire.mcnamara@microchip.com> wrote:
> From: Daire McNamara <daire.mcnamara@microchip.com>
>
> Add support for the Microchip PolarFire PCIe controller when
> configured in host (Root Complex) mode.
>
> Signed-off-by: Daire McNamara <daire.mcnamara@microchip.com>
> Reviewed-by: Rob Herring <robh@kernel.org>

Thanks for your patch!

> --- a/drivers/pci/controller/Kconfig
> +++ b/drivers/pci/controller/Kconfig
> @@ -298,6 +298,16 @@ config PCI_LOONGSON
>           Say Y here if you want to enable PCI controller support on
>           Loongson systems.
>
> +config PCIE_MICROCHIP_HOST
> +       bool "Microchip AXI PCIe host bridge support"
> +       depends on PCI_MSI && OF
> +       select PCI_MSI_IRQ_DOMAIN
> +       select GENERIC_MSI_IRQ_DOMAIN
> +       select PCI_HOST_COMMON
> +       help
> +         Say Y here if you want kernel to support the Microchip AXI PCIe
> +         Host Bridge driver.

Is this PCIe host bridge accessible only from the PolarFire RISC-V
CPU cores, or also from softcores implemented in the PolarFire FPGA?

In case of the former, we want to add a

    depends on CONFIG_SOC_MICROCHIP_POLARFIRE || COMPILE_TEST

conditional.

> +
>  config PCIE_HISI_ERR
>         depends on ACPI_APEI_GHES && (ARM64 || COMPILE_TEST)
>         bool "HiSilicon HIP PCIe controller error handling driver"

Gr{oetje,eeting}s,

                        Geert


--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller
  2021-02-10 13:07   ` Geert Uytterhoeven
@ 2021-02-11 13:03     ` Ben Dooks
  2021-02-11 13:07       ` Geert Uytterhoeven
  0 siblings, 1 reply; 15+ messages in thread
From: Ben Dooks @ 2021-02-11 13:03 UTC (permalink / raw)
  To: Geert Uytterhoeven, daire.mcnamara
  Cc: Lorenzo Pieralisi, Bjorn Helgaas, Rob Herring, linux-pci,
	Rob Herring,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	david.abdurachmanov, cyril.jean

On 10/02/2021 13:07, Geert Uytterhoeven wrote:
> Hi Daire,
> 
> On Mon, Jan 25, 2021 at 5:33 PM <daire.mcnamara@microchip.com> wrote:
>> From: Daire McNamara <daire.mcnamara@microchip.com>
>>
>> Add support for the Microchip PolarFire PCIe controller when
>> configured in host (Root Complex) mode.
>>
>> Signed-off-by: Daire McNamara <daire.mcnamara@microchip.com>
>> Reviewed-by: Rob Herring <robh@kernel.org>
> 
> Thanks for your patch!
> 
>> --- a/drivers/pci/controller/Kconfig
>> +++ b/drivers/pci/controller/Kconfig
>> @@ -298,6 +298,16 @@ config PCI_LOONGSON
>>            Say Y here if you want to enable PCI controller support on
>>            Loongson systems.
>>
>> +config PCIE_MICROCHIP_HOST
>> +       bool "Microchip AXI PCIe host bridge support"
>> +       depends on PCI_MSI && OF
>> +       select PCI_MSI_IRQ_DOMAIN
>> +       select GENERIC_MSI_IRQ_DOMAIN
>> +       select PCI_HOST_COMMON
>> +       help
>> +         Say Y here if you want kernel to support the Microchip AXI PCIe
>> +         Host Bridge driver.
> 
> Is this PCIe host bridge accessible only from the PolarFire RISC-V
> CPU cores, or also from softcores implemented in the PolarFire FPGA?
> 
> In case of the former, we want to add a
> 
>      depends on CONFIG_SOC_MICROCHIP_POLARFIRE || COMPILE_TEST


I'd say having it on COMPILE_TEST if there's no polarfire includes
would be useful to allow compile testing of the driver by the build
robots.


-- 
Ben Dooks				http://www.codethink.co.uk/
Senior Engineer				Codethink - Providing Genius

https://www.codethink.co.uk/privacy.html

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller
  2021-02-11 13:03     ` Ben Dooks
@ 2021-02-11 13:07       ` Geert Uytterhoeven
  2021-02-16  9:54         ` Daire.McNamara
  0 siblings, 1 reply; 15+ messages in thread
From: Geert Uytterhoeven @ 2021-02-11 13:07 UTC (permalink / raw)
  To: Ben Dooks
  Cc: daire.mcnamara, Lorenzo Pieralisi, Bjorn Helgaas, Rob Herring,
	linux-pci, Rob Herring,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	david.abdurachmanov, cyril.jean

Hi Ben,

On Thu, Feb 11, 2021 at 2:03 PM Ben Dooks <ben.dooks@codethink.co.uk> wrote:
> On 10/02/2021 13:07, Geert Uytterhoeven wrote:
> > On Mon, Jan 25, 2021 at 5:33 PM <daire.mcnamara@microchip.com> wrote:
> >> From: Daire McNamara <daire.mcnamara@microchip.com>
> >>
> >> Add support for the Microchip PolarFire PCIe controller when
> >> configured in host (Root Complex) mode.
> >>
> >> Signed-off-by: Daire McNamara <daire.mcnamara@microchip.com>
> >> Reviewed-by: Rob Herring <robh@kernel.org>
> >
> > Thanks for your patch!
> >
> >> --- a/drivers/pci/controller/Kconfig
> >> +++ b/drivers/pci/controller/Kconfig
> >> @@ -298,6 +298,16 @@ config PCI_LOONGSON
> >>            Say Y here if you want to enable PCI controller support on
> >>            Loongson systems.
> >>
> >> +config PCIE_MICROCHIP_HOST
> >> +       bool "Microchip AXI PCIe host bridge support"
> >> +       depends on PCI_MSI && OF
> >> +       select PCI_MSI_IRQ_DOMAIN
> >> +       select GENERIC_MSI_IRQ_DOMAIN
> >> +       select PCI_HOST_COMMON
> >> +       help
> >> +         Say Y here if you want kernel to support the Microchip AXI PCIe
> >> +         Host Bridge driver.
> >
> > Is this PCIe host bridge accessible only from the PolarFire RISC-V
> > CPU cores, or also from softcores implemented in the PolarFire FPGA?
> >
> > In case of the former, we want to add a
> >
> >      depends on CONFIG_SOC_MICROCHIP_POLARFIRE || COMPILE_TEST
>
>
> I'd say having it on COMPILE_TEST if there's no polarfire includes
> would be useful to allow compile testing of the driver by the build
> robots.

As currently there is no platform dependency at all, it will be
compile-tested by allmodconfig on every config that has PCI_MSI && OF.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller
  2021-02-11 13:07       ` Geert Uytterhoeven
@ 2021-02-16  9:54         ` Daire.McNamara
  0 siblings, 0 replies; 15+ messages in thread
From: Daire.McNamara @ 2021-02-16  9:54 UTC (permalink / raw)
  To: geert, ben.dooks
  Cc: lorenzo.pieralisi, bhelgaas, robh, linux-pci, robh+dt,
	devicetree, david.abdurachmanov, Cyril.Jean



On 11/02/2021 13:07, Geert Uytterhoeven wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> Hi Ben,
> 
> On Thu, Feb 11, 2021 at 2:03 PM Ben Dooks <ben.dooks@codethink.co.uk> wrote:
>> On 10/02/2021 13:07, Geert Uytterhoeven wrote:
>>> On Mon, Jan 25, 2021 at 5:33 PM <daire.mcnamara@microchip.com> wrote:
>>>> From: Daire McNamara <daire.mcnamara@microchip.com>
>>>>
>>>> Add support for the Microchip PolarFire PCIe controller when
>>>> configured in host (Root Complex) mode.
>>>>
>>>> Signed-off-by: Daire McNamara <daire.mcnamara@microchip.com>
>>>> Reviewed-by: Rob Herring <robh@kernel.org>
>>>
>>> Thanks for your patch!
>>>
>>>> --- a/drivers/pci/controller/Kconfig
>>>> +++ b/drivers/pci/controller/Kconfig
>>>> @@ -298,6 +298,16 @@ config PCI_LOONGSON
>>>>            Say Y here if you want to enable PCI controller support on
>>>>            Loongson systems.
>>>>
>>>> +config PCIE_MICROCHIP_HOST
>>>> +       bool "Microchip AXI PCIe host bridge support"
>>>> +       depends on PCI_MSI && OF
>>>> +       select PCI_MSI_IRQ_DOMAIN
>>>> +       select GENERIC_MSI_IRQ_DOMAIN
>>>> +       select PCI_HOST_COMMON
>>>> +       help
>>>> +         Say Y here if you want kernel to support the Microchip AXI PCIe
>>>> +         Host Bridge driver.
>>>
>>> Is this PCIe host bridge accessible only from the PolarFire RISC-V
>>> CPU cores, or also from softcores implemented in the PolarFire FPGA?
>>>
>>> In case of the former, we want to add a
>>>
>>>      depends on CONFIG_SOC_MICROCHIP_POLARFIRE || COMPILE_TEST
>>
>>
>> I'd say having it on COMPILE_TEST if there's no polarfire includes
>> would be useful to allow compile testing of the driver by the build
>> robots.
> 
> As currently there is no platform dependency at all, it will be
> compile-tested by allmodconfig on every config that has PCI_MSI && OF.
> 
> Gr{oetje,eeting}s,
> 
>                         Geert
This PCIe host bridge is accessible from softcores implemented in the PolarFire SoC FPGA.  But, I still thing its reasonable to add a 'depends on CONFIG_SOC_MICROCHIP_POLARFIRE || COMPILE_TEST' line.
> 
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
> 
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
>                                 -- Linus Torvalds
> 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller
  2021-01-25 16:29 ` [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller daire.mcnamara
  2021-02-01 19:05   ` Lorenzo Pieralisi
  2021-02-10 13:07   ` Geert Uytterhoeven
@ 2022-01-27 20:20   ` Bjorn Helgaas
  2022-01-28  9:55     ` Marc Zyngier
  2 siblings, 1 reply; 15+ messages in thread
From: Bjorn Helgaas @ 2022-01-27 20:20 UTC (permalink / raw)
  To: daire.mcnamara
  Cc: lorenzo.pieralisi, bhelgaas, robh, linux-pci, robh+dt,
	devicetree, david.abdurachmanov, cyril.jean, Marc Zyngier

[+cc Marc]

On Mon, Jan 25, 2021 at 04:29:33PM +0000, daire.mcnamara@microchip.com wrote:
> From: Daire McNamara <daire.mcnamara@microchip.com>
> 
> Add support for the Microchip PolarFire PCIe controller when
> configured in host (Root Complex) mode.

> +static void mc_handle_msi(struct irq_desc *desc)
> +{
> +	struct mc_port *port = irq_desc_get_handler_data(desc);
> +	struct device *dev = port->dev;
> +	struct mc_msi *msi = &port->msi;
> +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> +	unsigned long status;
> +	u32 bit;
> +	u32 virq;
> +
> +	status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
> +	if (status & PM_MSI_INT_MSI_MASK) {
> +		status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
> +		for_each_set_bit(bit, &status, msi->num_vectors) {
> +			virq = irq_find_mapping(msi->dev_domain, bit);
> +			if (virq)
> +				generic_handle_irq(virq);
> +			else
> +				dev_err_ratelimited(dev, "bad MSI IRQ %d\n", bit);
> +		}
> +	}
> +}
> +
> +static void mc_msi_bottom_irq_ack(struct irq_data *data)
> +{
> +	struct mc_port *port = irq_data_get_irq_chip_data(data);
> +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> +	u32 bitpos = data->hwirq;
> +	unsigned long status;
> +
> +	writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
> +	status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
> +	if (!status)
> +		writel_relaxed(BIT(PM_MSI_INT_MSI_SHIFT), bridge_base_addr + ISTATUS_LOCAL);

This looks like it might be racy.  What happens if we read 0 from
ISTATUS_MSI, but a new MSI is latched before we write ISTATUS_LOCAL?

Will mc_handle_msi() be called?  If so, will PM_MSI_INT_MSI_MASK be
set in the value it reads from ISTATUS_LOCAL?

Current code at:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pci/controller/pcie-microchip-host.c?id=v5.17-rc1#n406

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller
  2022-01-27 20:20   ` Bjorn Helgaas
@ 2022-01-28  9:55     ` Marc Zyngier
  2022-01-28 13:16       ` Bjorn Helgaas
  0 siblings, 1 reply; 15+ messages in thread
From: Marc Zyngier @ 2022-01-28  9:55 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: daire.mcnamara, lorenzo.pieralisi, bhelgaas, robh, linux-pci,
	robh+dt, devicetree, david.abdurachmanov, cyril.jean

On Thu, 27 Jan 2022 20:20:00 +0000,
Bjorn Helgaas <helgaas@kernel.org> wrote:
> 
> [+cc Marc]
> 
> On Mon, Jan 25, 2021 at 04:29:33PM +0000, daire.mcnamara@microchip.com wrote:
> > From: Daire McNamara <daire.mcnamara@microchip.com>
> > 
> > Add support for the Microchip PolarFire PCIe controller when
> > configured in host (Root Complex) mode.
> 
> > +static void mc_handle_msi(struct irq_desc *desc)
> > +{
> > +	struct mc_port *port = irq_desc_get_handler_data(desc);
> > +	struct device *dev = port->dev;
> > +	struct mc_msi *msi = &port->msi;
> > +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> > +	unsigned long status;
> > +	u32 bit;
> > +	u32 virq;
> > +
> > +	status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
> > +	if (status & PM_MSI_INT_MSI_MASK) {
> > +		status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
> > +		for_each_set_bit(bit, &status, msi->num_vectors) {
> > +			virq = irq_find_mapping(msi->dev_domain, bit);
> > +			if (virq)
> > +				generic_handle_irq(virq);

Wrong construct. Please use generic_handle_domain_irq().

> > +			else
> > +				dev_err_ratelimited(dev, "bad MSI IRQ %d\n", bit);
> > +		}
> > +	}
> > +}
> > +
> > +static void mc_msi_bottom_irq_ack(struct irq_data *data)
> > +{
> > +	struct mc_port *port = irq_data_get_irq_chip_data(data);
> > +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> > +	u32 bitpos = data->hwirq;
> > +	unsigned long status;
> > +
> > +	writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
> > +	status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
> > +	if (!status)
> > +		writel_relaxed(BIT(PM_MSI_INT_MSI_SHIFT), bridge_base_addr + ISTATUS_LOCAL);
> 
> This looks like it might be racy.  What happens if we read 0 from
> ISTATUS_MSI, but a new MSI is latched before we write ISTATUS_LOCAL?

I agree, this looks really odd. The irq_ack callback is per interrupt,
while this seems to deal with some global state. This cannot be right.

	M.

-- 
Without deviation from the norm, progress is not possible.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller
  2022-01-28  9:55     ` Marc Zyngier
@ 2022-01-28 13:16       ` Bjorn Helgaas
  0 siblings, 0 replies; 15+ messages in thread
From: Bjorn Helgaas @ 2022-01-28 13:16 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: daire.mcnamara, lorenzo.pieralisi, bhelgaas, robh, linux-pci,
	robh+dt, devicetree, david.abdurachmanov, cyril.jean

On Fri, Jan 28, 2022 at 09:55:04AM +0000, Marc Zyngier wrote:
> On Thu, 27 Jan 2022 20:20:00 +0000,
> Bjorn Helgaas <helgaas@kernel.org> wrote:
> > On Mon, Jan 25, 2021 at 04:29:33PM +0000, daire.mcnamara@microchip.com wrote:
> > > From: Daire McNamara <daire.mcnamara@microchip.com>
> > > 
> > > Add support for the Microchip PolarFire PCIe controller when
> > > configured in host (Root Complex) mode.
> > 
> > > +static void mc_handle_msi(struct irq_desc *desc)
> > > +{
> > > +	struct mc_port *port = irq_desc_get_handler_data(desc);
> > > +	struct device *dev = port->dev;
> > > +	struct mc_msi *msi = &port->msi;
> > > +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> > > +	unsigned long status;
> > > +	u32 bit;
> > > +	u32 virq;
> > > +
> > > +	status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
> > > +	if (status & PM_MSI_INT_MSI_MASK) {
> > > +		status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
> > > +		for_each_set_bit(bit, &status, msi->num_vectors) {
> > > +			virq = irq_find_mapping(msi->dev_domain, bit);
> > > +			if (virq)
> > > +				generic_handle_irq(virq);
> 
> Wrong construct. Please use generic_handle_domain_irq().

I responded to this old posting because it was the most recent one
that included the code below.   This irq_find_mapping() bit has since
been updated:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pci/controller/pcie-microchip-host.c?id=v5.17-rc1#n406

> > > +			else
> > > +				dev_err_ratelimited(dev, "bad MSI IRQ %d\n", bit);
> > > +		}
> > > +	}
> > > +}
> > > +
> > > +static void mc_msi_bottom_irq_ack(struct irq_data *data)
> > > +{
> > > +	struct mc_port *port = irq_data_get_irq_chip_data(data);
> > > +	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
> > > +	u32 bitpos = data->hwirq;
> > > +	unsigned long status;
> > > +
> > > +	writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
> > > +	status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
> > > +	if (!status)
> > > +		writel_relaxed(BIT(PM_MSI_INT_MSI_SHIFT), bridge_base_addr + ISTATUS_LOCAL);
> > 
> > This looks like it might be racy.  What happens if we read 0 from
> > ISTATUS_MSI, but a new MSI is latched before we write ISTATUS_LOCAL?
> 
> I agree, this looks really odd. The irq_ack callback is per interrupt,
> while this seems to deal with some global state. This cannot be right.
> 
> 	M.
> 
> -- 
> Without deviation from the norm, progress is not possible.

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2022-01-28 13:16 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-25 16:29 [PATCH v21 0/4] PCI: microchip: Add host driver for Microchip PCIe controller daire.mcnamara
2021-01-25 16:29 ` [PATCH v21 1/4] PCI: Call platform_set_drvdata earlier in devm_pci_alloc_host_bridge daire.mcnamara
2021-01-25 16:29 ` [PATCH v21 2/4] dt-bindings: PCI: microchip: Add Microchip PolarFire host binding daire.mcnamara
2021-01-25 16:29 ` [PATCH v21 3/4] PCI: microchip: Add host driver for Microchip PCIe controller daire.mcnamara
2021-02-01 19:05   ` Lorenzo Pieralisi
     [not found]     ` <MN2PR11MB42691AE1B54DEAB5C1BAA11D96B59@MN2PR11MB4269.namprd11.prod.outlook.com>
2021-02-04 10:20       ` Lorenzo Pieralisi
2021-02-10 13:07   ` Geert Uytterhoeven
2021-02-11 13:03     ` Ben Dooks
2021-02-11 13:07       ` Geert Uytterhoeven
2021-02-16  9:54         ` Daire.McNamara
2022-01-27 20:20   ` Bjorn Helgaas
2022-01-28  9:55     ` Marc Zyngier
2022-01-28 13:16       ` Bjorn Helgaas
2021-01-25 16:29 ` [PATCH v21 4/4] MAINTAINERS: Add Daire McNamara as maintainer for the Microchip PCIe driver daire.mcnamara
2021-02-04 11:42 ` [PATCH v21 0/4] PCI: microchip: Add host driver for Microchip PCIe controller Lorenzo Pieralisi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).