linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH] PCI/ACPI: xgene: Add ECAM quirk for X-Gene PCIe controller
@ 2016-09-17 14:24 Duc Dang
  2016-09-19 20:06 ` Bjorn Helgaas
  0 siblings, 1 reply; 9+ messages in thread
From: Duc Dang @ 2016-09-17 14:24 UTC (permalink / raw)
  To: helgaas, rafael, Lorenzo.Pieralisi
  Cc: arnd, msalter, linux-pci, linux-arm-kernel, linux-kernel, jcm,
	tn, patches, Duc Dang

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

This patch depends on "ECAM quirks handling for ARM64 platforms"
series (http://www.spinics.net/lists/arm-kernel/msg530692.html)
to address the limitation above for X-Gene PCIe controller.

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).

Signed-off-by: Duc Dang <dhdang@apm.com>
---
 drivers/acpi/pci_mcfg.c           |  32 +++++
 drivers/pci/host/Makefile         |   2 +-
 drivers/pci/host/pci-xgene-ecam.c | 280 ++++++++++++++++++++++++++++++++++++++
 include/linux/pci-ecam.h          |   5 +
 4 files changed, 318 insertions(+), 1 deletion(-)
 create mode 100644 drivers/pci/host/pci-xgene-ecam.c

diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
index ddf338b..adce35f 100644
--- a/drivers/acpi/pci_mcfg.c
+++ b/drivers/acpi/pci_mcfg.c
@@ -123,6 +123,38 @@ static struct mcfg_fixup mcfg_quirks[] = {
 	{ "CAVIUM", "THUNDERX", 2, 13, MCFG_BUS_ANY, &pci_thunder_ecam_ops,
 	  MCFG_RES_EMPTY},
 #endif
+#ifdef CONFIG_PCI_XGENE
+	{"APM   ", "XGENE   ", 1, 0, MCFG_BUS_ANY,
+		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
+	{"APM   ", "XGENE   ", 1, 1, MCFG_BUS_ANY,
+		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
+	{"APM   ", "XGENE   ", 1, 2, MCFG_BUS_ANY,
+		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
+	{"APM   ", "XGENE   ", 1, 3, MCFG_BUS_ANY,
+		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
+	{"APM   ", "XGENE   ", 1, 4, MCFG_BUS_ANY,
+		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
+	{"APM   ", "XGENE   ", 2, 0, MCFG_BUS_ANY,
+		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
+	{"APM   ", "XGENE   ", 2, 1, MCFG_BUS_ANY,
+		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
+	{"APM   ", "XGENE   ", 2, 2, MCFG_BUS_ANY,
+		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
+	{"APM   ", "XGENE   ", 2, 3, MCFG_BUS_ANY,
+		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
+	{"APM   ", "XGENE   ", 2, 4, MCFG_BUS_ANY,
+		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
+	{"APM   ", "XGENE   ", 3, 0, MCFG_BUS_ANY,
+		&xgene_v2_1_pcie_ecam_ops, MCFG_RES_EMPTY},
+	{"APM   ", "XGENE   ", 3, 1, MCFG_BUS_ANY,
+		&xgene_v2_1_pcie_ecam_ops, MCFG_RES_EMPTY},
+	{"APM   ", "XGENE   ", 4, 0, MCFG_BUS_ANY,
+		&xgene_v2_2_pcie_ecam_ops, MCFG_RES_EMPTY},
+	{"APM   ", "XGENE   ", 4, 1, MCFG_BUS_ANY,
+		&xgene_v2_2_pcie_ecam_ops, MCFG_RES_EMPTY},
+	{"APM   ", "XGENE   ", 4, 2, MCFG_BUS_ANY,
+		&xgene_v2_2_pcie_ecam_ops, MCFG_RES_EMPTY},
+#endif
 };
 
 static char mcfg_oem_id[ACPI_OEM_ID_SIZE];
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 8843410..af4f505 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -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_PCI_XGENE) += pci-xgene.o pci-xgene-ecam.o
 obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-ecam.c b/drivers/pci/host/pci-xgene-ecam.c
new file mode 100644
index 0000000..b66a04f
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-ecam.c
@@ -0,0 +1,280 @@
+/*
+ * APM X-Gene PCIe ECAM fixup driver
+ *
+ * Copyright (c) 2016, Applied Micro Circuits Corporation
+ * Author:
+ *	Duc Dang <dhdang@apm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/pci-acpi.h>
+#include <linux/platform_device.h>
+#include <linux/pci-ecam.h>
+
+#ifdef CONFIG_ACPI
+#define RTDID			0x160
+#define ROOT_CAP_AND_CTRL	0x5C
+
+/* PCIe IP version */
+#define XGENE_PCIE_IP_VER_UNKN	0
+#define XGENE_PCIE_IP_VER_1	1
+#define XGENE_PCIE_IP_VER_2	2
+
+#define XGENE_CSR_LENGTH	0x10000
+
+struct xgene_pcie_acpi_root {
+	void __iomem *csr_base;
+	u32 version;
+};
+
+static int xgene_v1_pcie_ecam_init(struct pci_config_window *cfg)
+{
+	struct xgene_pcie_acpi_root *xgene_root;
+	struct device *dev = cfg->parent;
+	u32 csr_base;
+
+	xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL);
+	if (!xgene_root)
+		return -ENOMEM;
+
+	switch (cfg->res.start) {
+	case 0xE0D0000000ULL:
+		csr_base = 0x1F2B0000;
+		break;
+	case 0xD0D0000000ULL:
+		csr_base = 0x1F2C0000;
+		break;
+	case 0x90D0000000ULL:
+		csr_base = 0x1F2D0000;
+		break;
+	case 0xA0D0000000ULL:
+		csr_base = 0x1F500000;
+		break;
+	case 0xC0D0000000ULL:
+		csr_base = 0x1F510000;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH);
+	if (!xgene_root->csr_base) {
+		kfree(xgene_root);
+		return -ENODEV;
+	}
+
+	xgene_root->version = XGENE_PCIE_IP_VER_1;
+
+	cfg->priv = xgene_root;
+
+	return 0;
+}
+
+static int xgene_v2_1_pcie_ecam_init(struct pci_config_window *cfg)
+{
+	struct xgene_pcie_acpi_root *xgene_root;
+	struct device *dev = cfg->parent;
+	resource_size_t csr_base;
+
+	xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL);
+	if (!xgene_root)
+		return -ENOMEM;
+
+	switch (cfg->res.start) {
+	case 0xC0D0000000ULL:
+		csr_base = 0x1F2B0000;
+		break;
+	case 0xA0D0000000ULL:
+		csr_base = 0x1F2C0000;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH);
+	if (!xgene_root->csr_base) {
+		kfree(xgene_root);
+		return -ENODEV;
+	}
+
+	xgene_root->version = XGENE_PCIE_IP_VER_2;
+
+	cfg->priv = xgene_root;
+
+	return 0;
+}
+
+static int xgene_v2_2_pcie_ecam_init(struct pci_config_window *cfg)
+{
+	struct xgene_pcie_acpi_root *xgene_root;
+	struct device *dev = cfg->parent;
+	resource_size_t csr_base;
+
+	xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL);
+	if (!xgene_root)
+		return -ENOMEM;
+
+	switch (cfg->res.start) {
+	case 0xE0D0000000ULL:
+		csr_base = 0x1F2B0000;
+		break;
+	case 0xA0D0000000ULL:
+		csr_base = 0x1F500000;
+		break;
+	case 0x90D0000000ULL:
+		csr_base = 0x1F2D0000;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH);
+	if (!xgene_root->csr_base) {
+		kfree(xgene_root);
+		return -ENODEV;
+	}
+
+	xgene_root->version = XGENE_PCIE_IP_VER_2;
+
+	cfg->priv = xgene_root;
+
+	return 0;
+}
+/*
+ * For Configuration request, RTDID register is used as Bus Number,
+ * Device Number and Function number of the header fields.
+ */
+static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
+{
+	struct pci_config_window *cfg = bus->sysdata;
+	struct xgene_pcie_acpi_root *port = cfg->priv;
+	unsigned int b, d, f;
+	u32 rtdid_val = 0;
+
+	b = bus->number;
+	d = PCI_SLOT(devfn);
+	f = PCI_FUNC(devfn);
+
+	if (!pci_is_root_bus(bus))
+		rtdid_val = (b << 8) | (d << 3) | f;
+
+	writel(rtdid_val, port->csr_base + RTDID);
+	/* read the register back to ensure flush */
+	readl(port->csr_base + RTDID);
+}
+
+/*
+ * X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as
+ * the translation from PCI bus to native BUS.  Entire DDR region
+ * is mapped into PCIe space using these registers, so it can be
+ * reached by DMA from EP devices.  The BAR0/1 of bridge should be
+ * hidden during enumeration to avoid the sizing and resource allocation
+ * by PCIe core.
+ */
+static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset)
+{
+	if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) ||
+				     (offset == PCI_BASE_ADDRESS_1)))
+		return true;
+
+	return false;
+}
+
+void __iomem *xgene_pcie_ecam_map_bus(struct pci_bus *bus,
+				      unsigned int devfn, int where)
+{
+	struct pci_config_window *cfg = bus->sysdata;
+	unsigned int busn = bus->number;
+	void __iomem *base;
+
+	if (busn < cfg->busr.start || busn > cfg->busr.end)
+		return NULL;
+
+	if ((pci_is_root_bus(bus) && devfn != 0) ||
+	    xgene_pcie_hide_rc_bars(bus, where))
+		return NULL;
+
+	xgene_pcie_set_rtdid_reg(bus, devfn);
+
+	if (busn > cfg->busr.start)
+		base = cfg->win + (1 << cfg->ops->bus_shift);
+	else
+		base = cfg->win;
+
+	return base + where;
+}
+
+static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
+				    int where, int size, u32 *val)
+{
+	struct pci_config_window *cfg = bus->sysdata;
+	struct xgene_pcie_acpi_root *port = cfg->priv;
+
+	if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
+	    PCIBIOS_SUCCESSFUL)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	/*
+	* The v1 controller has a bug in its Configuration Request
+	* Retry Status (CRS) logic: when CRS is enabled and we read the
+	* Vendor and Device ID of a non-existent device, the controller
+	* fabricates return data of 0xFFFF0001 ("device exists but is not
+	* ready") instead of 0xFFFFFFFF ("device does not exist").  This
+	* causes the PCI core to retry the read until it times out.
+	* Avoid this by not claiming to support CRS.
+	*/
+	if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) &&
+	    ((where & ~0x3) == ROOT_CAP_AND_CTRL))
+		*val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
+
+	if (size <= 2)
+		*val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+struct pci_ecam_ops xgene_v1_pcie_ecam_ops = {
+	.bus_shift	= 16,
+	.init		= xgene_v1_pcie_ecam_init,
+	.pci_ops	= {
+		.map_bus	= xgene_pcie_ecam_map_bus,
+		.read		= xgene_pcie_config_read32,
+		.write		= pci_generic_config_write,
+	}
+};
+
+struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops = {
+	.bus_shift	= 16,
+	.init		= xgene_v2_1_pcie_ecam_init,
+	.pci_ops	= {
+		.map_bus	= xgene_pcie_ecam_map_bus,
+		.read		= xgene_pcie_config_read32,
+		.write		= pci_generic_config_write,
+	}
+};
+
+struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops = {
+	.bus_shift	= 16,
+	.init		= xgene_v2_2_pcie_ecam_init,
+	.pci_ops	= {
+		.map_bus	= xgene_pcie_ecam_map_bus,
+		.read		= xgene_pcie_config_read32,
+		.write		= pci_generic_config_write,
+	}
+};
+#endif
diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h
index 35f0e81..40da3e7 100644
--- a/include/linux/pci-ecam.h
+++ b/include/linux/pci-ecam.h
@@ -65,6 +65,11 @@ extern struct pci_ecam_ops pci_thunder_pem_ops;
 #ifdef CONFIG_PCI_HOST_THUNDER_ECAM
 extern struct pci_ecam_ops pci_thunder_ecam_ops;
 #endif
+#ifdef CONFIG_PCI_XGENE
+extern struct pci_ecam_ops xgene_v1_pcie_ecam_ops;
+extern struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops;
+extern struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops;
+#endif
 
 #ifdef CONFIG_PCI_HOST_GENERIC
 /* for DT-based PCI controllers that support ECAM */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH] PCI/ACPI: xgene: Add ECAM quirk for X-Gene PCIe controller
  2016-09-17 14:24 [RFC PATCH] PCI/ACPI: xgene: Add ECAM quirk for X-Gene PCIe controller Duc Dang
@ 2016-09-19 20:06 ` Bjorn Helgaas
  2016-09-20  1:07   ` Duc Dang
  0 siblings, 1 reply; 9+ messages in thread
From: Bjorn Helgaas @ 2016-09-19 20:06 UTC (permalink / raw)
  To: Duc Dang
  Cc: rafael, Lorenzo.Pieralisi, arnd, msalter, linux-pci,
	linux-arm-kernel, linux-kernel, jcm, tn, patches

Hi Duc,

On Sat, Sep 17, 2016 at 07:24:38AM -0700, Duc Dang wrote:
> PCIe controller in X-Gene SoCs is not ECAM compliant: software
> needs to configure additional concontroller register to address
> device at bus:dev:function.
> 
> This patch depends on "ECAM quirks handling for ARM64 platforms"
> series (http://www.spinics.net/lists/arm-kernel/msg530692.html)
> to address the limitation above for X-Gene PCIe controller.
> 
> 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).
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> ---
>  drivers/acpi/pci_mcfg.c           |  32 +++++
>  drivers/pci/host/Makefile         |   2 +-
>  drivers/pci/host/pci-xgene-ecam.c | 280 ++++++++++++++++++++++++++++++++++++++
>  include/linux/pci-ecam.h          |   5 +
>  4 files changed, 318 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/pci/host/pci-xgene-ecam.c

This adds a bunch of stuff, but doesn't remove anything.  So I assume
it's adding new functionality that didn't exist before.  What is it?

I sort of expected this to also remove, for example, the seemingly
identical xgene_pcie_config_read32() in drivers/pci/host/pci-xgene.c.
Actually, a bunch of this code seems to be duplicated from there.  It
doesn't seem like we should end up with all this duplicated code.

I'd really like it better if all this could get folded into
pci-xgene.c so we don't end up with more files.

> diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
> index ddf338b..adce35f 100644
> --- a/drivers/acpi/pci_mcfg.c
> +++ b/drivers/acpi/pci_mcfg.c
> @@ -123,6 +123,38 @@ static struct mcfg_fixup mcfg_quirks[] = {
>  	{ "CAVIUM", "THUNDERX", 2, 13, MCFG_BUS_ANY, &pci_thunder_ecam_ops,
>  	  MCFG_RES_EMPTY},
>  #endif
> +#ifdef CONFIG_PCI_XGENE
> +	{"APM   ", "XGENE   ", 1, 0, MCFG_BUS_ANY,
> +		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
> +	{"APM   ", "XGENE   ", 1, 1, MCFG_BUS_ANY,
> +		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
> +	{"APM   ", "XGENE   ", 1, 2, MCFG_BUS_ANY,
> +		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
> +	{"APM   ", "XGENE   ", 1, 3, MCFG_BUS_ANY,
> +		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
> +	{"APM   ", "XGENE   ", 1, 4, MCFG_BUS_ANY,
> +		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
> +	{"APM   ", "XGENE   ", 2, 0, MCFG_BUS_ANY,
> +		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
> +	{"APM   ", "XGENE   ", 2, 1, MCFG_BUS_ANY,
> +		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
> +	{"APM   ", "XGENE   ", 2, 2, MCFG_BUS_ANY,
> +		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
> +	{"APM   ", "XGENE   ", 2, 3, MCFG_BUS_ANY,
> +		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
> +	{"APM   ", "XGENE   ", 2, 4, MCFG_BUS_ANY,
> +		&xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
> +	{"APM   ", "XGENE   ", 3, 0, MCFG_BUS_ANY,
> +		&xgene_v2_1_pcie_ecam_ops, MCFG_RES_EMPTY},
> +	{"APM   ", "XGENE   ", 3, 1, MCFG_BUS_ANY,
> +		&xgene_v2_1_pcie_ecam_ops, MCFG_RES_EMPTY},
> +	{"APM   ", "XGENE   ", 4, 0, MCFG_BUS_ANY,
> +		&xgene_v2_2_pcie_ecam_ops, MCFG_RES_EMPTY},
> +	{"APM   ", "XGENE   ", 4, 1, MCFG_BUS_ANY,
> +		&xgene_v2_2_pcie_ecam_ops, MCFG_RES_EMPTY},
> +	{"APM   ", "XGENE   ", 4, 2, MCFG_BUS_ANY,
> +		&xgene_v2_2_pcie_ecam_ops, MCFG_RES_EMPTY},

Most of these are the same.  Let's add a macro that fills in the
boilerplate so each entry only contains the variable parts.  I'm going
to propose the same for the ThunderX quirks.

> +#endif
>  };
>  
>  static char mcfg_oem_id[ACPI_OEM_ID_SIZE];
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index 8843410..af4f505 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -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_PCI_XGENE) += pci-xgene.o pci-xgene-ecam.o
>  obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-ecam.c b/drivers/pci/host/pci-xgene-ecam.c
> new file mode 100644
> index 0000000..b66a04f
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-ecam.c
> @@ -0,0 +1,280 @@
> +/*
> + * APM X-Gene PCIe ECAM fixup driver
> + *
> + * Copyright (c) 2016, Applied Micro Circuits Corporation
> + * Author:
> + *	Duc Dang <dhdang@apm.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_pci.h>
> +#include <linux/pci-acpi.h>
> +#include <linux/platform_device.h>
> +#include <linux/pci-ecam.h>
> +
> +#ifdef CONFIG_ACPI
> +#define RTDID			0x160
> +#define ROOT_CAP_AND_CTRL	0x5C
> +
> +/* PCIe IP version */
> +#define XGENE_PCIE_IP_VER_UNKN	0
> +#define XGENE_PCIE_IP_VER_1	1
> +#define XGENE_PCIE_IP_VER_2	2

We only use XGENE_PCIE_IP_VER_1, so I think the others should be
removed.  I think it would be nicer to have a "crs_broken:1" bit set
by the probe path, and get rid of the version field altogether.

> +#define XGENE_CSR_LENGTH	0x10000
> +
> +struct xgene_pcie_acpi_root {
> +	void __iomem *csr_base;
> +	u32 version;
> +};

I think this should be folded into struct xgene_pcie_port so we don't
have to allocate and manage it separately.

> +static int xgene_v1_pcie_ecam_init(struct pci_config_window *cfg)
> +{
> +	struct xgene_pcie_acpi_root *xgene_root;
> +	struct device *dev = cfg->parent;
> +	u32 csr_base;
> +
> +	xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL);
> +	if (!xgene_root)
> +		return -ENOMEM;
> +
> +	switch (cfg->res.start) {
> +	case 0xE0D0000000ULL:
> +		csr_base = 0x1F2B0000;
> +		break;
> +	case 0xD0D0000000ULL:
> +		csr_base = 0x1F2C0000;
> +		break;
> +	case 0x90D0000000ULL:
> +		csr_base = 0x1F2D0000;
> +		break;
> +	case 0xA0D0000000ULL:
> +		csr_base = 0x1F500000;
> +		break;
> +	case 0xC0D0000000ULL:
> +		csr_base = 0x1F510000;
> +		break;

Ugh.  What in the world is going on here?  Apparently we're testing a
host bridge resource against this hard-coded list of random values,
and based on that, we know about this *other* list of hard-coded CSR
ranges?  This is not the way resource discovery normally works ;)

> +	default:
> +		return -ENODEV;
> +	}
> +
> +	xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH);

There should be a request_region() somewhere, too.  Ideal would be to
use devm_ioremap_resource(), but I don't know where this apparent
resource is coming from.

> +	if (!xgene_root->csr_base) {
> +		kfree(xgene_root);
> +		return -ENODEV;
> +	}
> +
> +	xgene_root->version = XGENE_PCIE_IP_VER_1;
> +
> +	cfg->priv = xgene_root;
> +
> +	return 0;
> +}
> +
> +static int xgene_v2_1_pcie_ecam_init(struct pci_config_window *cfg)
> +{
> +	struct xgene_pcie_acpi_root *xgene_root;
> +	struct device *dev = cfg->parent;
> +	resource_size_t csr_base;
> +
> +	xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL);
> +	if (!xgene_root)
> +		return -ENOMEM;
> +
> +	switch (cfg->res.start) {
> +	case 0xC0D0000000ULL:
> +		csr_base = 0x1F2B0000;
> +		break;
> +	case 0xA0D0000000ULL:
> +		csr_base = 0x1F2C0000;
> +		break;
> +	default:
> +		return -ENODEV;
> +	}
> +
> +	xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH);
> +	if (!xgene_root->csr_base) {
> +		kfree(xgene_root);
> +		return -ENODEV;
> +	}
> +
> +	xgene_root->version = XGENE_PCIE_IP_VER_2;
> +
> +	cfg->priv = xgene_root;
> +
> +	return 0;
> +}
> +
> +static int xgene_v2_2_pcie_ecam_init(struct pci_config_window *cfg)
> +{
> +	struct xgene_pcie_acpi_root *xgene_root;
> +	struct device *dev = cfg->parent;
> +	resource_size_t csr_base;
> +
> +	xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL);
> +	if (!xgene_root)
> +		return -ENOMEM;
> +
> +	switch (cfg->res.start) {
> +	case 0xE0D0000000ULL:
> +		csr_base = 0x1F2B0000;
> +		break;
> +	case 0xA0D0000000ULL:
> +		csr_base = 0x1F500000;
> +		break;
> +	case 0x90D0000000ULL:
> +		csr_base = 0x1F2D0000;
> +		break;
> +	default:
> +		return -ENODEV;
> +	}
> +
> +	xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH);
> +	if (!xgene_root->csr_base) {
> +		kfree(xgene_root);
> +		return -ENODEV;
> +	}
> +
> +	xgene_root->version = XGENE_PCIE_IP_VER_2;
> +
> +	cfg->priv = xgene_root;
> +
> +	return 0;
> +}
> +/*
> + * For Configuration request, RTDID register is used as Bus Number,
> + * Device Number and Function number of the header fields.
> + */
> +static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
> +{
> +	struct pci_config_window *cfg = bus->sysdata;
> +	struct xgene_pcie_acpi_root *port = cfg->priv;
> +	unsigned int b, d, f;
> +	u32 rtdid_val = 0;
> +
> +	b = bus->number;
> +	d = PCI_SLOT(devfn);
> +	f = PCI_FUNC(devfn);
> +
> +	if (!pci_is_root_bus(bus))
> +		rtdid_val = (b << 8) | (d << 3) | f;
> +
> +	writel(rtdid_val, port->csr_base + RTDID);
> +	/* read the register back to ensure flush */
> +	readl(port->csr_base + RTDID);
> +}
> +
> +/*
> + * X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as
> + * the translation from PCI bus to native BUS.  Entire DDR region
> + * is mapped into PCIe space using these registers, so it can be
> + * reached by DMA from EP devices.  The BAR0/1 of bridge should be
> + * hidden during enumeration to avoid the sizing and resource allocation
> + * by PCIe core.
> + */
> +static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset)
> +{
> +	if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) ||
> +				     (offset == PCI_BASE_ADDRESS_1)))
> +		return true;
> +
> +	return false;
> +}
> +
> +void __iomem *xgene_pcie_ecam_map_bus(struct pci_bus *bus,
> +				      unsigned int devfn, int where)
> +{
> +	struct pci_config_window *cfg = bus->sysdata;
> +	unsigned int busn = bus->number;
> +	void __iomem *base;
> +
> +	if (busn < cfg->busr.start || busn > cfg->busr.end)
> +		return NULL;
> +
> +	if ((pci_is_root_bus(bus) && devfn != 0) ||
> +	    xgene_pcie_hide_rc_bars(bus, where))
> +		return NULL;
> +
> +	xgene_pcie_set_rtdid_reg(bus, devfn);
> +
> +	if (busn > cfg->busr.start)
> +		base = cfg->win + (1 << cfg->ops->bus_shift);
> +	else
> +		base = cfg->win;
> +
> +	return base + where;
> +}
> +
> +static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
> +				    int where, int size, u32 *val)
> +{
> +	struct pci_config_window *cfg = bus->sysdata;
> +	struct xgene_pcie_acpi_root *port = cfg->priv;
> +
> +	if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
> +	    PCIBIOS_SUCCESSFUL)
> +		return PCIBIOS_DEVICE_NOT_FOUND;
> +
> +	/*
> +	* The v1 controller has a bug in its Configuration Request
> +	* Retry Status (CRS) logic: when CRS is enabled and we read the
> +	* Vendor and Device ID of a non-existent device, the controller
> +	* fabricates return data of 0xFFFF0001 ("device exists but is not
> +	* ready") instead of 0xFFFFFFFF ("device does not exist").  This
> +	* causes the PCI core to retry the read until it times out.
> +	* Avoid this by not claiming to support CRS.
> +	*/
> +	if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) &&
> +	    ((where & ~0x3) == ROOT_CAP_AND_CTRL))
> +		*val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
> +
> +	if (size <= 2)
> +		*val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
> +
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +struct pci_ecam_ops xgene_v1_pcie_ecam_ops = {
> +	.bus_shift	= 16,
> +	.init		= xgene_v1_pcie_ecam_init,
> +	.pci_ops	= {
> +		.map_bus	= xgene_pcie_ecam_map_bus,
> +		.read		= xgene_pcie_config_read32,
> +		.write		= pci_generic_config_write,
> +	}
> +};
> +
> +struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops = {
> +	.bus_shift	= 16,
> +	.init		= xgene_v2_1_pcie_ecam_init,
> +	.pci_ops	= {
> +		.map_bus	= xgene_pcie_ecam_map_bus,
> +		.read		= xgene_pcie_config_read32,
> +		.write		= pci_generic_config_write,
> +	}
> +};
> +
> +struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops = {
> +	.bus_shift	= 16,
> +	.init		= xgene_v2_2_pcie_ecam_init,
> +	.pci_ops	= {
> +		.map_bus	= xgene_pcie_ecam_map_bus,
> +		.read		= xgene_pcie_config_read32,
> +		.write		= pci_generic_config_write,
> +	}
> +};
> +#endif
> diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h
> index 35f0e81..40da3e7 100644
> --- a/include/linux/pci-ecam.h
> +++ b/include/linux/pci-ecam.h
> @@ -65,6 +65,11 @@ extern struct pci_ecam_ops pci_thunder_pem_ops;
>  #ifdef CONFIG_PCI_HOST_THUNDER_ECAM
>  extern struct pci_ecam_ops pci_thunder_ecam_ops;
>  #endif
> +#ifdef CONFIG_PCI_XGENE
> +extern struct pci_ecam_ops xgene_v1_pcie_ecam_ops;
> +extern struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops;
> +extern struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops;
> +#endif
>  
>  #ifdef CONFIG_PCI_HOST_GENERIC
>  /* for DT-based PCI controllers that support ECAM */
> -- 
> 1.9.1
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH] PCI/ACPI: xgene: Add ECAM quirk for X-Gene PCIe controller
  2016-09-19 20:06 ` Bjorn Helgaas
@ 2016-09-20  1:07   ` Duc Dang
  2016-09-21 21:22     ` Bjorn Helgaas
  0 siblings, 1 reply; 9+ messages in thread
From: Duc Dang @ 2016-09-20  1:07 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: Rafael Wysocki, Lorenzo Pieralisi, Arnd Bergmann, Mark Salter,
	linux-pci, linux-arm, Linux Kernel Mailing List, Jon Masters,
	Tomasz Nowicki, patches

Hi Bjorn,

Thanks for reviewing my RFC patch.

On Mon, Sep 19, 2016 at 1:06 PM, Bjorn Helgaas <helgaas@kernel.org> wrote:
> Hi Duc,
>
> On Sat, Sep 17, 2016 at 07:24:38AM -0700, Duc Dang wrote:
>> PCIe controller in X-Gene SoCs is not ECAM compliant: software
>> needs to configure additional concontroller register to address
>> device at bus:dev:function.
>>
>> This patch depends on "ECAM quirks handling for ARM64 platforms"
>> series (http://www.spinics.net/lists/arm-kernel/msg530692.html)
>> to address the limitation above for X-Gene PCIe controller.
>>
>> 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).
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> ---
>>  drivers/acpi/pci_mcfg.c           |  32 +++++
>>  drivers/pci/host/Makefile         |   2 +-
>>  drivers/pci/host/pci-xgene-ecam.c | 280 ++++++++++++++++++++++++++++++++++++++
>>  include/linux/pci-ecam.h          |   5 +
>>  4 files changed, 318 insertions(+), 1 deletion(-)
>>  create mode 100644 drivers/pci/host/pci-xgene-ecam.c
>
> This adds a bunch of stuff, but doesn't remove anything.  So I assume
> it's adding new functionality that didn't exist before.  What is it?

This patch only adds the ability for X-Gene PCIe controller to be
probable in ACPI boot mode. All the 'quirk' code added is for ECAM to
work with X-Gene.

>
> I sort of expected this to also remove, for example, the seemingly
> identical xgene_pcie_config_read32() in drivers/pci/host/pci-xgene.c.
> Actually, a bunch of this code seems to be duplicated from there.  It
> doesn't seem like we should end up with all this duplicated code.
>
> I'd really like it better if all this could get folded into
> pci-xgene.c so we don't end up with more files.

I am still debating whether to put this X-Gene ECAM quirk code into
drivers/acpi or keep it here in drivers/pci/host. But given the
direction of other quirk patches for ThunderX and HiSi, seem like the
quirk will stay in drivers/pci/host. I can definitely fold the new
quirk code into pci-xgene.c as you suggested and eliminate the
identical one.

>
>> diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
>> index ddf338b..adce35f 100644
>> --- a/drivers/acpi/pci_mcfg.c
>> +++ b/drivers/acpi/pci_mcfg.c
>> @@ -123,6 +123,38 @@ static struct mcfg_fixup mcfg_quirks[] = {
>>       { "CAVIUM", "THUNDERX", 2, 13, MCFG_BUS_ANY, &pci_thunder_ecam_ops,
>>         MCFG_RES_EMPTY},
>>  #endif
>> +#ifdef CONFIG_PCI_XGENE
>> +     {"APM   ", "XGENE   ", 1, 0, MCFG_BUS_ANY,
>> +             &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
>> +     {"APM   ", "XGENE   ", 1, 1, MCFG_BUS_ANY,
>> +             &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
>> +     {"APM   ", "XGENE   ", 1, 2, MCFG_BUS_ANY,
>> +             &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
>> +     {"APM   ", "XGENE   ", 1, 3, MCFG_BUS_ANY,
>> +             &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
>> +     {"APM   ", "XGENE   ", 1, 4, MCFG_BUS_ANY,
>> +             &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
>> +     {"APM   ", "XGENE   ", 2, 0, MCFG_BUS_ANY,
>> +             &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
>> +     {"APM   ", "XGENE   ", 2, 1, MCFG_BUS_ANY,
>> +             &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
>> +     {"APM   ", "XGENE   ", 2, 2, MCFG_BUS_ANY,
>> +             &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
>> +     {"APM   ", "XGENE   ", 2, 3, MCFG_BUS_ANY,
>> +             &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
>> +     {"APM   ", "XGENE   ", 2, 4, MCFG_BUS_ANY,
>> +             &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY},
>> +     {"APM   ", "XGENE   ", 3, 0, MCFG_BUS_ANY,
>> +             &xgene_v2_1_pcie_ecam_ops, MCFG_RES_EMPTY},
>> +     {"APM   ", "XGENE   ", 3, 1, MCFG_BUS_ANY,
>> +             &xgene_v2_1_pcie_ecam_ops, MCFG_RES_EMPTY},
>> +     {"APM   ", "XGENE   ", 4, 0, MCFG_BUS_ANY,
>> +             &xgene_v2_2_pcie_ecam_ops, MCFG_RES_EMPTY},
>> +     {"APM   ", "XGENE   ", 4, 1, MCFG_BUS_ANY,
>> +             &xgene_v2_2_pcie_ecam_ops, MCFG_RES_EMPTY},
>> +     {"APM   ", "XGENE   ", 4, 2, MCFG_BUS_ANY,
>> +             &xgene_v2_2_pcie_ecam_ops, MCFG_RES_EMPTY},
>
> Most of these are the same.  Let's add a macro that fills in the
> boilerplate so each entry only contains the variable parts.  I'm going
> to propose the same for the ThunderX quirks.

I will follow up on this.

>
>> +#endif
>>  };
>>
>>  static char mcfg_oem_id[ACPI_OEM_ID_SIZE];
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index 8843410..af4f505 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -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_PCI_XGENE) += pci-xgene.o pci-xgene-ecam.o
>>  obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-ecam.c b/drivers/pci/host/pci-xgene-ecam.c
>> new file mode 100644
>> index 0000000..b66a04f
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-ecam.c
>> @@ -0,0 +1,280 @@
>> +/*
>> + * APM X-Gene PCIe ECAM fixup driver
>> + *
>> + * Copyright (c) 2016, Applied Micro Circuits Corporation
>> + * Author:
>> + *   Duc Dang <dhdang@apm.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_pci.h>
>> +#include <linux/pci-acpi.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pci-ecam.h>
>> +
>> +#ifdef CONFIG_ACPI
>> +#define RTDID                        0x160
>> +#define ROOT_CAP_AND_CTRL    0x5C
>> +
>> +/* PCIe IP version */
>> +#define XGENE_PCIE_IP_VER_UNKN       0
>> +#define XGENE_PCIE_IP_VER_1  1
>> +#define XGENE_PCIE_IP_VER_2  2
>
> We only use XGENE_PCIE_IP_VER_1, so I think the others should be
> removed.  I think it would be nicer to have a "crs_broken:1" bit set
> by the probe path, and get rid of the version field altogether.

We do use XGENE_PCIE_IP_VER_2 for X-Gene v2 PCIe controller, but your
idea about using crs_broken is much better. I will make change
following that. This will affect DT booting too.

>
>> +#define XGENE_CSR_LENGTH     0x10000
>> +
>> +struct xgene_pcie_acpi_root {
>> +     void __iomem *csr_base;
>> +     u32 version;
>> +};
>
> I think this should be folded into struct xgene_pcie_port so we don't
> have to allocate and manage it separately.

I will need to look into this more. When booting with ACPI mode, the
code in pci-xgene.c is not used (except the cfg read/write functions
that are shared with ECAM quirk code), so puting these into
xgene_pcie_port will force ECAM quirk code to allocate this structure
as well.

>
>> +static int xgene_v1_pcie_ecam_init(struct pci_config_window *cfg)
>> +{
>> +     struct xgene_pcie_acpi_root *xgene_root;
>> +     struct device *dev = cfg->parent;
>> +     u32 csr_base;
>> +
>> +     xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL);
>> +     if (!xgene_root)
>> +             return -ENOMEM;
>> +
>> +     switch (cfg->res.start) {
>> +     case 0xE0D0000000ULL:
>> +             csr_base = 0x1F2B0000;
>> +             break;
>> +     case 0xD0D0000000ULL:
>> +             csr_base = 0x1F2C0000;
>> +             break;
>> +     case 0x90D0000000ULL:
>> +             csr_base = 0x1F2D0000;
>> +             break;
>> +     case 0xA0D0000000ULL:
>> +             csr_base = 0x1F500000;
>> +             break;
>> +     case 0xC0D0000000ULL:
>> +             csr_base = 0x1F510000;
>> +             break;
>
> Ugh.  What in the world is going on here?  Apparently we're testing a
> host bridge resource against this hard-coded list of random values,
> and based on that, we know about this *other* list of hard-coded CSR
> ranges?  This is not the way resource discovery normally works ;)

Yes, this is very ugly :(

But discovering CSR regions by using acpi_walk_resource for each root
port is not a preferred solution because of the concern that it may
open a backdoor for continuously abusive usage of the quirk for new
version of SoCs (https://patchwork.kernel.org/patch/9178791/, last 2
comments from Lorenzo and Ard)

>
>> +     default:
>> +             return -ENODEV;
>> +     }
>> +
>> +     xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH);
>
> There should be a request_region() somewhere, too.  Ideal would be to
> use devm_ioremap_resource(), but I don't know where this apparent
> resource is coming from.

Yes, I will use request_region/devm_ioremap_resource here.

>
>> +     if (!xgene_root->csr_base) {
>> +             kfree(xgene_root);
>> +             return -ENODEV;
>> +     }
>> +
>> +     xgene_root->version = XGENE_PCIE_IP_VER_1;
>> +
>> +     cfg->priv = xgene_root;
>> +
>> +     return 0;
>> +}
>> +
>> +static int xgene_v2_1_pcie_ecam_init(struct pci_config_window *cfg)
>> +{
>> +     struct xgene_pcie_acpi_root *xgene_root;
>> +     struct device *dev = cfg->parent;
>> +     resource_size_t csr_base;
>> +
>> +     xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL);
>> +     if (!xgene_root)
>> +             return -ENOMEM;
>> +
>> +     switch (cfg->res.start) {
>> +     case 0xC0D0000000ULL:
>> +             csr_base = 0x1F2B0000;
>> +             break;
>> +     case 0xA0D0000000ULL:
>> +             csr_base = 0x1F2C0000;
>> +             break;
>> +     default:
>> +             return -ENODEV;
>> +     }
>> +
>> +     xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH);
>> +     if (!xgene_root->csr_base) {
>> +             kfree(xgene_root);
>> +             return -ENODEV;
>> +     }
>> +
>> +     xgene_root->version = XGENE_PCIE_IP_VER_2;
>> +
>> +     cfg->priv = xgene_root;
>> +
>> +     return 0;
>> +}
>> +
>> +static int xgene_v2_2_pcie_ecam_init(struct pci_config_window *cfg)
>> +{
>> +     struct xgene_pcie_acpi_root *xgene_root;
>> +     struct device *dev = cfg->parent;
>> +     resource_size_t csr_base;
>> +
>> +     xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL);
>> +     if (!xgene_root)
>> +             return -ENOMEM;
>> +
>> +     switch (cfg->res.start) {
>> +     case 0xE0D0000000ULL:
>> +             csr_base = 0x1F2B0000;
>> +             break;
>> +     case 0xA0D0000000ULL:
>> +             csr_base = 0x1F500000;
>> +             break;
>> +     case 0x90D0000000ULL:
>> +             csr_base = 0x1F2D0000;
>> +             break;
>> +     default:
>> +             return -ENODEV;
>> +     }
>> +
>> +     xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH);
>> +     if (!xgene_root->csr_base) {
>> +             kfree(xgene_root);
>> +             return -ENODEV;
>> +     }
>> +
>> +     xgene_root->version = XGENE_PCIE_IP_VER_2;
>> +
>> +     cfg->priv = xgene_root;
>> +
>> +     return 0;
>> +}
>> +/*
>> + * For Configuration request, RTDID register is used as Bus Number,
>> + * Device Number and Function number of the header fields.
>> + */
>> +static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
>> +{
>> +     struct pci_config_window *cfg = bus->sysdata;
>> +     struct xgene_pcie_acpi_root *port = cfg->priv;
>> +     unsigned int b, d, f;
>> +     u32 rtdid_val = 0;
>> +
>> +     b = bus->number;
>> +     d = PCI_SLOT(devfn);
>> +     f = PCI_FUNC(devfn);
>> +
>> +     if (!pci_is_root_bus(bus))
>> +             rtdid_val = (b << 8) | (d << 3) | f;
>> +
>> +     writel(rtdid_val, port->csr_base + RTDID);
>> +     /* read the register back to ensure flush */
>> +     readl(port->csr_base + RTDID);
>> +}
>> +
>> +/*
>> + * X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as
>> + * the translation from PCI bus to native BUS.  Entire DDR region
>> + * is mapped into PCIe space using these registers, so it can be
>> + * reached by DMA from EP devices.  The BAR0/1 of bridge should be
>> + * hidden during enumeration to avoid the sizing and resource allocation
>> + * by PCIe core.
>> + */
>> +static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset)
>> +{
>> +     if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) ||
>> +                                  (offset == PCI_BASE_ADDRESS_1)))
>> +             return true;
>> +
>> +     return false;
>> +}
>> +
>> +void __iomem *xgene_pcie_ecam_map_bus(struct pci_bus *bus,
>> +                                   unsigned int devfn, int where)
>> +{
>> +     struct pci_config_window *cfg = bus->sysdata;
>> +     unsigned int busn = bus->number;
>> +     void __iomem *base;
>> +
>> +     if (busn < cfg->busr.start || busn > cfg->busr.end)
>> +             return NULL;
>> +
>> +     if ((pci_is_root_bus(bus) && devfn != 0) ||
>> +         xgene_pcie_hide_rc_bars(bus, where))
>> +             return NULL;
>> +
>> +     xgene_pcie_set_rtdid_reg(bus, devfn);
>> +
>> +     if (busn > cfg->busr.start)
>> +             base = cfg->win + (1 << cfg->ops->bus_shift);
>> +     else
>> +             base = cfg->win;
>> +
>> +     return base + where;
>> +}
>> +
>> +static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
>> +                                 int where, int size, u32 *val)
>> +{
>> +     struct pci_config_window *cfg = bus->sysdata;
>> +     struct xgene_pcie_acpi_root *port = cfg->priv;
>> +
>> +     if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
>> +         PCIBIOS_SUCCESSFUL)
>> +             return PCIBIOS_DEVICE_NOT_FOUND;
>> +
>> +     /*
>> +     * The v1 controller has a bug in its Configuration Request
>> +     * Retry Status (CRS) logic: when CRS is enabled and we read the
>> +     * Vendor and Device ID of a non-existent device, the controller
>> +     * fabricates return data of 0xFFFF0001 ("device exists but is not
>> +     * ready") instead of 0xFFFFFFFF ("device does not exist").  This
>> +     * causes the PCI core to retry the read until it times out.
>> +     * Avoid this by not claiming to support CRS.
>> +     */
>> +     if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) &&
>> +         ((where & ~0x3) == ROOT_CAP_AND_CTRL))
>> +             *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
>> +
>> +     if (size <= 2)
>> +             *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
>> +
>> +     return PCIBIOS_SUCCESSFUL;
>> +}
>> +
>> +struct pci_ecam_ops xgene_v1_pcie_ecam_ops = {
>> +     .bus_shift      = 16,
>> +     .init           = xgene_v1_pcie_ecam_init,
>> +     .pci_ops        = {
>> +             .map_bus        = xgene_pcie_ecam_map_bus,
>> +             .read           = xgene_pcie_config_read32,
>> +             .write          = pci_generic_config_write,
>> +     }
>> +};
>> +
>> +struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops = {
>> +     .bus_shift      = 16,
>> +     .init           = xgene_v2_1_pcie_ecam_init,
>> +     .pci_ops        = {
>> +             .map_bus        = xgene_pcie_ecam_map_bus,
>> +             .read           = xgene_pcie_config_read32,
>> +             .write          = pci_generic_config_write,
>> +     }
>> +};
>> +
>> +struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops = {
>> +     .bus_shift      = 16,
>> +     .init           = xgene_v2_2_pcie_ecam_init,
>> +     .pci_ops        = {
>> +             .map_bus        = xgene_pcie_ecam_map_bus,
>> +             .read           = xgene_pcie_config_read32,
>> +             .write          = pci_generic_config_write,
>> +     }
>> +};
>> +#endif
>> diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h
>> index 35f0e81..40da3e7 100644
>> --- a/include/linux/pci-ecam.h
>> +++ b/include/linux/pci-ecam.h
>> @@ -65,6 +65,11 @@ extern struct pci_ecam_ops pci_thunder_pem_ops;
>>  #ifdef CONFIG_PCI_HOST_THUNDER_ECAM
>>  extern struct pci_ecam_ops pci_thunder_ecam_ops;
>>  #endif
>> +#ifdef CONFIG_PCI_XGENE
>> +extern struct pci_ecam_ops xgene_v1_pcie_ecam_ops;
>> +extern struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops;
>> +extern struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops;
>> +#endif
>>
>>  #ifdef CONFIG_PCI_HOST_GENERIC
>>  /* for DT-based PCI controllers that support ECAM */
>> --
>> 1.9.1
>>
Regards,
Duc Dang.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH] PCI/ACPI: xgene: Add ECAM quirk for X-Gene PCIe controller
  2016-09-20  1:07   ` Duc Dang
@ 2016-09-21 21:22     ` Bjorn Helgaas
  2016-10-26  1:24       ` [RFC PATCH v2 1/1] " Duc Dang
  2016-10-26  1:31       ` [RFC PATCH] " Duc Dang
  0 siblings, 2 replies; 9+ messages in thread
From: Bjorn Helgaas @ 2016-09-21 21:22 UTC (permalink / raw)
  To: Duc Dang
  Cc: Rafael Wysocki, Lorenzo Pieralisi, Arnd Bergmann, Mark Salter,
	linux-pci, linux-arm, Linux Kernel Mailing List, Jon Masters,
	Tomasz Nowicki, patches

On Mon, Sep 19, 2016 at 06:07:37PM -0700, Duc Dang wrote:
> On Mon, Sep 19, 2016 at 1:06 PM, Bjorn Helgaas <helgaas@kernel.org> wrote:
> > On Sat, Sep 17, 2016 at 07:24:38AM -0700, Duc Dang wrote:

> This patch only adds the ability for X-Gene PCIe controller to be
> probable in ACPI boot mode. All the 'quirk' code added is for ECAM to
> work with X-Gene.
>
> > I sort of expected this to also remove, for example, the seemingly
> > identical xgene_pcie_config_read32() in drivers/pci/host/pci-xgene.c.
> > Actually, a bunch of this code seems to be duplicated from there.  It
> > doesn't seem like we should end up with all this duplicated code.
> >
> > I'd really like it better if all this could get folded into
> > pci-xgene.c so we don't end up with more files.
> 
> I am still debating whether to put this X-Gene ECAM quirk code into
> drivers/acpi or keep it here in drivers/pci/host. But given the
> direction of other quirk patches for ThunderX and HiSi, seem like the
> quirk will stay in drivers/pci/host. I can definitely fold the new
> quirk code into pci-xgene.c as you suggested and eliminate the
> identical one.

I like Tomasz's patches, where the MCFG quirk itself is in
acpi/pci_mcfg.c, and it uses config accessors exported from
drivers/pci/host.

I do not want to end up with duplicate accessors.  The mapping
functions and accessors should be the same whether we're booting with
DT or ACPI.

I think a patch to add ACPI support should only contain:

  - acpi/pci_mcfg.c quirks to fix incorrect ACPI MCFG resources or use
    special accessors,

  - pnp/quirks.c quirks to compensate for missing ACPI _CRS for the
    ECAM regions, and

  - pci-xgene.c code to derive the csr_base and cfg_base.  Today we
    get that from DT, but the _CRS producer/consumer mess means we
    don't have a good way to get it from ACPI, so you'll need some
    sort of quirk for this.

> >> +struct xgene_pcie_acpi_root {
> >> +     void __iomem *csr_base;
> >> +     u32 version;
> >> +};
> >
> > I think this should be folded into struct xgene_pcie_port so we don't
> > have to allocate and manage it separately.
> 
> I will need to look into this more. When booting with ACPI mode, the
> code in pci-xgene.c is not used (except the cfg read/write functions
> that are shared with ECAM quirk code), so puting these into
> xgene_pcie_port will force ECAM quirk code to allocate this structure
> as well.

This information is needed whether booting with DT or ACPI, so we
should use the existing xgene_pcie_port.csr_base and initialize it
differently depending on which we're using.

> >> +     default:
> >> +             return -ENODEV;
> >> +     }
> >> +
> >> +     xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH);
> >
> > There should be a request_region() somewhere, too.  Ideal would be to
> > use devm_ioremap_resource(), but I don't know where this apparent
> > resource is coming from.
> 
> Yes, I will use request_region/devm_ioremap_resource here.

We're not *adding* any new resources that need ioremapping; all we're
doing is changing the *source* of the resource, so we should use the
same devm_ioremap_resource() you already have in xgene_pcie_map_reg().
You might have to refactor that slightly so we can lookup the resource
via either DT or ACPI (you'll probably actually use a quirk since ACPI
doesn't have a good mechanism for this), and then use the same call to
devm_ioremap_resource().

Bjorn

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH v2 1/1] PCI/ACPI: xgene: Add ECAM quirk for X-Gene PCIe controller
  2016-09-21 21:22     ` Bjorn Helgaas
@ 2016-10-26  1:24       ` Duc Dang
  2016-11-02 16:53         ` Bjorn Helgaas
  2016-10-26  1:31       ` [RFC PATCH] " Duc Dang
  1 sibling, 1 reply; 9+ messages in thread
From: Duc Dang @ 2016-10-26  1:24 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: Rafael Wysocki, Lorenzo Pieralisi, Arnd Bergmann, Mark Salter,
	linux-pci, linux-arm, Linux Kernel Mailing List, Jon Masters,
	Tomasz Nowicki, patches, Duc Dang

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

This patch depends on "ECAM quirks handling for ARM64 platforms"
series (http://www.spinics.net/lists/arm-kernel/msg530692.html,
the series was also modified by Bjorn) to address the limitation
above for X-Gene PCIe controller.

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).

Signed-off-by: Duc Dang <dhdang@apm.com>
---
v2 changes:
	1. Get rid of pci-xgene-ecam.c file and fold quirk code into pci-xgene.c
	2. Redefine fixup array for X-Gene
	3. Use devm_ioremap_resource to map csr_base

 drivers/acpi/pci_mcfg.c      |  30 ++++++++
 drivers/pci/host/pci-xgene.c | 165 ++++++++++++++++++++++++++++++++++++++++++-
 include/linux/pci-ecam.h     |   5 ++
 3 files changed, 197 insertions(+), 3 deletions(-)

diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
index bb2c508..9dfc937 100644
--- a/drivers/acpi/pci_mcfg.c
+++ b/drivers/acpi/pci_mcfg.c
@@ -96,6 +96,36 @@ struct mcfg_fixup {
 	THUNDER_ECAM_MCFG(2, 12),
 	THUNDER_ECAM_MCFG(2, 13),
 #endif
+#ifdef CONFIG_PCI_XGENE
+#define XGENE_V1_ECAM_MCFG(rev, seg) \
+	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
+		&xgene_v1_pcie_ecam_ops }
+#define XGENE_V2_1_ECAM_MCFG(rev, seg) \
+	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
+		&xgene_v2_1_pcie_ecam_ops }
+#define XGENE_V2_2_ECAM_MCFG(rev, seg) \
+	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
+		&xgene_v2_2_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_1_ECAM_MCFG(3, 0),
+	XGENE_V2_1_ECAM_MCFG(3, 1),
+	/* X-Gene SoC with v2.2 PCIe controller */
+	XGENE_V2_2_ECAM_MCFG(4, 0),
+	XGENE_V2_2_ECAM_MCFG(4, 1),
+	XGENE_V2_2_ECAM_MCFG(4, 2),
+#endif
 };
 
 static char mcfg_oem_id[ACPI_OEM_ID_SIZE];
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 1de23d7..d6aa642 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -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,6 +66,7 @@
 /* PCIe IP version */
 #define XGENE_PCIE_IP_VER_UNKN		0
 #define XGENE_PCIE_IP_VER_1		1
+#define XGENE_PCIE_IP_VER_2		2
 
 struct xgene_pcie_port {
 	struct device_node	*node;
@@ -97,7 +100,15 @@ static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
  */
 static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
 {
-	struct xgene_pcie_port *port = bus->sysdata;
+	struct pci_config_window *cfg;
+	struct xgene_pcie_port *port;
+
+	if (acpi_disabled)
+		port = bus->sysdata;
+	else {
+		cfg = bus->sysdata;
+		port = cfg->priv;
+	}
 
 	if (bus->number >= (bus->primary + 1))
 		return port->cfg_base + AXI_EP_CFG_ACCESS;
@@ -111,10 +122,18 @@ 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 pci_config_window *cfg;
+	struct xgene_pcie_port *port;
 	unsigned int b, d, f;
 	u32 rtdid_val = 0;
 
+	if (acpi_disabled)
+		port = bus->sysdata;
+	else {
+		cfg = bus->sysdata;
+		port = cfg->priv;
+	}
+
 	b = bus->number;
 	d = PCI_SLOT(devfn);
 	f = PCI_FUNC(devfn);
@@ -158,7 +177,15 @@ 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 pci_config_window *cfg;
+	struct xgene_pcie_port *port;
+
+	if (acpi_disabled)
+		port = bus->sysdata;
+	else {
+		cfg = bus->sysdata;
+		port = cfg->priv;
+	}
 
 	if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
 	    PCIBIOS_SUCCESSFUL)
@@ -189,6 +216,138 @@ static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
 	.write = pci_generic_config_write32,
 };
 
+#ifdef CONFIG_ACPI
+static struct resource xgene_v1_csr_res[] = {
+	[0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
+	[1] = DEFINE_RES_MEM(0x1f2c0000UL, SZ_64K),
+	[2] = DEFINE_RES_MEM(0x1f2d0000UL, SZ_64K),
+	[3] = DEFINE_RES_MEM(0x1f500000UL, SZ_64K),
+	[4] = DEFINE_RES_MEM(0x1f510000UL, SZ_64K),
+};
+
+static int xgene_v1_pcie_ecam_init(struct pci_config_window *cfg)
+{
+	struct acpi_device *adev = to_acpi_device(cfg->parent);
+	struct acpi_pci_root *root = acpi_driver_data(adev);
+	struct device *dev = cfg->parent;
+	struct xgene_pcie_port *port;
+	struct resource *csr;
+
+	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	csr = &xgene_v1_csr_res[root->segment];
+	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 = XGENE_PCIE_IP_VER_1;
+
+	cfg->priv = port;
+
+	return 0;
+}
+
+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_write,
+	}
+};
+
+static struct resource xgene_v2_1_csr_res[] = {
+	[0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
+	[1] = DEFINE_RES_MEM(0x1f2c0000UL, SZ_64K),
+};
+
+static int xgene_v2_1_pcie_ecam_init(struct pci_config_window *cfg)
+{
+	struct acpi_device *adev = to_acpi_device(cfg->parent);
+	struct acpi_pci_root *root = acpi_driver_data(adev);
+	struct device *dev = cfg->parent;
+	struct xgene_pcie_port *port;
+	struct resource *csr;
+
+	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	csr = &xgene_v2_1_csr_res[root->segment];
+	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 = XGENE_PCIE_IP_VER_2;
+
+	cfg->priv = port;
+
+	return 0;
+}
+
+struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops = {
+	.bus_shift      = 16,
+	.init           = xgene_v2_1_pcie_ecam_init,
+	.pci_ops        = {
+		.map_bus        = xgene_pcie_map_bus,
+		.read           = xgene_pcie_config_read32,
+		.write          = pci_generic_config_write,
+	}
+};
+
+static struct resource xgene_v2_2_csr_res[] = {
+	[0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
+	[1] = DEFINE_RES_MEM(0x1f500000UL, SZ_64K),
+	[2] = DEFINE_RES_MEM(0x1f2d0000UL, SZ_64K),
+};
+
+static int xgene_v2_2_pcie_ecam_init(struct pci_config_window *cfg)
+{
+	struct acpi_device *adev = to_acpi_device(cfg->parent);
+	struct acpi_pci_root *root = acpi_driver_data(adev);
+	struct device *dev = cfg->parent;
+	struct xgene_pcie_port *port;
+	struct resource *csr;
+
+	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	csr = &xgene_v2_2_csr_res[root->segment];
+	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 = XGENE_PCIE_IP_VER_2;
+
+	cfg->priv = port;
+
+	return 0;
+}
+
+struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops = {
+	.bus_shift      = 16,
+	.init           = xgene_v2_2_pcie_ecam_init,
+	.pci_ops        = {
+		.map_bus        = xgene_pcie_map_bus,
+		.read           = xgene_pcie_config_read32,
+		.write          = pci_generic_config_write,
+	}
+};
+#endif
+
 static u64 xgene_pcie_set_ib_mask(struct xgene_pcie_port *port, u32 addr,
 				  u32 flags, u64 size)
 {
diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h
index 35f0e81..40da3e7 100644
--- a/include/linux/pci-ecam.h
+++ b/include/linux/pci-ecam.h
@@ -65,6 +65,11 @@ void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn,
 #ifdef CONFIG_PCI_HOST_THUNDER_ECAM
 extern struct pci_ecam_ops pci_thunder_ecam_ops;
 #endif
+#ifdef CONFIG_PCI_XGENE
+extern struct pci_ecam_ops xgene_v1_pcie_ecam_ops;
+extern struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops;
+extern struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops;
+#endif
 
 #ifdef CONFIG_PCI_HOST_GENERIC
 /* for DT-based PCI controllers that support ECAM */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH] PCI/ACPI: xgene: Add ECAM quirk for X-Gene PCIe controller
  2016-09-21 21:22     ` Bjorn Helgaas
  2016-10-26  1:24       ` [RFC PATCH v2 1/1] " Duc Dang
@ 2016-10-26  1:31       ` Duc Dang
  1 sibling, 0 replies; 9+ messages in thread
From: Duc Dang @ 2016-10-26  1:31 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: Rafael Wysocki, Lorenzo Pieralisi, Arnd Bergmann, Mark Salter,
	linux-pci, linux-arm, Linux Kernel Mailing List, Jon Masters,
	Tomasz Nowicki, patches

On Wed, Sep 21, 2016 at 2:22 PM, Bjorn Helgaas <helgaas@kernel.org> wrote:
> On Mon, Sep 19, 2016 at 06:07:37PM -0700, Duc Dang wrote:
>> On Mon, Sep 19, 2016 at 1:06 PM, Bjorn Helgaas <helgaas@kernel.org> wrote:
>> > On Sat, Sep 17, 2016 at 07:24:38AM -0700, Duc Dang wrote:
>
>> This patch only adds the ability for X-Gene PCIe controller to be
>> probable in ACPI boot mode. All the 'quirk' code added is for ECAM to
>> work with X-Gene.
>>
>> > I sort of expected this to also remove, for example, the seemingly
>> > identical xgene_pcie_config_read32() in drivers/pci/host/pci-xgene.c.
>> > Actually, a bunch of this code seems to be duplicated from there.  It
>> > doesn't seem like we should end up with all this duplicated code.
>> >
>> > I'd really like it better if all this could get folded into
>> > pci-xgene.c so we don't end up with more files.
>>
>> I am still debating whether to put this X-Gene ECAM quirk code into
>> drivers/acpi or keep it here in drivers/pci/host. But given the
>> direction of other quirk patches for ThunderX and HiSi, seem like the
>> quirk will stay in drivers/pci/host. I can definitely fold the new
>> quirk code into pci-xgene.c as you suggested and eliminate the
>> identical one.
>
> I like Tomasz's patches, where the MCFG quirk itself is in
> acpi/pci_mcfg.c, and it uses config accessors exported from
> drivers/pci/host.

Yes, I removed the new file and folded the quirk code into pci-xgene.c.

>
> I do not want to end up with duplicate accessors.  The mapping
> functions and accessors should be the same whether we're booting with
> DT or ACPI.
>
> I think a patch to add ACPI support should only contain:
>
>   - acpi/pci_mcfg.c quirks to fix incorrect ACPI MCFG resources or use
>     special accessors,
>
>   - pnp/quirks.c quirks to compensate for missing ACPI _CRS for the
>     ECAM regions, and
>
>   - pci-xgene.c code to derive the csr_base and cfg_base.  Today we
>     get that from DT, but the _CRS producer/consumer mess means we
>     don't have a good way to get it from ACPI, so you'll need some
>     sort of quirk for this.

The new quirk code (v2 patch) follows this direction, but I have not
found a good way to introduce quirk into pnp/quirks.c yet. Just to
clarify a little bit, our ACPI table provides ECAM base address from
_CBA method. The missing piece is the controller register base address
(csr_base) that I need to get from a hard-coded resource array.

I also owe you the rework for Configuration Request Retry Status
workaround, but it will need to be done in a separate patch set for
both DT and ACPI.
>
>> >> +struct xgene_pcie_acpi_root {
>> >> +     void __iomem *csr_base;
>> >> +     u32 version;
>> >> +};
>> >
>> > I think this should be folded into struct xgene_pcie_port so we don't
>> > have to allocate and manage it separately.
>>
>> I will need to look into this more. When booting with ACPI mode, the
>> code in pci-xgene.c is not used (except the cfg read/write functions
>> that are shared with ECAM quirk code), so puting these into
>> xgene_pcie_port will force ECAM quirk code to allocate this structure
>> as well.
>
> This information is needed whether booting with DT or ACPI, so we
> should use the existing xgene_pcie_port.csr_base and initialize it
> differently depending on which we're using.

The new ECAM quirk code will also allocate struct xgene_pcie_port, I
got rid of xgene_pcie_acpi_root struct.

>
>> >> +     default:
>> >> +             return -ENODEV;
>> >> +     }
>> >> +
>> >> +     xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH);
>> >
>> > There should be a request_region() somewhere, too.  Ideal would be to
>> > use devm_ioremap_resource(), but I don't know where this apparent
>> > resource is coming from.
>>
>> Yes, I will use request_region/devm_ioremap_resource here.
>
> We're not *adding* any new resources that need ioremapping; all we're
> doing is changing the *source* of the resource, so we should use the
> same devm_ioremap_resource() you already have in xgene_pcie_map_reg().
> You might have to refactor that slightly so we can lookup the resource
> via either DT or ACPI (you'll probably actually use a quirk since ACPI
> doesn't have a good mechanism for this), and then use the same call to
> devm_ioremap_resource().

I changed to use devm_ioremap_resource to map the csr_base from the
fixed resource array defined for each X-Gene SoC. The 'cat
/proc/iomem' for PCIe port on Mustang board is like following:

1f2b0000-1f2bffff : PNP0A08:00
e040000000-e07fffffff : PCI Bus 0000:00
  e040000000-e0401fffff : PCI Bus 0000:01
    e040000000-e0400fffff : 0000:01:00.0
      e040000000-e0400fffff : mlx4_core
    e040100000-e0401fffff : 0000:01:00.0
e0d0000000-e0dfffffff : PCI ECAM
f000000000-ffffffffff : PCI Bus 0000:00
  f000000000-f001ffffff : PCI Bus 0000:01
    f000000000-f001ffffff : 0000:01:00.0
      f000000000-f001ffffff : mlx4_core

Is this what you expect? Or you are looking for something else?

Regards,
Duc Dabng.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH v2 1/1] PCI/ACPI: xgene: Add ECAM quirk for X-Gene PCIe controller
  2016-10-26  1:24       ` [RFC PATCH v2 1/1] " Duc Dang
@ 2016-11-02 16:53         ` Bjorn Helgaas
  2016-11-02 20:54           ` Duc Dang
  0 siblings, 1 reply; 9+ messages in thread
From: Bjorn Helgaas @ 2016-11-02 16:53 UTC (permalink / raw)
  To: Duc Dang
  Cc: Rafael Wysocki, Lorenzo Pieralisi, Arnd Bergmann, Mark Salter,
	linux-pci, linux-arm, Linux Kernel Mailing List, Jon Masters,
	Tomasz Nowicki, patches

Hi Duc,

On Tue, Oct 25, 2016 at 06:24:32PM -0700, Duc Dang wrote:
> PCIe controllers in X-Gene SoCs is not ECAM compliant: software
> needs to configure additional controller's register to address
> device at bus:dev:function.
> 
> This patch depends on "ECAM quirks handling for ARM64 platforms"
> series (http://www.spinics.net/lists/arm-kernel/msg530692.html,
> the series was also modified by Bjorn) to address the limitation
> above for X-Gene PCIe controller.
> 
> 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).

The quirks here contain some hard-coded address space consumed by
ECAM.  The ECAM quirk itself is not a generic description of that
address space in the sense of a PCI BAR or an ACPI _CRS method, i.e.,
the quirk description is not enough to keep other parts of the kernel
from treating the address space as "available".

Can you add a note here in the changelog about how you are describing
this space generically?  The standard solution is a PNP0C02 device
with _CRS that describes it.

It would be ideal if you could open a bugzilla at bugzilla.kernel.org
and attach there a dmesg log, /proc/iomem contents, and DSDT.  This
would show both the generic PNP0C02 piece and the ECAM quirk piece.

BTW, I did refresh and re-push the pci/ecam-v6 branch where I'm
collecting this stuff, so if you want to rebase your patch on top of
that and test it, that would be great.

> Signed-off-by: Duc Dang <dhdang@apm.com>
> ---
> v2 changes:
> 	1. Get rid of pci-xgene-ecam.c file and fold quirk code into pci-xgene.c
> 	2. Redefine fixup array for X-Gene
> 	3. Use devm_ioremap_resource to map csr_base
> 
>  drivers/acpi/pci_mcfg.c      |  30 ++++++++
>  drivers/pci/host/pci-xgene.c | 165 ++++++++++++++++++++++++++++++++++++++++++-
>  include/linux/pci-ecam.h     |   5 ++
>  3 files changed, 197 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
> index bb2c508..9dfc937 100644
> --- a/drivers/acpi/pci_mcfg.c
> +++ b/drivers/acpi/pci_mcfg.c
> @@ -96,6 +96,36 @@ struct mcfg_fixup {
>  	THUNDER_ECAM_MCFG(2, 12),
>  	THUNDER_ECAM_MCFG(2, 13),
>  #endif
> +#ifdef CONFIG_PCI_XGENE
> +#define XGENE_V1_ECAM_MCFG(rev, seg) \
> +	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
> +		&xgene_v1_pcie_ecam_ops }
> +#define XGENE_V2_1_ECAM_MCFG(rev, seg) \
> +	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
> +		&xgene_v2_1_pcie_ecam_ops }
> +#define XGENE_V2_2_ECAM_MCFG(rev, seg) \
> +	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
> +		&xgene_v2_2_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_1_ECAM_MCFG(3, 0),
> +	XGENE_V2_1_ECAM_MCFG(3, 1),
> +	/* X-Gene SoC with v2.2 PCIe controller */
> +	XGENE_V2_2_ECAM_MCFG(4, 0),
> +	XGENE_V2_2_ECAM_MCFG(4, 1),
> +	XGENE_V2_2_ECAM_MCFG(4, 2),
> +#endif
>  };
>  
>  static char mcfg_oem_id[ACPI_OEM_ID_SIZE];
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 1de23d7..d6aa642 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -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,6 +66,7 @@
>  /* PCIe IP version */
>  #define XGENE_PCIE_IP_VER_UNKN		0
>  #define XGENE_PCIE_IP_VER_1		1
> +#define XGENE_PCIE_IP_VER_2		2
>  
>  struct xgene_pcie_port {
>  	struct device_node	*node;
> @@ -97,7 +100,15 @@ static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
>   */
>  static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
>  {
> -	struct xgene_pcie_port *port = bus->sysdata;
> +	struct pci_config_window *cfg;
> +	struct xgene_pcie_port *port;
> +
> +	if (acpi_disabled)
> +		port = bus->sysdata;
> +	else {
> +		cfg = bus->sysdata;
> +		port = cfg->priv;
> +	}
>  
>  	if (bus->number >= (bus->primary + 1))
>  		return port->cfg_base + AXI_EP_CFG_ACCESS;
> @@ -111,10 +122,18 @@ 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 pci_config_window *cfg;
> +	struct xgene_pcie_port *port;
>  	unsigned int b, d, f;
>  	u32 rtdid_val = 0;
>  
> +	if (acpi_disabled)
> +		port = bus->sysdata;
> +	else {
> +		cfg = bus->sysdata;
> +		port = cfg->priv;
> +	}
> +
>  	b = bus->number;
>  	d = PCI_SLOT(devfn);
>  	f = PCI_FUNC(devfn);
> @@ -158,7 +177,15 @@ 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 pci_config_window *cfg;
> +	struct xgene_pcie_port *port;
> +
> +	if (acpi_disabled)
> +		port = bus->sysdata;
> +	else {
> +		cfg = bus->sysdata;
> +		port = cfg->priv;
> +	}
>  
>  	if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
>  	    PCIBIOS_SUCCESSFUL)
> @@ -189,6 +216,138 @@ static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
>  	.write = pci_generic_config_write32,
>  };
>  
> +#ifdef CONFIG_ACPI
> +static struct resource xgene_v1_csr_res[] = {
> +	[0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
> +	[1] = DEFINE_RES_MEM(0x1f2c0000UL, SZ_64K),
> +	[2] = DEFINE_RES_MEM(0x1f2d0000UL, SZ_64K),
> +	[3] = DEFINE_RES_MEM(0x1f500000UL, SZ_64K),
> +	[4] = DEFINE_RES_MEM(0x1f510000UL, SZ_64K),
> +};
> +
> +static int xgene_v1_pcie_ecam_init(struct pci_config_window *cfg)
> +{
> +	struct acpi_device *adev = to_acpi_device(cfg->parent);
> +	struct acpi_pci_root *root = acpi_driver_data(adev);
> +	struct device *dev = cfg->parent;
> +	struct xgene_pcie_port *port;
> +	struct resource *csr;
> +
> +	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> +	if (!port)
> +		return -ENOMEM;
> +
> +	csr = &xgene_v1_csr_res[root->segment];
> +	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 = XGENE_PCIE_IP_VER_1;
> +
> +	cfg->priv = port;
> +
> +	return 0;
> +}
> +
> +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_write,
> +	}
> +};
> +
> +static struct resource xgene_v2_1_csr_res[] = {
> +	[0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
> +	[1] = DEFINE_RES_MEM(0x1f2c0000UL, SZ_64K),
> +};
> +
> +static int xgene_v2_1_pcie_ecam_init(struct pci_config_window *cfg)
> +{
> +	struct acpi_device *adev = to_acpi_device(cfg->parent);
> +	struct acpi_pci_root *root = acpi_driver_data(adev);
> +	struct device *dev = cfg->parent;
> +	struct xgene_pcie_port *port;
> +	struct resource *csr;
> +
> +	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> +	if (!port)
> +		return -ENOMEM;
> +
> +	csr = &xgene_v2_1_csr_res[root->segment];
> +	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 = XGENE_PCIE_IP_VER_2;
> +
> +	cfg->priv = port;
> +
> +	return 0;
> +}
> +
> +struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops = {
> +	.bus_shift      = 16,
> +	.init           = xgene_v2_1_pcie_ecam_init,
> +	.pci_ops        = {
> +		.map_bus        = xgene_pcie_map_bus,
> +		.read           = xgene_pcie_config_read32,
> +		.write          = pci_generic_config_write,
> +	}
> +};
> +
> +static struct resource xgene_v2_2_csr_res[] = {
> +	[0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
> +	[1] = DEFINE_RES_MEM(0x1f500000UL, SZ_64K),
> +	[2] = DEFINE_RES_MEM(0x1f2d0000UL, SZ_64K),
> +};
> +
> +static int xgene_v2_2_pcie_ecam_init(struct pci_config_window *cfg)
> +{
> +	struct acpi_device *adev = to_acpi_device(cfg->parent);
> +	struct acpi_pci_root *root = acpi_driver_data(adev);
> +	struct device *dev = cfg->parent;
> +	struct xgene_pcie_port *port;
> +	struct resource *csr;
> +
> +	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> +	if (!port)
> +		return -ENOMEM;
> +
> +	csr = &xgene_v2_2_csr_res[root->segment];
> +	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 = XGENE_PCIE_IP_VER_2;
> +
> +	cfg->priv = port;
> +
> +	return 0;
> +}
> +
> +struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops = {
> +	.bus_shift      = 16,
> +	.init           = xgene_v2_2_pcie_ecam_init,
> +	.pci_ops        = {
> +		.map_bus        = xgene_pcie_map_bus,
> +		.read           = xgene_pcie_config_read32,
> +		.write          = pci_generic_config_write,
> +	}
> +};
> +#endif
> +
>  static u64 xgene_pcie_set_ib_mask(struct xgene_pcie_port *port, u32 addr,
>  				  u32 flags, u64 size)
>  {
> diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h
> index 35f0e81..40da3e7 100644
> --- a/include/linux/pci-ecam.h
> +++ b/include/linux/pci-ecam.h
> @@ -65,6 +65,11 @@ void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn,
>  #ifdef CONFIG_PCI_HOST_THUNDER_ECAM
>  extern struct pci_ecam_ops pci_thunder_ecam_ops;
>  #endif
> +#ifdef CONFIG_PCI_XGENE
> +extern struct pci_ecam_ops xgene_v1_pcie_ecam_ops;
> +extern struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops;
> +extern struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops;
> +#endif
>  
>  #ifdef CONFIG_PCI_HOST_GENERIC
>  /* for DT-based PCI controllers that support ECAM */
> -- 
> 1.9.1
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH v2 1/1] PCI/ACPI: xgene: Add ECAM quirk for X-Gene PCIe controller
  2016-11-02 16:53         ` Bjorn Helgaas
@ 2016-11-02 20:54           ` Duc Dang
  2016-11-07 22:38             ` Bjorn Helgaas
  0 siblings, 1 reply; 9+ messages in thread
From: Duc Dang @ 2016-11-02 20:54 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: Rafael Wysocki, Lorenzo Pieralisi, Arnd Bergmann, Mark Salter,
	linux-pci, linux-arm, Linux Kernel Mailing List, Jon Masters,
	Tomasz Nowicki, patches

On Wed, Nov 2, 2016 at 9:53 AM, Bjorn Helgaas <helgaas@kernel.org> wrote:
>
> Hi Duc,
>
> On Tue, Oct 25, 2016 at 06:24:32PM -0700, Duc Dang wrote:
> > PCIe controllers in X-Gene SoCs is not ECAM compliant: software
> > needs to configure additional controller's register to address
> > device at bus:dev:function.
> >
> > This patch depends on "ECAM quirks handling for ARM64 platforms"
> > series (http://www.spinics.net/lists/arm-kernel/msg530692.html,
> > the series was also modified by Bjorn) to address the limitation
> > above for X-Gene PCIe controller.
> >
> > 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).
>
> The quirks here contain some hard-coded address space consumed by
> ECAM.  The ECAM quirk itself is not a generic description of that
> address space in the sense of a PCI BAR or an ACPI _CRS method, i.e.,
> the quirk description is not enough to keep other parts of the kernel
> from treating the address space as "available".

Your concern here is the controller register region that I declared as
hard-coded array of resource (xgene_vx_csr_res) is not owned by the
root bridge? So unlike DT case where kernel discovers and knows
exactly who owns this resource (and we can also use
platform_get_resource to get the resource); in ACPI case with ECAM
quirk, kernel knows who requests this resource but does not know who
owns it? Do I understand correctly?

>
> Can you add a note here in the changelog about how you are describing
> this space generically?  The standard solution is a PNP0C02 device
> with _CRS that describes it.
>
> It would be ideal if you could open a bugzilla at bugzilla.kernel.org
> and attach there a dmesg log, /proc/iomem contents, and DSDT.  This
> would show both the generic PNP0C02 piece and the ECAM quirk piece.

We don't have PNP0C02 device in our ACPI Table. Do you want me add a
PNP0C02 device into our firmware and check/document the difference in
/proc/iomem output?
>
> BTW, I did refresh and re-push the pci/ecam-v6 branch where I'm
> collecting this stuff, so if you want to rebase your patch on top of
> that and test it, that would be great.

Thanks, I will definitely do that.
>
> > Signed-off-by: Duc Dang <dhdang@apm.com>
> > ---
> > v2 changes:
> >       1. Get rid of pci-xgene-ecam.c file and fold quirk code into pci-xgene.c
> >       2. Redefine fixup array for X-Gene
> >       3. Use devm_ioremap_resource to map csr_base
> >
> >  drivers/acpi/pci_mcfg.c      |  30 ++++++++
> >  drivers/pci/host/pci-xgene.c | 165 ++++++++++++++++++++++++++++++++++++++++++-
> >  include/linux/pci-ecam.h     |   5 ++
> >  3 files changed, 197 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
> > index bb2c508..9dfc937 100644
> > --- a/drivers/acpi/pci_mcfg.c
> > +++ b/drivers/acpi/pci_mcfg.c
> > @@ -96,6 +96,36 @@ struct mcfg_fixup {
> >       THUNDER_ECAM_MCFG(2, 12),
> >       THUNDER_ECAM_MCFG(2, 13),
> >  #endif
> > +#ifdef CONFIG_PCI_XGENE
> > +#define XGENE_V1_ECAM_MCFG(rev, seg) \
> > +     {"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
> > +             &xgene_v1_pcie_ecam_ops }
> > +#define XGENE_V2_1_ECAM_MCFG(rev, seg) \
> > +     {"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
> > +             &xgene_v2_1_pcie_ecam_ops }
> > +#define XGENE_V2_2_ECAM_MCFG(rev, seg) \
> > +     {"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
> > +             &xgene_v2_2_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_1_ECAM_MCFG(3, 0),
> > +     XGENE_V2_1_ECAM_MCFG(3, 1),
> > +     /* X-Gene SoC with v2.2 PCIe controller */
> > +     XGENE_V2_2_ECAM_MCFG(4, 0),
> > +     XGENE_V2_2_ECAM_MCFG(4, 1),
> > +     XGENE_V2_2_ECAM_MCFG(4, 2),
> > +#endif
> >  };
> >
> >  static char mcfg_oem_id[ACPI_OEM_ID_SIZE];
> > diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> > index 1de23d7..d6aa642 100644
> > --- a/drivers/pci/host/pci-xgene.c
> > +++ b/drivers/pci/host/pci-xgene.c
> > @@ -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,6 +66,7 @@
> >  /* PCIe IP version */
> >  #define XGENE_PCIE_IP_VER_UNKN               0
> >  #define XGENE_PCIE_IP_VER_1          1
> > +#define XGENE_PCIE_IP_VER_2          2
> >
> >  struct xgene_pcie_port {
> >       struct device_node      *node;
> > @@ -97,7 +100,15 @@ static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
> >   */
> >  static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
> >  {
> > -     struct xgene_pcie_port *port = bus->sysdata;
> > +     struct pci_config_window *cfg;
> > +     struct xgene_pcie_port *port;
> > +
> > +     if (acpi_disabled)
> > +             port = bus->sysdata;
> > +     else {
> > +             cfg = bus->sysdata;
> > +             port = cfg->priv;
> > +     }
> >
> >       if (bus->number >= (bus->primary + 1))
> >               return port->cfg_base + AXI_EP_CFG_ACCESS;
> > @@ -111,10 +122,18 @@ 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 pci_config_window *cfg;
> > +     struct xgene_pcie_port *port;
> >       unsigned int b, d, f;
> >       u32 rtdid_val = 0;
> >
> > +     if (acpi_disabled)
> > +             port = bus->sysdata;
> > +     else {
> > +             cfg = bus->sysdata;
> > +             port = cfg->priv;
> > +     }
> > +
> >       b = bus->number;
> >       d = PCI_SLOT(devfn);
> >       f = PCI_FUNC(devfn);
> > @@ -158,7 +177,15 @@ 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 pci_config_window *cfg;
> > +     struct xgene_pcie_port *port;
> > +
> > +     if (acpi_disabled)
> > +             port = bus->sysdata;
> > +     else {
> > +             cfg = bus->sysdata;
> > +             port = cfg->priv;
> > +     }
> >
> >       if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
> >           PCIBIOS_SUCCESSFUL)
> > @@ -189,6 +216,138 @@ static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
> >       .write = pci_generic_config_write32,
> >  };
> >
> > +#ifdef CONFIG_ACPI
> > +static struct resource xgene_v1_csr_res[] = {
> > +     [0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
> > +     [1] = DEFINE_RES_MEM(0x1f2c0000UL, SZ_64K),
> > +     [2] = DEFINE_RES_MEM(0x1f2d0000UL, SZ_64K),
> > +     [3] = DEFINE_RES_MEM(0x1f500000UL, SZ_64K),
> > +     [4] = DEFINE_RES_MEM(0x1f510000UL, SZ_64K),
> > +};
> > +
> > +static int xgene_v1_pcie_ecam_init(struct pci_config_window *cfg)
> > +{
> > +     struct acpi_device *adev = to_acpi_device(cfg->parent);
> > +     struct acpi_pci_root *root = acpi_driver_data(adev);
> > +     struct device *dev = cfg->parent;
> > +     struct xgene_pcie_port *port;
> > +     struct resource *csr;
> > +
> > +     port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> > +     if (!port)
> > +             return -ENOMEM;
> > +
> > +     csr = &xgene_v1_csr_res[root->segment];
> > +     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 = XGENE_PCIE_IP_VER_1;
> > +
> > +     cfg->priv = port;
> > +
> > +     return 0;
> > +}
> > +
> > +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_write,
> > +     }
> > +};
> > +
> > +static struct resource xgene_v2_1_csr_res[] = {
> > +     [0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
> > +     [1] = DEFINE_RES_MEM(0x1f2c0000UL, SZ_64K),
> > +};
> > +
> > +static int xgene_v2_1_pcie_ecam_init(struct pci_config_window *cfg)
> > +{
> > +     struct acpi_device *adev = to_acpi_device(cfg->parent);
> > +     struct acpi_pci_root *root = acpi_driver_data(adev);
> > +     struct device *dev = cfg->parent;
> > +     struct xgene_pcie_port *port;
> > +     struct resource *csr;
> > +
> > +     port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> > +     if (!port)
> > +             return -ENOMEM;
> > +
> > +     csr = &xgene_v2_1_csr_res[root->segment];
> > +     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 = XGENE_PCIE_IP_VER_2;
> > +
> > +     cfg->priv = port;
> > +
> > +     return 0;
> > +}
> > +
> > +struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops = {
> > +     .bus_shift      = 16,
> > +     .init           = xgene_v2_1_pcie_ecam_init,
> > +     .pci_ops        = {
> > +             .map_bus        = xgene_pcie_map_bus,
> > +             .read           = xgene_pcie_config_read32,
> > +             .write          = pci_generic_config_write,
> > +     }
> > +};
> > +
> > +static struct resource xgene_v2_2_csr_res[] = {
> > +     [0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
> > +     [1] = DEFINE_RES_MEM(0x1f500000UL, SZ_64K),
> > +     [2] = DEFINE_RES_MEM(0x1f2d0000UL, SZ_64K),
> > +};
> > +
> > +static int xgene_v2_2_pcie_ecam_init(struct pci_config_window *cfg)
> > +{
> > +     struct acpi_device *adev = to_acpi_device(cfg->parent);
> > +     struct acpi_pci_root *root = acpi_driver_data(adev);
> > +     struct device *dev = cfg->parent;
> > +     struct xgene_pcie_port *port;
> > +     struct resource *csr;
> > +
> > +     port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> > +     if (!port)
> > +             return -ENOMEM;
> > +
> > +     csr = &xgene_v2_2_csr_res[root->segment];
> > +     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 = XGENE_PCIE_IP_VER_2;
> > +
> > +     cfg->priv = port;
> > +
> > +     return 0;
> > +}
> > +
> > +struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops = {
> > +     .bus_shift      = 16,
> > +     .init           = xgene_v2_2_pcie_ecam_init,
> > +     .pci_ops        = {
> > +             .map_bus        = xgene_pcie_map_bus,
> > +             .read           = xgene_pcie_config_read32,
> > +             .write          = pci_generic_config_write,
> > +     }
> > +};
> > +#endif
> > +
> >  static u64 xgene_pcie_set_ib_mask(struct xgene_pcie_port *port, u32 addr,
> >                                 u32 flags, u64 size)
> >  {
> > diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h
> > index 35f0e81..40da3e7 100644
> > --- a/include/linux/pci-ecam.h
> > +++ b/include/linux/pci-ecam.h
> > @@ -65,6 +65,11 @@ void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn,
> >  #ifdef CONFIG_PCI_HOST_THUNDER_ECAM
> >  extern struct pci_ecam_ops pci_thunder_ecam_ops;
> >  #endif
> > +#ifdef CONFIG_PCI_XGENE
> > +extern struct pci_ecam_ops xgene_v1_pcie_ecam_ops;
> > +extern struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops;
> > +extern struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops;
> > +#endif
> >
> >  #ifdef CONFIG_PCI_HOST_GENERIC
> >  /* for DT-based PCI controllers that support ECAM */
> > --
> > 1.9.1
> >
Regards,
Duc Dang.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH v2 1/1] PCI/ACPI: xgene: Add ECAM quirk for X-Gene PCIe controller
  2016-11-02 20:54           ` Duc Dang
@ 2016-11-07 22:38             ` Bjorn Helgaas
  0 siblings, 0 replies; 9+ messages in thread
From: Bjorn Helgaas @ 2016-11-07 22:38 UTC (permalink / raw)
  To: Duc Dang
  Cc: Rafael Wysocki, Lorenzo Pieralisi, Arnd Bergmann, Mark Salter,
	linux-pci, linux-arm, Linux Kernel Mailing List, Jon Masters,
	Tomasz Nowicki, patches

On Wed, Nov 02, 2016 at 01:54:44PM -0700, Duc Dang wrote:
> On Wed, Nov 2, 2016 at 9:53 AM, Bjorn Helgaas <helgaas@kernel.org> wrote:
> > On Tue, Oct 25, 2016 at 06:24:32PM -0700, Duc Dang wrote:
> > > PCIe controllers in X-Gene SoCs is not ECAM compliant: software
> > > needs to configure additional controller's register to address
> > > device at bus:dev:function.
> > >
> > > This patch depends on "ECAM quirks handling for ARM64 platforms"
> > > series (http://www.spinics.net/lists/arm-kernel/msg530692.html,
> > > the series was also modified by Bjorn) to address the limitation
> > > above for X-Gene PCIe controller.
> > >
> > > 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).
> >
> > The quirks here contain some hard-coded address space consumed by
> > ECAM.  The ECAM quirk itself is not a generic description of that
> > address space in the sense of a PCI BAR or an ACPI _CRS method, i.e.,
> > the quirk description is not enough to keep other parts of the kernel
> > from treating the address space as "available".
> 
> Your concern here is the controller register region that I declared as
> hard-coded array of resource (xgene_vx_csr_res) is not owned by the
> root bridge? So unlike DT case where kernel discovers and knows
> exactly who owns this resource (and we can also use
> platform_get_resource to get the resource); in ACPI case with ECAM
> quirk, kernel knows who requests this resource but does not know who
> owns it? Do I understand correctly?

I think so.  MCFG tells the PCI bridge driver where the ECAM space is.
But the ACPI core itself doesn't look at MCFG, so it doesn't know that
space is occupied, and it could potentially assign that space to some
other device, which would cause a conflict.

> > Can you add a note here in the changelog about how you are describing
> > this space generically?  The standard solution is a PNP0C02 device
> > with _CRS that describes it.
> >
> > It would be ideal if you could open a bugzilla at bugzilla.kernel.org
> > and attach there a dmesg log, /proc/iomem contents, and DSDT.  This
> > would show both the generic PNP0C02 piece and the ECAM quirk piece.
> 
> We don't have PNP0C02 device in our ACPI Table. Do you want me add a
> PNP0C02 device into our firmware and check/document the difference in
> /proc/iomem output?

Yes, you should have a PNP0C02 device and its _CRS should describe the
ECAM space.  _CRS is generic (not device-specific), and it's what
tells the ACPI core that the space is already in use.

If you already had a PNP0C02 device, you could make a quirk similar to
quirk_amd_mmconfig_area() to add things to its _CRS.

But I think you have firmware in the field that does not have a
PNP0C02 device at all, and that's harder to fix because you would need
to add some sort of fake device.

Rafael, do you have any ideas about this?  I can imagine something
like the following, but I don't know what cans of worms it might open:

  struct pnp_protocol pnpquirk_protocol = {
    .name = "Plug and Play Quirks",
  };

  void quirk()
  {
    struct pnp_dev *dev;
    struct resource res;

    ret = pnp_register_protocol(&pnpquirk_protocol);
    if (ret)
      return;

    dev = pnp_alloc_dev(&pnpquirk_protocol, 0, "PNP0C02");
    if (!dev)
      return;

    res.start = XX;          /* ECAM start */
    res.end = YY;            /* ECAM end */
    res.flags = IORESOURCE_MEM;
    pnp_add_resource(dev, &res);

    dev->active = 1;
    pnp_add_device(dev);

    dev_info(&dev->dev, "fabricated device to reserve ECAM space %pR\n", &res);
  }

> > BTW, I did refresh and re-push the pci/ecam-v6 branch where I'm
> > collecting this stuff, so if you want to rebase your patch on top of
> > that and test it, that would be great.
> 
> Thanks, I will definitely do that.
> >
> > > Signed-off-by: Duc Dang <dhdang@apm.com>
> > > ---
> > > v2 changes:
> > >       1. Get rid of pci-xgene-ecam.c file and fold quirk code into pci-xgene.c
> > >       2. Redefine fixup array for X-Gene
> > >       3. Use devm_ioremap_resource to map csr_base
> > >
> > >  drivers/acpi/pci_mcfg.c      |  30 ++++++++
> > >  drivers/pci/host/pci-xgene.c | 165 ++++++++++++++++++++++++++++++++++++++++++-
> > >  include/linux/pci-ecam.h     |   5 ++
> > >  3 files changed, 197 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
> > > index bb2c508..9dfc937 100644
> > > --- a/drivers/acpi/pci_mcfg.c
> > > +++ b/drivers/acpi/pci_mcfg.c
> > > @@ -96,6 +96,36 @@ struct mcfg_fixup {
> > >       THUNDER_ECAM_MCFG(2, 12),
> > >       THUNDER_ECAM_MCFG(2, 13),
> > >  #endif
> > > +#ifdef CONFIG_PCI_XGENE
> > > +#define XGENE_V1_ECAM_MCFG(rev, seg) \
> > > +     {"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
> > > +             &xgene_v1_pcie_ecam_ops }
> > > +#define XGENE_V2_1_ECAM_MCFG(rev, seg) \
> > > +     {"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
> > > +             &xgene_v2_1_pcie_ecam_ops }
> > > +#define XGENE_V2_2_ECAM_MCFG(rev, seg) \
> > > +     {"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
> > > +             &xgene_v2_2_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_1_ECAM_MCFG(3, 0),
> > > +     XGENE_V2_1_ECAM_MCFG(3, 1),
> > > +     /* X-Gene SoC with v2.2 PCIe controller */
> > > +     XGENE_V2_2_ECAM_MCFG(4, 0),
> > > +     XGENE_V2_2_ECAM_MCFG(4, 1),
> > > +     XGENE_V2_2_ECAM_MCFG(4, 2),
> > > +#endif
> > >  };
> > >
> > >  static char mcfg_oem_id[ACPI_OEM_ID_SIZE];
> > > diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> > > index 1de23d7..d6aa642 100644
> > > --- a/drivers/pci/host/pci-xgene.c
> > > +++ b/drivers/pci/host/pci-xgene.c
> > > @@ -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,6 +66,7 @@
> > >  /* PCIe IP version */
> > >  #define XGENE_PCIE_IP_VER_UNKN               0
> > >  #define XGENE_PCIE_IP_VER_1          1
> > > +#define XGENE_PCIE_IP_VER_2          2
> > >
> > >  struct xgene_pcie_port {
> > >       struct device_node      *node;
> > > @@ -97,7 +100,15 @@ static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
> > >   */
> > >  static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
> > >  {
> > > -     struct xgene_pcie_port *port = bus->sysdata;
> > > +     struct pci_config_window *cfg;
> > > +     struct xgene_pcie_port *port;
> > > +
> > > +     if (acpi_disabled)
> > > +             port = bus->sysdata;
> > > +     else {
> > > +             cfg = bus->sysdata;
> > > +             port = cfg->priv;
> > > +     }
> > >
> > >       if (bus->number >= (bus->primary + 1))
> > >               return port->cfg_base + AXI_EP_CFG_ACCESS;
> > > @@ -111,10 +122,18 @@ 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 pci_config_window *cfg;
> > > +     struct xgene_pcie_port *port;
> > >       unsigned int b, d, f;
> > >       u32 rtdid_val = 0;
> > >
> > > +     if (acpi_disabled)
> > > +             port = bus->sysdata;
> > > +     else {
> > > +             cfg = bus->sysdata;
> > > +             port = cfg->priv;
> > > +     }
> > > +
> > >       b = bus->number;
> > >       d = PCI_SLOT(devfn);
> > >       f = PCI_FUNC(devfn);
> > > @@ -158,7 +177,15 @@ 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 pci_config_window *cfg;
> > > +     struct xgene_pcie_port *port;
> > > +
> > > +     if (acpi_disabled)
> > > +             port = bus->sysdata;
> > > +     else {
> > > +             cfg = bus->sysdata;
> > > +             port = cfg->priv;
> > > +     }
> > >
> > >       if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
> > >           PCIBIOS_SUCCESSFUL)
> > > @@ -189,6 +216,138 @@ static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
> > >       .write = pci_generic_config_write32,
> > >  };
> > >
> > > +#ifdef CONFIG_ACPI
> > > +static struct resource xgene_v1_csr_res[] = {
> > > +     [0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
> > > +     [1] = DEFINE_RES_MEM(0x1f2c0000UL, SZ_64K),
> > > +     [2] = DEFINE_RES_MEM(0x1f2d0000UL, SZ_64K),
> > > +     [3] = DEFINE_RES_MEM(0x1f500000UL, SZ_64K),
> > > +     [4] = DEFINE_RES_MEM(0x1f510000UL, SZ_64K),
> > > +};
> > > +
> > > +static int xgene_v1_pcie_ecam_init(struct pci_config_window *cfg)
> > > +{
> > > +     struct acpi_device *adev = to_acpi_device(cfg->parent);
> > > +     struct acpi_pci_root *root = acpi_driver_data(adev);
> > > +     struct device *dev = cfg->parent;
> > > +     struct xgene_pcie_port *port;
> > > +     struct resource *csr;
> > > +
> > > +     port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> > > +     if (!port)
> > > +             return -ENOMEM;
> > > +
> > > +     csr = &xgene_v1_csr_res[root->segment];
> > > +     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 = XGENE_PCIE_IP_VER_1;
> > > +
> > > +     cfg->priv = port;
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +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_write,
> > > +     }
> > > +};
> > > +
> > > +static struct resource xgene_v2_1_csr_res[] = {
> > > +     [0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
> > > +     [1] = DEFINE_RES_MEM(0x1f2c0000UL, SZ_64K),
> > > +};
> > > +
> > > +static int xgene_v2_1_pcie_ecam_init(struct pci_config_window *cfg)
> > > +{
> > > +     struct acpi_device *adev = to_acpi_device(cfg->parent);
> > > +     struct acpi_pci_root *root = acpi_driver_data(adev);
> > > +     struct device *dev = cfg->parent;
> > > +     struct xgene_pcie_port *port;
> > > +     struct resource *csr;
> > > +
> > > +     port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> > > +     if (!port)
> > > +             return -ENOMEM;
> > > +
> > > +     csr = &xgene_v2_1_csr_res[root->segment];
> > > +     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 = XGENE_PCIE_IP_VER_2;
> > > +
> > > +     cfg->priv = port;
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops = {
> > > +     .bus_shift      = 16,
> > > +     .init           = xgene_v2_1_pcie_ecam_init,
> > > +     .pci_ops        = {
> > > +             .map_bus        = xgene_pcie_map_bus,
> > > +             .read           = xgene_pcie_config_read32,
> > > +             .write          = pci_generic_config_write,
> > > +     }
> > > +};
> > > +
> > > +static struct resource xgene_v2_2_csr_res[] = {
> > > +     [0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
> > > +     [1] = DEFINE_RES_MEM(0x1f500000UL, SZ_64K),
> > > +     [2] = DEFINE_RES_MEM(0x1f2d0000UL, SZ_64K),
> > > +};
> > > +
> > > +static int xgene_v2_2_pcie_ecam_init(struct pci_config_window *cfg)
> > > +{
> > > +     struct acpi_device *adev = to_acpi_device(cfg->parent);
> > > +     struct acpi_pci_root *root = acpi_driver_data(adev);
> > > +     struct device *dev = cfg->parent;
> > > +     struct xgene_pcie_port *port;
> > > +     struct resource *csr;
> > > +
> > > +     port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> > > +     if (!port)
> > > +             return -ENOMEM;
> > > +
> > > +     csr = &xgene_v2_2_csr_res[root->segment];
> > > +     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 = XGENE_PCIE_IP_VER_2;
> > > +
> > > +     cfg->priv = port;
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops = {
> > > +     .bus_shift      = 16,
> > > +     .init           = xgene_v2_2_pcie_ecam_init,
> > > +     .pci_ops        = {
> > > +             .map_bus        = xgene_pcie_map_bus,
> > > +             .read           = xgene_pcie_config_read32,
> > > +             .write          = pci_generic_config_write,
> > > +     }
> > > +};
> > > +#endif
> > > +
> > >  static u64 xgene_pcie_set_ib_mask(struct xgene_pcie_port *port, u32 addr,
> > >                                 u32 flags, u64 size)
> > >  {
> > > diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h
> > > index 35f0e81..40da3e7 100644
> > > --- a/include/linux/pci-ecam.h
> > > +++ b/include/linux/pci-ecam.h
> > > @@ -65,6 +65,11 @@ void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn,
> > >  #ifdef CONFIG_PCI_HOST_THUNDER_ECAM
> > >  extern struct pci_ecam_ops pci_thunder_ecam_ops;
> > >  #endif
> > > +#ifdef CONFIG_PCI_XGENE
> > > +extern struct pci_ecam_ops xgene_v1_pcie_ecam_ops;
> > > +extern struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops;
> > > +extern struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops;
> > > +#endif
> > >
> > >  #ifdef CONFIG_PCI_HOST_GENERIC
> > >  /* for DT-based PCI controllers that support ECAM */
> > > --
> > > 1.9.1
> > >
> Regards,
> Duc Dang.

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2016-11-07 22:38 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-17 14:24 [RFC PATCH] PCI/ACPI: xgene: Add ECAM quirk for X-Gene PCIe controller Duc Dang
2016-09-19 20:06 ` Bjorn Helgaas
2016-09-20  1:07   ` Duc Dang
2016-09-21 21:22     ` Bjorn Helgaas
2016-10-26  1:24       ` [RFC PATCH v2 1/1] " Duc Dang
2016-11-02 16:53         ` Bjorn Helgaas
2016-11-02 20:54           ` Duc Dang
2016-11-07 22:38             ` Bjorn Helgaas
2016-10-26  1:31       ` [RFC PATCH] " Duc Dang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).