mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 08:33:17 +02:00
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
This commit is contained in:
commit
c51638f15e
121
Documentation/devicetree/bindings/pci/amd,versal2-mdb-host.yaml
Normal file
121
Documentation/devicetree/bindings/pci/amd,versal2-mdb-host.yaml
Normal file
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
476
drivers/pci/controller/dwc/pcie-amd-mdb.c
Normal file
476
drivers/pci/controller/dwc/pcie-amd-mdb.c
Normal file
|
|
@ -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);
|
||||
Loading…
Reference in New Issue
Block a user