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