diff options
| author | Bjorn Helgaas <bhelgaas@google.com> | 2025-03-27 13:14:48 -0500 | 
|---|---|---|
| committer | Bjorn Helgaas <bhelgaas@google.com> | 2025-03-27 13:14:48 -0500 | 
| commit | c51638f15ef57908815b5c079bd4fc1ed8a13c00 (patch) | |
| tree | 1e6f7f4ac5720352dbe37b4889fbfc870ceff529 | |
| parent | 17dbd3f621a21dee6ee91953826d25e31a828b98 (diff) | |
| parent | 5f3de23d858edf5df89c397678ba492b96646df4 (diff) | |
Merge branch 'pci/controller/amd-mdb'
- Add DT binding and driver for AMD MDB (Multimedia DMA Bridge)
  (Thippeswamy Havalige)
* pci/controller/amd-mdb:
  PCI: amd-mdb: Add AMD MDB Root Port driver
  dt-bindings: PCI: amd-mdb: Add AMD Versal2 MDB PCIe Root Port Bridge
  dt-bindings: PCI: dwc: Add AMD Versal2 MDB SLCR support
| -rw-r--r-- | Documentation/devicetree/bindings/pci/amd,versal2-mdb-host.yaml | 121 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml | 2 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/Kconfig | 11 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/Makefile | 1 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-amd-mdb.c | 476 | 
5 files changed, 611 insertions, 0 deletions
| diff --git a/Documentation/devicetree/bindings/pci/amd,versal2-mdb-host.yaml b/Documentation/devicetree/bindings/pci/amd,versal2-mdb-host.yaml new file mode 100644 index 000000000000..43dc2585c237 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/amd,versal2-mdb-host.yaml @@ -0,0 +1,121 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/amd,versal2-mdb-host.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AMD Versal2 MDB(Multimedia DMA Bridge) Host Controller + +maintainers: +  - Thippeswamy Havalige <thippeswamy.havalige@amd.com> + +allOf: +  - $ref: /schemas/pci/pci-host-bridge.yaml# +  - $ref: /schemas/pci/snps,dw-pcie.yaml# + +properties: +  compatible: +    const: amd,versal2-mdb-host + +  reg: +    items: +      - description: MDB System Level Control and Status Register (SLCR) Base +      - description: configuration region +      - description: data bus interface +      - description: address translation unit register + +  reg-names: +    items: +      - const: slcr +      - const: config +      - const: dbi +      - const: atu + +  ranges: +    maxItems: 2 + +  msi-map: +    maxItems: 1 + +  interrupts: +    maxItems: 1 + +  interrupt-map-mask: +    items: +      - const: 0 +      - const: 0 +      - const: 0 +      - const: 7 + +  interrupt-map: +    maxItems: 4 + +  "#interrupt-cells": +    const: 1 + +  interrupt-controller: +    description: identifies the node as an interrupt controller +    type: object +    additionalProperties: false +    properties: +      interrupt-controller: true + +      "#address-cells": +        const: 0 + +      "#interrupt-cells": +        const: 1 + +    required: +      - interrupt-controller +      - "#address-cells" +      - "#interrupt-cells" + +required: +  - reg +  - reg-names +  - interrupts +  - interrupt-map +  - interrupt-map-mask +  - msi-map +  - "#interrupt-cells" +  - interrupt-controller + +unevaluatedProperties: false + +examples: +  - | +    #include <dt-bindings/interrupt-controller/arm-gic.h> +    #include <dt-bindings/interrupt-controller/irq.h> + +    soc { +        #address-cells = <2>; +        #size-cells = <2>; +        pcie@ed931000 { +            compatible = "amd,versal2-mdb-host"; +            reg = <0x0 0xed931000 0x0 0x2000>, +                  <0x1000 0x100000 0x0 0xff00000>, +                  <0x1000 0x0 0x0 0x1000>, +                  <0x0 0xed860000 0x0 0x2000>; +            reg-names = "slcr", "config", "dbi", "atu"; +            ranges = <0x2000000 0x00 0xa0000000 0x00 0xa0000000 0x00 0x10000000>, +                     <0x43000000 0x1100 0x00 0x1100 0x00 0x00 0x1000000>; +            interrupts = <GIC_SPI 165 IRQ_TYPE_LEVEL_HIGH>; +            interrupt-parent = <&gic>; +            interrupt-map-mask = <0 0 0 7>; +            interrupt-map = <0 0 0 1 &pcie_intc_0 0>, +                            <0 0 0 2 &pcie_intc_0 1>, +                            <0 0 0 3 &pcie_intc_0 2>, +                            <0 0 0 4 &pcie_intc_0 3>; +            msi-map = <0x0 &gic_its 0x00 0x10000>; +            #address-cells = <3>; +            #size-cells = <2>; +            #interrupt-cells = <1>; +            device_type = "pci"; +            pcie_intc_0: interrupt-controller { +                #address-cells = <0>; +                #interrupt-cells = <1>; +                interrupt-controller; +           }; +        }; +    }; diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml index 205326fb2d75..1117a86fb6f7 100644 --- a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml @@ -113,6 +113,8 @@ properties:                enum: [ smu, mpu ]              - description: Tegra234 aperture                enum: [ ecam ] +            - description: AMD MDB PCIe SLCR region +              const: slcr      allOf:        - contains:            const: dbi diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index b6d6778b0698..8803fb8767a5 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -27,6 +27,17 @@ config PCIE_AL  	  required only for DT-based platforms. ACPI platforms with the  	  Annapurna Labs PCIe controller don't need to enable this. +config PCIE_AMD_MDB +	bool "AMD MDB Versal2 PCIe controller" +	depends on OF && (ARM64 || COMPILE_TEST) +	depends on PCI_MSI +	select PCIE_DW_HOST +	help +	  Say Y here if you want to enable PCIe controller support on AMD +	  Versal2 SoCs. The AMD MDB Versal2 PCIe controller is based on +	  DesignWare IP and therefore the driver re-uses the DesignWare +	  core functions to implement the driver. +  config PCI_MESON  	tristate "Amlogic Meson PCIe controller"  	default m if ARCH_MESON diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index a8308d9ea986..ae27eda6ec5e 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o  obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o  obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o  obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o +obj-$(CONFIG_PCIE_AMD_MDB) += pcie-amd-mdb.o  obj-$(CONFIG_PCIE_BT1) += pcie-bt1.o  obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o  obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o diff --git a/drivers/pci/controller/dwc/pcie-amd-mdb.c b/drivers/pci/controller/dwc/pcie-amd-mdb.c new file mode 100644 index 000000000000..4eb2a4e8189d --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-amd-mdb.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for AMD MDB PCIe Bridge + * + * Copyright (C) 2024-2025, Advanced Micro Devices, Inc. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/of_device.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/resource.h> +#include <linux/types.h> + +#include "pcie-designware.h" + +#define AMD_MDB_TLP_IR_STATUS_MISC		0x4C0 +#define AMD_MDB_TLP_IR_MASK_MISC		0x4C4 +#define AMD_MDB_TLP_IR_ENABLE_MISC		0x4C8 +#define AMD_MDB_TLP_IR_DISABLE_MISC		0x4CC + +#define AMD_MDB_TLP_PCIE_INTX_MASK	GENMASK(23, 16) + +#define AMD_MDB_PCIE_INTR_INTX_ASSERT(x)	BIT((x) * 2) + +/* Interrupt registers definitions. */ +#define AMD_MDB_PCIE_INTR_CMPL_TIMEOUT		15 +#define AMD_MDB_PCIE_INTR_INTX			16 +#define AMD_MDB_PCIE_INTR_PM_PME_RCVD		24 +#define AMD_MDB_PCIE_INTR_PME_TO_ACK_RCVD	25 +#define AMD_MDB_PCIE_INTR_MISC_CORRECTABLE	26 +#define AMD_MDB_PCIE_INTR_NONFATAL		27 +#define AMD_MDB_PCIE_INTR_FATAL			28 + +#define IMR(x) BIT(AMD_MDB_PCIE_INTR_ ##x) +#define AMD_MDB_PCIE_IMR_ALL_MASK			\ +	(						\ +		IMR(CMPL_TIMEOUT)	|		\ +		IMR(PM_PME_RCVD)	|		\ +		IMR(PME_TO_ACK_RCVD)	|		\ +		IMR(MISC_CORRECTABLE)	|		\ +		IMR(NONFATAL)		|		\ +		IMR(FATAL)		|		\ +		AMD_MDB_TLP_PCIE_INTX_MASK		\ +	) + +/** + * struct amd_mdb_pcie - PCIe port information + * @pci: DesignWare PCIe controller structure + * @slcr: MDB System Level Control and Status Register (SLCR) base + * @intx_domain: INTx IRQ domain pointer + * @mdb_domain: MDB IRQ domain pointer + * @intx_irq: INTx IRQ interrupt number + */ +struct amd_mdb_pcie { +	struct dw_pcie			pci; +	void __iomem			*slcr; +	struct irq_domain		*intx_domain; +	struct irq_domain		*mdb_domain; +	int				intx_irq; +}; + +static const struct dw_pcie_host_ops amd_mdb_pcie_host_ops = { +}; + +static void amd_mdb_intx_irq_mask(struct irq_data *data) +{ +	struct amd_mdb_pcie *pcie = irq_data_get_irq_chip_data(data); +	struct dw_pcie *pci = &pcie->pci; +	struct dw_pcie_rp *port = &pci->pp; +	unsigned long flags; +	u32 val; + +	raw_spin_lock_irqsave(&port->lock, flags); +	val = FIELD_PREP(AMD_MDB_TLP_PCIE_INTX_MASK, +			 AMD_MDB_PCIE_INTR_INTX_ASSERT(data->hwirq)); + +	/* +	 * Writing '1' to a bit in AMD_MDB_TLP_IR_DISABLE_MISC disables that +	 * interrupt, writing '0' has no effect. +	 */ +	writel_relaxed(val, pcie->slcr + AMD_MDB_TLP_IR_DISABLE_MISC); +	raw_spin_unlock_irqrestore(&port->lock, flags); +} + +static void amd_mdb_intx_irq_unmask(struct irq_data *data) +{ +	struct amd_mdb_pcie *pcie = irq_data_get_irq_chip_data(data); +	struct dw_pcie *pci = &pcie->pci; +	struct dw_pcie_rp *port = &pci->pp; +	unsigned long flags; +	u32 val; + +	raw_spin_lock_irqsave(&port->lock, flags); +	val = FIELD_PREP(AMD_MDB_TLP_PCIE_INTX_MASK, +			 AMD_MDB_PCIE_INTR_INTX_ASSERT(data->hwirq)); + +	/* +	 * Writing '1' to a bit in AMD_MDB_TLP_IR_ENABLE_MISC enables that +	 * interrupt, writing '0' has no effect. +	 */ +	writel_relaxed(val, pcie->slcr + AMD_MDB_TLP_IR_ENABLE_MISC); +	raw_spin_unlock_irqrestore(&port->lock, flags); +} + +static struct irq_chip amd_mdb_intx_irq_chip = { +	.name		= "AMD MDB INTx", +	.irq_mask	= amd_mdb_intx_irq_mask, +	.irq_unmask	= amd_mdb_intx_irq_unmask, +}; + +/** + * amd_mdb_pcie_intx_map - Set the handler for the INTx and mark IRQ as valid + * @domain: IRQ domain + * @irq: Virtual IRQ number + * @hwirq: Hardware interrupt number + * + * Return: Always returns '0'. + */ +static int amd_mdb_pcie_intx_map(struct irq_domain *domain, +				 unsigned int irq, irq_hw_number_t hwirq) +{ +	irq_set_chip_and_handler(irq, &amd_mdb_intx_irq_chip, +				 handle_level_irq); +	irq_set_chip_data(irq, domain->host_data); +	irq_set_status_flags(irq, IRQ_LEVEL); + +	return 0; +} + +/* INTx IRQ domain operations. */ +static const struct irq_domain_ops amd_intx_domain_ops = { +	.map = amd_mdb_pcie_intx_map, +}; + +static irqreturn_t dw_pcie_rp_intx(int irq, void *args) +{ +	struct amd_mdb_pcie *pcie = args; +	unsigned long val; +	int i, int_status; + +	val = readl_relaxed(pcie->slcr + AMD_MDB_TLP_IR_STATUS_MISC); +	int_status = FIELD_GET(AMD_MDB_TLP_PCIE_INTX_MASK, val); + +	for (i = 0; i < PCI_NUM_INTX; i++) { +		if (int_status & AMD_MDB_PCIE_INTR_INTX_ASSERT(i)) +			generic_handle_domain_irq(pcie->intx_domain, i); +	} + +	return IRQ_HANDLED; +} + +#define _IC(x, s)[AMD_MDB_PCIE_INTR_ ## x] = { __stringify(x), s } + +static const struct { +	const char	*sym; +	const char	*str; +} intr_cause[32] = { +	_IC(CMPL_TIMEOUT,	"Completion timeout"), +	_IC(PM_PME_RCVD,	"PM_PME message received"), +	_IC(PME_TO_ACK_RCVD,	"PME_TO_ACK message received"), +	_IC(MISC_CORRECTABLE,	"Correctable error message"), +	_IC(NONFATAL,		"Non fatal error message"), +	_IC(FATAL,		"Fatal error message"), +}; + +static void amd_mdb_event_irq_mask(struct irq_data *d) +{ +	struct amd_mdb_pcie *pcie = irq_data_get_irq_chip_data(d); +	struct dw_pcie *pci = &pcie->pci; +	struct dw_pcie_rp *port = &pci->pp; +	unsigned long flags; +	u32 val; + +	raw_spin_lock_irqsave(&port->lock, flags); +	val = BIT(d->hwirq); +	writel_relaxed(val, pcie->slcr + AMD_MDB_TLP_IR_DISABLE_MISC); +	raw_spin_unlock_irqrestore(&port->lock, flags); +} + +static void amd_mdb_event_irq_unmask(struct irq_data *d) +{ +	struct amd_mdb_pcie *pcie = irq_data_get_irq_chip_data(d); +	struct dw_pcie *pci = &pcie->pci; +	struct dw_pcie_rp *port = &pci->pp; +	unsigned long flags; +	u32 val; + +	raw_spin_lock_irqsave(&port->lock, flags); +	val = BIT(d->hwirq); +	writel_relaxed(val, pcie->slcr + AMD_MDB_TLP_IR_ENABLE_MISC); +	raw_spin_unlock_irqrestore(&port->lock, flags); +} + +static struct irq_chip amd_mdb_event_irq_chip = { +	.name		= "AMD MDB RC-Event", +	.irq_mask	= amd_mdb_event_irq_mask, +	.irq_unmask	= amd_mdb_event_irq_unmask, +}; + +static int amd_mdb_pcie_event_map(struct irq_domain *domain, +				  unsigned int irq, irq_hw_number_t hwirq) +{ +	irq_set_chip_and_handler(irq, &amd_mdb_event_irq_chip, +				 handle_level_irq); +	irq_set_chip_data(irq, domain->host_data); +	irq_set_status_flags(irq, IRQ_LEVEL); + +	return 0; +} + +static const struct irq_domain_ops event_domain_ops = { +	.map = amd_mdb_pcie_event_map, +}; + +static irqreturn_t amd_mdb_pcie_event(int irq, void *args) +{ +	struct amd_mdb_pcie *pcie = args; +	unsigned long val; +	int i; + +	val = readl_relaxed(pcie->slcr + AMD_MDB_TLP_IR_STATUS_MISC); +	val &= ~readl_relaxed(pcie->slcr + AMD_MDB_TLP_IR_MASK_MISC); +	for_each_set_bit(i, &val, 32) +		generic_handle_domain_irq(pcie->mdb_domain, i); +	writel_relaxed(val, pcie->slcr + AMD_MDB_TLP_IR_STATUS_MISC); + +	return IRQ_HANDLED; +} + +static void amd_mdb_pcie_free_irq_domains(struct amd_mdb_pcie *pcie) +{ +	if (pcie->intx_domain) { +		irq_domain_remove(pcie->intx_domain); +		pcie->intx_domain = NULL; +	} + +	if (pcie->mdb_domain) { +		irq_domain_remove(pcie->mdb_domain); +		pcie->mdb_domain = NULL; +	} +} + +static int amd_mdb_pcie_init_port(struct amd_mdb_pcie *pcie) +{ +	unsigned long val; + +	/* Disable all TLP interrupts. */ +	writel_relaxed(AMD_MDB_PCIE_IMR_ALL_MASK, +		       pcie->slcr + AMD_MDB_TLP_IR_DISABLE_MISC); + +	/* Clear pending TLP interrupts. */ +	val = readl_relaxed(pcie->slcr + AMD_MDB_TLP_IR_STATUS_MISC); +	val &= AMD_MDB_PCIE_IMR_ALL_MASK; +	writel_relaxed(val, pcie->slcr + AMD_MDB_TLP_IR_STATUS_MISC); + +	/* Enable all TLP interrupts. */ +	writel_relaxed(AMD_MDB_PCIE_IMR_ALL_MASK, +		       pcie->slcr + AMD_MDB_TLP_IR_ENABLE_MISC); + +	return 0; +} + +/** + * amd_mdb_pcie_init_irq_domains - Initialize IRQ domain + * @pcie: PCIe port information + * @pdev: Platform device + * + * Return: Returns '0' on success and error value on failure. + */ +static int amd_mdb_pcie_init_irq_domains(struct amd_mdb_pcie *pcie, +					 struct platform_device *pdev) +{ +	struct dw_pcie *pci = &pcie->pci; +	struct dw_pcie_rp *pp = &pci->pp; +	struct device *dev = &pdev->dev; +	struct device_node *node = dev->of_node; +	struct device_node *pcie_intc_node; +	int err; + +	pcie_intc_node = of_get_next_child(node, NULL); +	if (!pcie_intc_node) { +		dev_err(dev, "No PCIe Intc node found\n"); +		return -ENODEV; +	} + +	pcie->mdb_domain = irq_domain_add_linear(pcie_intc_node, 32, +						 &event_domain_ops, pcie); +	if (!pcie->mdb_domain) { +		err = -ENOMEM; +		dev_err(dev, "Failed to add MDB domain\n"); +		goto out; +	} + +	irq_domain_update_bus_token(pcie->mdb_domain, DOMAIN_BUS_NEXUS); + +	pcie->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, +						  &amd_intx_domain_ops, pcie); +	if (!pcie->intx_domain) { +		err = -ENOMEM; +		dev_err(dev, "Failed to add INTx domain\n"); +		goto mdb_out; +	} + +	of_node_put(pcie_intc_node); +	irq_domain_update_bus_token(pcie->intx_domain, DOMAIN_BUS_WIRED); + +	raw_spin_lock_init(&pp->lock); + +	return 0; +mdb_out: +	amd_mdb_pcie_free_irq_domains(pcie); +out: +	of_node_put(pcie_intc_node); +	return err; +} + +static irqreturn_t amd_mdb_pcie_intr_handler(int irq, void *args) +{ +	struct amd_mdb_pcie *pcie = args; +	struct device *dev; +	struct irq_data *d; + +	dev = pcie->pci.dev; + +	/* +	 * In the future, error reporting will be hooked to the AER subsystem. +	 * Currently, the driver prints a warning message to the user. +	 */ +	d = irq_domain_get_irq_data(pcie->mdb_domain, irq); +	if (intr_cause[d->hwirq].str) +		dev_warn(dev, "%s\n", intr_cause[d->hwirq].str); +	else +		dev_warn_once(dev, "Unknown IRQ %ld\n", d->hwirq); + +	return IRQ_HANDLED; +} + +static int amd_mdb_setup_irq(struct amd_mdb_pcie *pcie, +			     struct platform_device *pdev) +{ +	struct dw_pcie *pci = &pcie->pci; +	struct dw_pcie_rp *pp = &pci->pp; +	struct device *dev = &pdev->dev; +	int i, irq, err; + +	amd_mdb_pcie_init_port(pcie); + +	pp->irq = platform_get_irq(pdev, 0); +	if (pp->irq < 0) +		return pp->irq; + +	for (i = 0; i < ARRAY_SIZE(intr_cause); i++) { +		if (!intr_cause[i].str) +			continue; + +		irq = irq_create_mapping(pcie->mdb_domain, i); +		if (!irq) { +			dev_err(dev, "Failed to map MDB domain interrupt\n"); +			return -ENOMEM; +		} + +		err = devm_request_irq(dev, irq, amd_mdb_pcie_intr_handler, +				       IRQF_NO_THREAD, intr_cause[i].sym, pcie); +		if (err) { +			dev_err(dev, "Failed to request IRQ %d, err=%d\n", +				irq, err); +			return err; +		} +	} + +	pcie->intx_irq = irq_create_mapping(pcie->mdb_domain, +					    AMD_MDB_PCIE_INTR_INTX); +	if (!pcie->intx_irq) { +		dev_err(dev, "Failed to map INTx interrupt\n"); +		return -ENXIO; +	} + +	err = devm_request_irq(dev, pcie->intx_irq, dw_pcie_rp_intx, +			       IRQF_NO_THREAD, NULL, pcie); +	if (err) { +		dev_err(dev, "Failed to request INTx IRQ %d, err=%d\n", +			irq, err); +		return err; +	} + +	/* Plug the main event handler. */ +	err = devm_request_irq(dev, pp->irq, amd_mdb_pcie_event, IRQF_NO_THREAD, +			       "amd_mdb pcie_irq", pcie); +	if (err) { +		dev_err(dev, "Failed to request event IRQ %d, err=%d\n", +			pp->irq, err); +		return err; +	} + +	return 0; +} + +static int amd_mdb_add_pcie_port(struct amd_mdb_pcie *pcie, +				 struct platform_device *pdev) +{ +	struct dw_pcie *pci = &pcie->pci; +	struct dw_pcie_rp *pp = &pci->pp; +	struct device *dev = &pdev->dev; +	int err; + +	pcie->slcr = devm_platform_ioremap_resource_byname(pdev, "slcr"); +	if (IS_ERR(pcie->slcr)) +		return PTR_ERR(pcie->slcr); + +	err = amd_mdb_pcie_init_irq_domains(pcie, pdev); +	if (err) +		return err; + +	err = amd_mdb_setup_irq(pcie, pdev); +	if (err) { +		dev_err(dev, "Failed to set up interrupts, err=%d\n", err); +		goto out; +	} + +	pp->ops = &amd_mdb_pcie_host_ops; + +	err = dw_pcie_host_init(pp); +	if (err) { +		dev_err(dev, "Failed to initialize host, err=%d\n", err); +		goto out; +	} + +	return 0; + +out: +	amd_mdb_pcie_free_irq_domains(pcie); +	return err; +} + +static int amd_mdb_pcie_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct amd_mdb_pcie *pcie; +	struct dw_pcie *pci; + +	pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); +	if (!pcie) +		return -ENOMEM; + +	pci = &pcie->pci; +	pci->dev = dev; + +	platform_set_drvdata(pdev, pcie); + +	return amd_mdb_add_pcie_port(pcie, pdev); +} + +static const struct of_device_id amd_mdb_pcie_of_match[] = { +	{ +		.compatible = "amd,versal2-mdb-host", +	}, +	{}, +}; + +static struct platform_driver amd_mdb_pcie_driver = { +	.driver = { +		.name	= "amd-mdb-pcie", +		.of_match_table = amd_mdb_pcie_of_match, +		.suppress_bind_attrs = true, +	}, +	.probe = amd_mdb_pcie_probe, +}; + +builtin_platform_driver(amd_mdb_pcie_driver); | 
