Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c5d46039 authored by Duc Dang's avatar Duc Dang Committed by Bjorn Helgaas
Browse files

PCI: Add MCFG quirks for X-Gene host controller



PCIe controllers in X-Gene SoCs are not ECAM compliant: software needs to
configure additional controller's register to address device at
bus:dev:function.

Add a quirk to discover controller MMIO register space and configure
controller registers to select and address the target secondary device.

The quirk will only be applied for X-Gene PCIe MCFG table with
OEM revison 1, 2, 3 or 4 (PCIe controller v1 and v2 on X-Gene SoCs).

Tested-by: default avatarJon Masters <jcm@redhat.com>
Signed-off-by: default avatarDuc Dang <dhdang@apm.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
parent 648d93fc
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -108,6 +108,31 @@ static struct mcfg_fixup mcfg_quirks[] = {
	THUNDER_ECAM_QUIRK(2, 11),
	THUNDER_ECAM_QUIRK(2, 12),
	THUNDER_ECAM_QUIRK(2, 13),

#define XGENE_V1_ECAM_MCFG(rev, seg) \
	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
		&xgene_v1_pcie_ecam_ops }
#define XGENE_V2_ECAM_MCFG(rev, seg) \
	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
		&xgene_v2_pcie_ecam_ops }
	/* X-Gene SoC with v1 PCIe controller */
	XGENE_V1_ECAM_MCFG(1, 0),
	XGENE_V1_ECAM_MCFG(1, 1),
	XGENE_V1_ECAM_MCFG(1, 2),
	XGENE_V1_ECAM_MCFG(1, 3),
	XGENE_V1_ECAM_MCFG(1, 4),
	XGENE_V1_ECAM_MCFG(2, 0),
	XGENE_V1_ECAM_MCFG(2, 1),
	XGENE_V1_ECAM_MCFG(2, 2),
	XGENE_V1_ECAM_MCFG(2, 3),
	XGENE_V1_ECAM_MCFG(2, 4),
	/* X-Gene SoC with v2.1 PCIe controller */
	XGENE_V2_ECAM_MCFG(3, 0),
	XGENE_V2_ECAM_MCFG(3, 1),
	/* X-Gene SoC with v2.2 PCIe controller */
	XGENE_V2_ECAM_MCFG(4, 0),
	XGENE_V2_ECAM_MCFG(4, 1),
	XGENE_V2_ECAM_MCFG(4, 2),
};

static char mcfg_oem_id[ACPI_OEM_ID_SIZE];
+2 −2
Original line number Diff line number Diff line
@@ -133,8 +133,8 @@ config PCIE_XILINX

config PCI_XGENE
	bool "X-Gene PCIe controller"
	depends on ARCH_XGENE
	depends on OF
	depends on ARM64
	depends on OF || (ACPI && PCI_QUIRKS)
	select PCIEPORTBUS
	help
	  Say Y here if you want internal PCI support on APM X-Gene SoC.
+1 −1
Original line number Diff line number Diff line
@@ -15,7 +15,7 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
obj-$(CONFIG_ARM64) += pci-xgene.o
obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
+119 −7
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@
#include <linux/of_irq.h>
#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/pci-acpi.h>
#include <linux/pci-ecam.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

@@ -64,7 +66,9 @@
/* PCIe IP version */
#define XGENE_PCIE_IP_VER_UNKN		0
#define XGENE_PCIE_IP_VER_1		1
#define XGENE_PCIE_IP_VER_2		2

#if defined(CONFIG_PCI_XGENE) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS))
struct xgene_pcie_port {
	struct device_node	*node;
	struct device		*dev;
@@ -91,13 +95,24 @@ static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
	return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags;
}

static inline struct xgene_pcie_port *pcie_bus_to_port(struct pci_bus *bus)
{
	struct pci_config_window *cfg;

	if (acpi_disabled)
		return (struct xgene_pcie_port *)(bus->sysdata);

	cfg = bus->sysdata;
	return (struct xgene_pcie_port *)(cfg->priv);
}

/*
 * When the address bit [17:16] is 2'b01, the Configuration access will be
 * treated as Type 1 and it will be forwarded to external PCIe device.
 */
static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
{
	struct xgene_pcie_port *port = bus->sysdata;
	struct xgene_pcie_port *port = pcie_bus_to_port(bus);

	if (bus->number >= (bus->primary + 1))
		return port->cfg_base + AXI_EP_CFG_ACCESS;
@@ -111,7 +126,7 @@ static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
 */
static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
{
	struct xgene_pcie_port *port = bus->sysdata;
	struct xgene_pcie_port *port = pcie_bus_to_port(bus);
	unsigned int b, d, f;
	u32 rtdid_val = 0;

@@ -158,7 +173,7 @@ static void __iomem *xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
				    int where, int size, u32 *val)
{
	struct xgene_pcie_port *port = bus->sysdata;
	struct xgene_pcie_port *port = pcie_bus_to_port(bus);

	if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
	    PCIBIOS_SUCCESSFUL)
@@ -182,13 +197,103 @@ static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,

	return PCIBIOS_SUCCESSFUL;
}
#endif

static struct pci_ops xgene_pcie_ops = {
#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
static int xgene_get_csr_resource(struct acpi_device *adev,
				  struct resource *res)
{
	struct device *dev = &adev->dev;
	struct resource_entry *entry;
	struct list_head list;
	unsigned long flags;
	int ret;

	INIT_LIST_HEAD(&list);
	flags = IORESOURCE_MEM;
	ret = acpi_dev_get_resources(adev, &list,
				     acpi_dev_filter_resource_type_cb,
				     (void *) flags);
	if (ret < 0) {
		dev_err(dev, "failed to parse _CRS method, error code %d\n",
			ret);
		return ret;
	}

	if (ret == 0) {
		dev_err(dev, "no IO and memory resources present in _CRS\n");
		return -EINVAL;
	}

	entry = list_first_entry(&list, struct resource_entry, node);
	*res = *entry->res;
	acpi_dev_free_resource_list(&list);
	return 0;
}

static int xgene_pcie_ecam_init(struct pci_config_window *cfg, u32 ipversion)
{
	struct device *dev = cfg->parent;
	struct acpi_device *adev = to_acpi_device(dev);
	struct xgene_pcie_port *port;
	struct resource csr;
	int ret;

	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
	if (!port)
		return -ENOMEM;

	ret = xgene_get_csr_resource(adev, &csr);
	if (ret) {
		dev_err(dev, "can't get CSR resource\n");
		kfree(port);
		return ret;
	}
	port->csr_base = devm_ioremap_resource(dev, &csr);
	if (IS_ERR(port->csr_base)) {
		kfree(port);
		return -ENOMEM;
	}

	port->cfg_base = cfg->win;
	port->version = ipversion;

	cfg->priv = port;
	return 0;
}

static int xgene_v1_pcie_ecam_init(struct pci_config_window *cfg)
{
	return xgene_pcie_ecam_init(cfg, XGENE_PCIE_IP_VER_1);
}

struct pci_ecam_ops xgene_v1_pcie_ecam_ops = {
	.bus_shift      = 16,
	.init           = xgene_v1_pcie_ecam_init,
	.pci_ops        = {
		.map_bus        = xgene_pcie_map_bus,
		.read           = xgene_pcie_config_read32,
	.write = pci_generic_config_write32,
		.write          = pci_generic_config_write,
	}
};

static int xgene_v2_pcie_ecam_init(struct pci_config_window *cfg)
{
	return xgene_pcie_ecam_init(cfg, XGENE_PCIE_IP_VER_2);
}

struct pci_ecam_ops xgene_v2_pcie_ecam_ops = {
	.bus_shift      = 16,
	.init           = xgene_v2_pcie_ecam_init,
	.pci_ops        = {
		.map_bus        = xgene_pcie_map_bus,
		.read           = xgene_pcie_config_read32,
		.write          = pci_generic_config_write,
	}
};
#endif

#if defined(CONFIG_PCI_XGENE)
static u64 xgene_pcie_set_ib_mask(struct xgene_pcie_port *port, u32 addr,
				  u32 flags, u64 size)
{
@@ -521,6 +626,12 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
	return 0;
}

static struct pci_ops xgene_pcie_ops = {
	.map_bus = xgene_pcie_map_bus,
	.read = xgene_pcie_config_read32,
	.write = pci_generic_config_write32,
};

static int xgene_pcie_probe_bridge(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
@@ -591,3 +702,4 @@ static struct platform_driver xgene_pcie_driver = {
	.probe = xgene_pcie_probe_bridge,
};
builtin_platform_driver(xgene_pcie_driver);
#endif
+2 −0
Original line number Diff line number Diff line
@@ -64,6 +64,8 @@ extern struct pci_ecam_ops pci_32b_ops; /* 32-bit accesses only */
extern struct pci_ecam_ops hisi_pcie_ops;	/* HiSilicon */
extern struct pci_ecam_ops thunder_pem_ecam_ops; /* Cavium ThunderX 1.x & 2.x */
extern struct pci_ecam_ops pci_thunder_ecam_ops; /* Cavium ThunderX 1.x */
extern struct pci_ecam_ops xgene_v1_pcie_ecam_ops; /* APM X-Gene PCIe v1 */
extern struct pci_ecam_ops xgene_v2_pcie_ecam_ops; /* APM X-Gene PCIe v2.x */
#endif

#ifdef CONFIG_PCI_HOST_GENERIC