e18a088
From ec7b5bf1cc1444d9ad13bcef0f0f8d48ff9c0203 Mon Sep 17 00:00:00 2001
73c86eb
From: Peter Robinson <pbrobinson@gmail.com>
e18a088
Date: Sat, 19 Dec 2020 14:10:40 +0000
8341d18
Subject: [PATCH] PCI: Add MCFG quirks for Tegra194 host controllers
8341d18
8341d18
The PCIe controller in Tegra194 SoC is not completely ECAM-compliant.
8341d18
With the current hardware design limitations in place, ECAM can be enabled
8341d18
only for one controller (C5 controller to be precise) with bus numbers
8341d18
starting from 160 instead of 0. A different approach is taken to avoid this
8341d18
abnormal way of enabling ECAM for just one controller but to enable
8341d18
configuration space access for all the other controllers. In this approach,
8341d18
ops are added through MCFG quirk mechanism which access the configuration
8341d18
spaces by dynamically programming iATU (internal AddressTranslation Unit)
8341d18
to generate respective configuration accesses just like the way it is
8341d18
done in DesignWare core sub-system.
8341d18
8341d18
Signed-off-by: Vidya Sagar <vidyas@nvidia.com>
8341d18
Acked-by: Thierry Reding <treding@nvidia.com>
8341d18
[ Updated by jonathanh@nvidia.com only permit building the Tegra194
8341d18
  PCIe driver into the kernel and not as a module ]
8341d18
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
73c86eb
Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
8341d18
---
8341d18
 drivers/acpi/pci_mcfg.c                    |   7 ++
8341d18
 drivers/pci/controller/dwc/Kconfig         |  10 +-
8341d18
 drivers/pci/controller/dwc/Makefile        |   2 +-
8341d18
 drivers/pci/controller/dwc/pcie-tegra194.c | 102 +++++++++++++++++++++
8341d18
 include/linux/pci-ecam.h                   |   1 +
8341d18
 5 files changed, 117 insertions(+), 5 deletions(-)
8341d18
8341d18
diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
e18a088
index 95f23acd5b80..53cab975f612 100644
8341d18
--- a/drivers/acpi/pci_mcfg.c
8341d18
+++ b/drivers/acpi/pci_mcfg.c
8341d18
@@ -116,6 +116,13 @@ static struct mcfg_fixup mcfg_quirks[] = {
8341d18
 	THUNDER_ECAM_QUIRK(2, 12),
8341d18
 	THUNDER_ECAM_QUIRK(2, 13),
8341d18
 
8341d18
+	{ "NVIDIA", "TEGRA194", 1, 0, MCFG_BUS_ANY, &tegra194_pcie_ops},
8341d18
+	{ "NVIDIA", "TEGRA194", 1, 1, MCFG_BUS_ANY, &tegra194_pcie_ops},
8341d18
+	{ "NVIDIA", "TEGRA194", 1, 2, MCFG_BUS_ANY, &tegra194_pcie_ops},
8341d18
+	{ "NVIDIA", "TEGRA194", 1, 3, MCFG_BUS_ANY, &tegra194_pcie_ops},
8341d18
+	{ "NVIDIA", "TEGRA194", 1, 4, MCFG_BUS_ANY, &tegra194_pcie_ops},
8341d18
+	{ "NVIDIA", "TEGRA194", 1, 5, MCFG_BUS_ANY, &tegra194_pcie_ops},
8341d18
+
8341d18
 #define XGENE_V1_ECAM_MCFG(rev, seg) \
8341d18
 	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
8341d18
 		&xgene_v1_pcie_ecam_ops }
8341d18
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
e18a088
index bc049865f8e0..c5d40951a6ad 100644
8341d18
--- a/drivers/pci/controller/dwc/Kconfig
8341d18
+++ b/drivers/pci/controller/dwc/Kconfig
e18a088
@@ -248,25 +248,27 @@ config PCI_MESON
8341d18
 	  implement the driver.
8341d18
 
8341d18
 config PCIE_TEGRA194
8341d18
-	tristate
8341d18
+	bool
8341d18
 
8341d18
 config PCIE_TEGRA194_HOST
8341d18
-	tristate "NVIDIA Tegra194 (and later) PCIe controller - Host Mode"
8341d18
+	bool "NVIDIA Tegra194 (and later) PCIe controller - Host Mode"
8341d18
 	depends on ARCH_TEGRA_194_SOC || COMPILE_TEST
8341d18
 	depends on PCI_MSI_IRQ_DOMAIN
8341d18
 	select PCIE_DW_HOST
8341d18
 	select PHY_TEGRA194_P2U
8341d18
 	select PCIE_TEGRA194
8341d18
+	default y if ARCH_TEGRA_194_SOC
8341d18
 	help
8341d18
 	  Enables support for the PCIe controller in the NVIDIA Tegra194 SoC to
8341d18
 	  work in host mode. There are two instances of PCIe controllers in
8341d18
 	  Tegra194. This controller can work either as EP or RC. In order to
8341d18
 	  enable host-specific features PCIE_TEGRA194_HOST must be selected and
8341d18
 	  in order to enable device-specific features PCIE_TEGRA194_EP must be
8341d18
-	  selected. This uses the DesignWare core.
8341d18
+	  selected. This uses the DesignWare core. ACPI platforms with Tegra194
8341d18
+	  don't need to enable this.
8341d18
 
8341d18
 config PCIE_TEGRA194_EP
8341d18
-	tristate "NVIDIA Tegra194 (and later) PCIe controller - Endpoint Mode"
8341d18
+	bool "NVIDIA Tegra194 (and later) PCIe controller - Endpoint Mode"
8341d18
 	depends on ARCH_TEGRA_194_SOC || COMPILE_TEST
8341d18
 	depends on PCI_ENDPOINT
8341d18
 	select PCIE_DW_EP
8341d18
diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile
73c86eb
index a751553fa0db..dbb981876556 100644
8341d18
--- a/drivers/pci/controller/dwc/Makefile
8341d18
+++ b/drivers/pci/controller/dwc/Makefile
8341d18
@@ -17,7 +17,6 @@ obj-$(CONFIG_PCIE_INTEL_GW) += pcie-intel-gw.o
8341d18
 obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o
8341d18
 obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o
8341d18
 obj-$(CONFIG_PCI_MESON) += pci-meson.o
8341d18
-obj-$(CONFIG_PCIE_TEGRA194) += pcie-tegra194.o
8341d18
 obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o
8341d18
 obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o
8341d18
 
8341d18
@@ -34,4 +33,5 @@ obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o
8341d18
 ifdef CONFIG_PCI
8341d18
 obj-$(CONFIG_ARM64) += pcie-al.o
8341d18
 obj-$(CONFIG_ARM64) += pcie-hisi.o
8341d18
+obj-$(CONFIG_ARM64) += pcie-tegra194.o
8341d18
 endif
8341d18
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
e18a088
index f920e7efe118..87c7929db727 100644
8341d18
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
8341d18
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
8341d18
@@ -22,6 +22,8 @@
8341d18
 #include <linux/of_irq.h>
8341d18
 #include <linux/of_pci.h>
8341d18
 #include <linux/pci.h>
8341d18
+#include <linux/pci-acpi.h>
8341d18
+#include <linux/pci-ecam.h>
8341d18
 #include <linux/phy/phy.h>
8341d18
 #include <linux/pinctrl/consumer.h>
8341d18
 #include <linux/platform_device.h>
e18a088
@@ -311,6 +313,103 @@ struct tegra_pcie_dw_of_data {
8341d18
 	enum dw_pcie_device_mode mode;
8341d18
 };
8341d18
 
8341d18
+#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
8341d18
+struct tegra194_pcie_acpi  {
8341d18
+	void __iomem *config_base;
8341d18
+	void __iomem *iatu_base;
8341d18
+	void __iomem *dbi_base;
8341d18
+};
8341d18
+
8341d18
+static int tegra194_acpi_init(struct pci_config_window *cfg)
8341d18
+{
8341d18
+	struct device *dev = cfg->parent;
8341d18
+	struct tegra194_pcie_acpi *pcie;
8341d18
+
8341d18
+	pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
8341d18
+	if (!pcie)
8341d18
+		return -ENOMEM;
8341d18
+
8341d18
+	pcie->config_base = cfg->win;
8341d18
+	pcie->iatu_base = cfg->win + SZ_256K;
8341d18
+	pcie->dbi_base = cfg->win + SZ_512K;
8341d18
+	cfg->priv = pcie;
8341d18
+
8341d18
+	return 0;
8341d18
+}
8341d18
+
8341d18
+static inline void atu_reg_write(struct tegra194_pcie_acpi *pcie, int index,
8341d18
+				 u32 val, u32 reg)
8341d18
+{
8341d18
+	u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
8341d18
+
8341d18
+	writel(val, pcie->iatu_base + offset + reg);
8341d18
+}
8341d18
+
8341d18
+static void program_outbound_atu(struct tegra194_pcie_acpi *pcie, int index,
8341d18
+				 int type, u64 cpu_addr, u64 pci_addr, u64 size)
8341d18
+{
8341d18
+	atu_reg_write(pcie, index, lower_32_bits(cpu_addr),
8341d18
+		      PCIE_ATU_LOWER_BASE);
8341d18
+	atu_reg_write(pcie, index, upper_32_bits(cpu_addr),
8341d18
+		      PCIE_ATU_UPPER_BASE);
8341d18
+	atu_reg_write(pcie, index, lower_32_bits(pci_addr),
8341d18
+		      PCIE_ATU_LOWER_TARGET);
8341d18
+	atu_reg_write(pcie, index, lower_32_bits(cpu_addr + size - 1),
8341d18
+		      PCIE_ATU_LIMIT);
8341d18
+	atu_reg_write(pcie, index, upper_32_bits(pci_addr),
8341d18
+		      PCIE_ATU_UPPER_TARGET);
8341d18
+	atu_reg_write(pcie, index, type, PCIE_ATU_CR1);
8341d18
+	atu_reg_write(pcie, index, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
8341d18
+}
8341d18
+
8341d18
+static void __iomem *tegra194_map_bus(struct pci_bus *bus,
8341d18
+				      unsigned int devfn, int where)
8341d18
+{
8341d18
+	struct pci_config_window *cfg = bus->sysdata;
8341d18
+	struct tegra194_pcie_acpi *pcie = cfg->priv;
8341d18
+	u32 busdev;
8341d18
+	int type;
8341d18
+
8341d18
+	if (bus->number < cfg->busr.start || bus->number > cfg->busr.end)
8341d18
+		return NULL;
8341d18
+
8341d18
+	if (bus->number == cfg->busr.start) {
8341d18
+		if (PCI_SLOT(devfn) == 0)
8341d18
+			return pcie->dbi_base + where;
8341d18
+		else
8341d18
+			return NULL;
8341d18
+	}
8341d18
+
8341d18
+	busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
8341d18
+		 PCIE_ATU_FUNC(PCI_FUNC(devfn));
8341d18
+
8341d18
+	if (bus->parent->number == cfg->busr.start) {
8341d18
+		if (PCI_SLOT(devfn) == 0)
8341d18
+			type = PCIE_ATU_TYPE_CFG0;
8341d18
+		else
8341d18
+			return NULL;
8341d18
+	} else {
8341d18
+		type = PCIE_ATU_TYPE_CFG1;
8341d18
+	}
8341d18
+
8341d18
+	program_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0, type,
8341d18
+			     cfg->res.start, busdev, SZ_256K);
8341d18
+	return (void __iomem *)(pcie->config_base + where);
8341d18
+}
8341d18
+
8341d18
+const struct pci_ecam_ops tegra194_pcie_ops = {
8341d18
+	.bus_shift	= 20,
8341d18
+	.init		= tegra194_acpi_init,
8341d18
+	.pci_ops	= {
8341d18
+		.map_bus	= tegra194_map_bus,
8341d18
+		.read		= pci_generic_config_read,
8341d18
+		.write		= pci_generic_config_write,
8341d18
+	}
8341d18
+};
8341d18
+#endif /* defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) */
8341d18
+
8341d18
+#ifdef CONFIG_PCIE_TEGRA194
8341d18
+
8341d18
 static inline struct tegra_pcie_dw *to_tegra_pcie(struct dw_pcie *pci)
8341d18
 {
8341d18
 	return container_of(pci, struct tegra_pcie_dw, pci);
e18a088
@@ -2339,3 +2438,6 @@ MODULE_DEVICE_TABLE(of, tegra_pcie_dw_of_match);
8341d18
 MODULE_AUTHOR("Vidya Sagar <vidyas@nvidia.com>");
8341d18
 MODULE_DESCRIPTION("NVIDIA PCIe host controller driver");
8341d18
 MODULE_LICENSE("GPL v2");
8341d18
+
8341d18
+#endif /* CONFIG_PCIE_TEGRA194 */
8341d18
+
8341d18
diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h
e18a088
index 033ce74f02e8..ccbf3c38c6e6 100644
8341d18
--- a/include/linux/pci-ecam.h
8341d18
+++ b/include/linux/pci-ecam.h
e18a088
@@ -58,6 +58,7 @@ extern const struct pci_ecam_ops pci_thunder_ecam_ops; /* Cavium ThunderX 1.x */
8341d18
 extern const struct pci_ecam_ops xgene_v1_pcie_ecam_ops; /* APM X-Gene PCIe v1 */
8341d18
 extern const struct pci_ecam_ops xgene_v2_pcie_ecam_ops; /* APM X-Gene PCIe v2.x */
8341d18
 extern const struct pci_ecam_ops al_pcie_ops;	/* Amazon Annapurna Labs PCIe */
8341d18
+extern const struct pci_ecam_ops tegra194_pcie_ops; /* Tegra194 PCIe */
8341d18
 #endif
8341d18
 
8341d18
 #if IS_ENABLED(CONFIG_PCI_HOST_COMMON)
8341d18
-- 
e18a088
2.29.2
8341d18