All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-01-06 16:15 ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-01-06 16:15 UTC (permalink / raw)
  To: Bjorn Helgaas, Grant Likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, Tanmay Inamdar, Loc Ho, Feng Kan, Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines 
and shared across all 5 PCIe ports.

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  34 ++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  21 ++
 drivers/pci/host/Kconfig                           |   4 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 370 +++++++++++++++++++++
 6 files changed, 438 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1


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

* [PATCH 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-01-06 16:15 ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-01-06 16:15 UTC (permalink / raw)
  To: linux-arm-kernel

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines 
and shared across all 5 PCIe ports.

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  34 ++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  21 ++
 drivers/pci/host/Kconfig                           |   4 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 370 +++++++++++++++++++++
 6 files changed, 438 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1

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

* [PATCH 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-01-06 16:15 ` Duc Dang
@ 2015-01-06 16:15   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-01-06 16:15 UTC (permalink / raw)
  To: Bjorn Helgaas, Grant Likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, Tanmay Inamdar, Loc Ho, Feng Kan, Duc Dang

X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
16 HW IRQ lines.

Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Signed-off-by: Duc Dang <dhdang@apm.com>
---
 drivers/pci/host/Kconfig         |   4 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 370 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 375 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index c4b6568..650fd1d 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -84,11 +84,15 @@ config PCIE_XILINX
 	  Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
 	  Host Bridge driver.
 
+config PCI_XGENE_MSI
+	bool
+
 config PCI_XGENE
 	bool "X-Gene PCIe controller"
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 44c2699..c261cf7 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,4 +11,5 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..1d1e1aa
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,370 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#define MSI_INDEX0		0x000000
+#define MSI_INT0		0x800000
+
+struct xgene_msi_settings {
+	u32	index_per_group;
+	u32	irqs_per_index;
+	u32	nr_msi_vec;
+	u32	nr_hw_irqs;
+};
+
+struct xgene_msi {
+	struct irq_domain		*irqhost;
+	struct xgene_msi_settings	*settings;
+	u32				msi_addr_lo;
+	u32				msi_addr_hi;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	int				*msi_virqs;
+};
+
+struct xgene_msi_settings storm_msi_settings = {
+	.index_per_group	= 8,
+	.irqs_per_index		= 21,
+	.nr_msi_vec		= 2688,
+	.nr_hw_irqs		= 16,
+};
+
+typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
+struct xgene_msi xgene_msi_data;
+
+static inline irq_hw_number_t virq_to_hw(unsigned int virq)
+{
+	struct irq_data *irq_data = irq_get_irq_data(virq);
+
+	return WARN_ON(!irq_data) ? 0 : irq_data->hwirq;
+}
+
+static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
+{
+	xgene_msi->settings = &storm_msi_settings;
+	return 0;
+}
+
+static struct irq_chip xgene_msi_chip = {
+	.name		= "xgene-msi",
+	.irq_enable	= unmask_msi_irq,
+	.irq_disable	= mask_msi_irq,
+	.irq_mask	= mask_msi_irq,
+	.irq_unmask	= unmask_msi_irq,
+};
+
+static int xgene_msi_host_map(struct irq_domain *h, unsigned int virq,
+			      irq_hw_number_t hw)
+{
+	irq_set_chip_and_handler(virq, &xgene_msi_chip, handle_simple_irq);
+	irq_set_chip_data(virq, h->host_data);
+	set_irq_flags(irq, IRQF_VALID);
+
+	return 0;
+}
+
+static const struct irq_domain_ops xgene_msi_host_ops = {
+	.map = xgene_msi_host_map,
+};
+
+static int xgene_msi_alloc(struct xgene_msi *xgene_msi)
+{
+	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+	int msi;
+
+	mutex_lock(&xgene_msi->bitmap_lock);
+
+	msi = find_first_zero_bit(xgene_msi->bitmap, msi_irq_count);
+	if (msi < msi_irq_count)
+		set_bit(msi, xgene_msi->bitmap);
+	else
+		msi = -ENOSPC;
+
+	mutex_unlock(&xgene_msi->bitmap_lock);
+
+	return msi;
+}
+
+static void xgene_msi_free(struct xgene_msi *xgene_msi, unsigned long irq)
+{
+	mutex_lock(&xgene_msi->bitmap_lock);
+
+	if (!test_bit(irq, xgene_msi->bitmap))
+		pr_err("trying to free unused MSI#%lu\n", irq);
+	else
+		clear_bit(irq, xgene_msi->bitmap);
+
+	mutex_unlock(&xgene_msi->bitmap_lock);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+	u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+	int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+	if (!xgene_msi->msi_virqs)
+		return -ENOMEM;
+	return 0;
+}
+
+void arch_teardown_msi_irqs(struct pci_dev *dev)
+{
+	struct msi_desc *entry;
+	struct xgene_msi *xgene_msi;
+
+	list_for_each_entry(entry, &dev->msi_list, list) {
+		if (entry->irq == 0)
+			continue;
+		xgene_msi = irq_get_chip_data(entry->irq);
+		irq_set_msi_desc(entry->irq, NULL);
+		xgene_msi_free(xgene_msi, virq_to_hw(entry->irq));
+	}
+}
+
+static void xgene_compose_msi_msg(struct pci_dev *dev, int hwirq,
+				  struct msi_msg *msg,
+				  struct xgene_msi *xgene_msi)
+{
+	u32 nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	u32 irqs_per_index = xgene_msi->settings->irqs_per_index;
+	u32 reg_set = hwirq / (nr_hw_irqs * irqs_per_index);
+	u32 group = hwirq % nr_hw_irqs;
+
+	msg->address_hi = xgene_msi->msi_addr_hi;
+	msg->address_lo = xgene_msi->msi_addr_lo +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (hwirq / nr_hw_irqs) % irqs_per_index;
+}
+
+int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
+{
+	struct xgene_msi *xgene_msi = &xgene_msi_data;
+	struct msi_desc *entry;
+	struct msi_msg msg;
+	unsigned long virq, gic_irq;
+	int hwirq;
+
+	list_for_each_entry(entry, &pdev->msi_list, list) {
+		hwirq = xgene_msi_alloc(xgene_msi);
+		if (hwirq < 0) {
+			dev_err(&pdev->dev, "failed to allocate MSI\n");
+			return -ENOSPC;
+		}
+
+		virq = irq_create_mapping(xgene_msi->irqhost, hwirq);
+		if (virq == 0) {
+			dev_err(&pdev->dev, "failed to map hwirq %i\n", hwirq);
+			return -ENOSPC;
+		}
+
+		gic_irq = xgene_msi->msi_virqs[hwirq %
+				xgene_msi->settings->nr_hw_irqs];
+		pr_debug("Mapp HWIRQ %d on GIC IRQ %lu TO VIRQ %lu\n",
+			 hwirq, gic_irq, virq);
+		irq_set_msi_desc(virq, entry);
+		xgene_compose_msi_msg(pdev, hwirq, &msg, xgene_msi);
+		irq_set_handler_data(virq, (void *)gic_irq);
+		write_msi_msg(virq, &msg);
+	}
+
+	return 0;
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)
+{
+	struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp, processed = 0;
+	u32 nr_hw_irqs, irqs_per_index, index_per_group;
+
+	msi_grp = irq - xgene_msi->msi_virqs[0];
+	if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+		pr_err("invalid msi received\n");
+		return IRQ_NONE;
+	}
+
+	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	irqs_per_index = xgene_msi->settings->irqs_per_index;
+	index_per_group = xgene_msi->settings->index_per_group;
+
+	grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			hw_irq = (((msir_index * irqs_per_index) + intr_index) *
+				 nr_hw_irqs) + msi_grp;
+			virq = irq_find_mapping(xgene_msi->irqhost, hw_irq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+			processed++;
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+	u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+
+	for (i = 0; i < nr_hw_irqs; i++) {
+		virq = msi->msi_virqs[i];
+		if (virq != 0)
+			free_irq(virq, msi);
+	}
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+				 struct platform_device *pdev,
+				 int irq_index)
+{
+	int virt_msir;
+	cpumask_var_t mask;
+	int err;
+
+	virt_msir = platform_get_irq(pdev, irq_index);
+	if (virt_msir < 0) {
+		dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+			irq_index);
+		return -EINVAL;
+	}
+
+	err = request_irq(virt_msir, xgene_msi_isr, 0, "xgene-msi", msi);
+	if (err) {
+		dev_err(&pdev->dev, "request irq failed\n");
+		return err;
+	}
+
+	if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+		cpumask_setall(mask);
+		irq_set_affinity(virt_msir, mask);
+		free_cpumask_var(mask);
+	}
+
+	msi->msi_virqs[irq_index] = virt_msir;
+
+	return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene-storm-pcie-msi",
+	 .data = xgene_msi_init_storm_settings},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct device_node *np;
+	const struct of_device_id *matched_np;
+	struct xgene_msi *xgene_msi = &xgene_msi_data;
+	xgene_msi_initcall_t init_fn;
+	u32 nr_hw_irqs, nr_msi_vecs;
+
+	np = of_find_matching_node_and_match(NULL,
+			     xgene_msi_match_table, &matched_np);
+	if (!np)
+		return -ENODEV;
+
+	init_fn = (xgene_msi_initcall_t) matched_np->data;
+	rc = init_fn(xgene_msi);
+	if (rc)
+		return rc;
+
+	nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
+	xgene_msi->irqhost = irq_domain_add_linear(pdev->dev.of_node,
+			     nr_msi_vecs, &xgene_msi_host_ops, xgene_msi);
+	if (!xgene_msi->irqhost) {
+		dev_err(&pdev->dev, "No memory for MSI irqhost\n");
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	xgene_msi->msi_addr_hi = upper_32_bits(res->start);
+	xgene_msi->msi_addr_lo = lower_32_bits(res->start);
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
+		rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+		if (rc)
+			goto error;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+module_platform_driver(xgene_msi_driver);
+
+MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
+MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1


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

* [PATCH 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-01-06 16:15   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-01-06 16:15 UTC (permalink / raw)
  To: linux-arm-kernel

X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
16 HW IRQ lines.

Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Signed-off-by: Duc Dang <dhdang@apm.com>
---
 drivers/pci/host/Kconfig         |   4 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 370 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 375 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index c4b6568..650fd1d 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -84,11 +84,15 @@ config PCIE_XILINX
 	  Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
 	  Host Bridge driver.
 
+config PCI_XGENE_MSI
+	bool
+
 config PCI_XGENE
 	bool "X-Gene PCIe controller"
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 44c2699..c261cf7 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,4 +11,5 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..1d1e1aa
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,370 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#define MSI_INDEX0		0x000000
+#define MSI_INT0		0x800000
+
+struct xgene_msi_settings {
+	u32	index_per_group;
+	u32	irqs_per_index;
+	u32	nr_msi_vec;
+	u32	nr_hw_irqs;
+};
+
+struct xgene_msi {
+	struct irq_domain		*irqhost;
+	struct xgene_msi_settings	*settings;
+	u32				msi_addr_lo;
+	u32				msi_addr_hi;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	int				*msi_virqs;
+};
+
+struct xgene_msi_settings storm_msi_settings = {
+	.index_per_group	= 8,
+	.irqs_per_index		= 21,
+	.nr_msi_vec		= 2688,
+	.nr_hw_irqs		= 16,
+};
+
+typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
+struct xgene_msi xgene_msi_data;
+
+static inline irq_hw_number_t virq_to_hw(unsigned int virq)
+{
+	struct irq_data *irq_data = irq_get_irq_data(virq);
+
+	return WARN_ON(!irq_data) ? 0 : irq_data->hwirq;
+}
+
+static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
+{
+	xgene_msi->settings = &storm_msi_settings;
+	return 0;
+}
+
+static struct irq_chip xgene_msi_chip = {
+	.name		= "xgene-msi",
+	.irq_enable	= unmask_msi_irq,
+	.irq_disable	= mask_msi_irq,
+	.irq_mask	= mask_msi_irq,
+	.irq_unmask	= unmask_msi_irq,
+};
+
+static int xgene_msi_host_map(struct irq_domain *h, unsigned int virq,
+			      irq_hw_number_t hw)
+{
+	irq_set_chip_and_handler(virq, &xgene_msi_chip, handle_simple_irq);
+	irq_set_chip_data(virq, h->host_data);
+	set_irq_flags(irq, IRQF_VALID);
+
+	return 0;
+}
+
+static const struct irq_domain_ops xgene_msi_host_ops = {
+	.map = xgene_msi_host_map,
+};
+
+static int xgene_msi_alloc(struct xgene_msi *xgene_msi)
+{
+	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+	int msi;
+
+	mutex_lock(&xgene_msi->bitmap_lock);
+
+	msi = find_first_zero_bit(xgene_msi->bitmap, msi_irq_count);
+	if (msi < msi_irq_count)
+		set_bit(msi, xgene_msi->bitmap);
+	else
+		msi = -ENOSPC;
+
+	mutex_unlock(&xgene_msi->bitmap_lock);
+
+	return msi;
+}
+
+static void xgene_msi_free(struct xgene_msi *xgene_msi, unsigned long irq)
+{
+	mutex_lock(&xgene_msi->bitmap_lock);
+
+	if (!test_bit(irq, xgene_msi->bitmap))
+		pr_err("trying to free unused MSI#%lu\n", irq);
+	else
+		clear_bit(irq, xgene_msi->bitmap);
+
+	mutex_unlock(&xgene_msi->bitmap_lock);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+	u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+	int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+	if (!xgene_msi->msi_virqs)
+		return -ENOMEM;
+	return 0;
+}
+
+void arch_teardown_msi_irqs(struct pci_dev *dev)
+{
+	struct msi_desc *entry;
+	struct xgene_msi *xgene_msi;
+
+	list_for_each_entry(entry, &dev->msi_list, list) {
+		if (entry->irq == 0)
+			continue;
+		xgene_msi = irq_get_chip_data(entry->irq);
+		irq_set_msi_desc(entry->irq, NULL);
+		xgene_msi_free(xgene_msi, virq_to_hw(entry->irq));
+	}
+}
+
+static void xgene_compose_msi_msg(struct pci_dev *dev, int hwirq,
+				  struct msi_msg *msg,
+				  struct xgene_msi *xgene_msi)
+{
+	u32 nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	u32 irqs_per_index = xgene_msi->settings->irqs_per_index;
+	u32 reg_set = hwirq / (nr_hw_irqs * irqs_per_index);
+	u32 group = hwirq % nr_hw_irqs;
+
+	msg->address_hi = xgene_msi->msi_addr_hi;
+	msg->address_lo = xgene_msi->msi_addr_lo +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (hwirq / nr_hw_irqs) % irqs_per_index;
+}
+
+int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
+{
+	struct xgene_msi *xgene_msi = &xgene_msi_data;
+	struct msi_desc *entry;
+	struct msi_msg msg;
+	unsigned long virq, gic_irq;
+	int hwirq;
+
+	list_for_each_entry(entry, &pdev->msi_list, list) {
+		hwirq = xgene_msi_alloc(xgene_msi);
+		if (hwirq < 0) {
+			dev_err(&pdev->dev, "failed to allocate MSI\n");
+			return -ENOSPC;
+		}
+
+		virq = irq_create_mapping(xgene_msi->irqhost, hwirq);
+		if (virq == 0) {
+			dev_err(&pdev->dev, "failed to map hwirq %i\n", hwirq);
+			return -ENOSPC;
+		}
+
+		gic_irq = xgene_msi->msi_virqs[hwirq %
+				xgene_msi->settings->nr_hw_irqs];
+		pr_debug("Mapp HWIRQ %d on GIC IRQ %lu TO VIRQ %lu\n",
+			 hwirq, gic_irq, virq);
+		irq_set_msi_desc(virq, entry);
+		xgene_compose_msi_msg(pdev, hwirq, &msg, xgene_msi);
+		irq_set_handler_data(virq, (void *)gic_irq);
+		write_msi_msg(virq, &msg);
+	}
+
+	return 0;
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)
+{
+	struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp, processed = 0;
+	u32 nr_hw_irqs, irqs_per_index, index_per_group;
+
+	msi_grp = irq - xgene_msi->msi_virqs[0];
+	if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+		pr_err("invalid msi received\n");
+		return IRQ_NONE;
+	}
+
+	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	irqs_per_index = xgene_msi->settings->irqs_per_index;
+	index_per_group = xgene_msi->settings->index_per_group;
+
+	grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			hw_irq = (((msir_index * irqs_per_index) + intr_index) *
+				 nr_hw_irqs) + msi_grp;
+			virq = irq_find_mapping(xgene_msi->irqhost, hw_irq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+			processed++;
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+	u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+
+	for (i = 0; i < nr_hw_irqs; i++) {
+		virq = msi->msi_virqs[i];
+		if (virq != 0)
+			free_irq(virq, msi);
+	}
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+				 struct platform_device *pdev,
+				 int irq_index)
+{
+	int virt_msir;
+	cpumask_var_t mask;
+	int err;
+
+	virt_msir = platform_get_irq(pdev, irq_index);
+	if (virt_msir < 0) {
+		dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+			irq_index);
+		return -EINVAL;
+	}
+
+	err = request_irq(virt_msir, xgene_msi_isr, 0, "xgene-msi", msi);
+	if (err) {
+		dev_err(&pdev->dev, "request irq failed\n");
+		return err;
+	}
+
+	if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+		cpumask_setall(mask);
+		irq_set_affinity(virt_msir, mask);
+		free_cpumask_var(mask);
+	}
+
+	msi->msi_virqs[irq_index] = virt_msir;
+
+	return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene-storm-pcie-msi",
+	 .data = xgene_msi_init_storm_settings},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct device_node *np;
+	const struct of_device_id *matched_np;
+	struct xgene_msi *xgene_msi = &xgene_msi_data;
+	xgene_msi_initcall_t init_fn;
+	u32 nr_hw_irqs, nr_msi_vecs;
+
+	np = of_find_matching_node_and_match(NULL,
+			     xgene_msi_match_table, &matched_np);
+	if (!np)
+		return -ENODEV;
+
+	init_fn = (xgene_msi_initcall_t) matched_np->data;
+	rc = init_fn(xgene_msi);
+	if (rc)
+		return rc;
+
+	nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
+	xgene_msi->irqhost = irq_domain_add_linear(pdev->dev.of_node,
+			     nr_msi_vecs, &xgene_msi_host_ops, xgene_msi);
+	if (!xgene_msi->irqhost) {
+		dev_err(&pdev->dev, "No memory for MSI irqhost\n");
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	xgene_msi->msi_addr_hi = upper_32_bits(res->start);
+	xgene_msi->msi_addr_lo = lower_32_bits(res->start);
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
+		rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+		if (rc)
+			goto error;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+module_platform_driver(xgene_msi_driver);
+
+MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
+MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

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

* [PATCH 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node.
  2015-01-06 16:15 ` Duc Dang
@ 2015-01-06 16:15   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-01-06 16:15 UTC (permalink / raw)
  To: Bjorn Helgaas, Grant Likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, Tanmay Inamdar, Loc Ho, Feng Kan, Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Signed-off-by: Duc Dang <dhdang@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..70ba721 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -469,6 +469,27 @@
 			clocks = <&pcie4clk 0>;
 		};
 
+		msi: msi@79000000 {
+			compatible = "apm,xgene-storm-pcie-msi", "apm,xgene-pcie-msi";
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		serial0: serial@1c020000 {
 			status = "disabled";
 			device_type = "serial";
-- 
1.9.1


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

* [PATCH 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node.
@ 2015-01-06 16:15   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-01-06 16:15 UTC (permalink / raw)
  To: linux-arm-kernel

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Signed-off-by: Duc Dang <dhdang@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..70ba721 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -469,6 +469,27 @@
 			clocks = <&pcie4clk 0>;
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene-storm-pcie-msi", "apm,xgene-pcie-msi";
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		serial0: serial at 1c020000 {
 			status = "disabled";
 			device_type = "serial";
-- 
1.9.1

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

* [PATCH 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node.
  2015-01-06 16:15 ` Duc Dang
@ 2015-01-06 16:15   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-01-06 16:15 UTC (permalink / raw)
  To: Bjorn Helgaas, Grant Likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, Tanmay Inamdar, Loc Ho, Feng Kan, Duc Dang

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Signed-off-by: Duc Dang <dhdang@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 34 ++++++++++++++++++++++
 1 file changed, 34 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..386782c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,34 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene-pcie-msi" or "apm,xgene-storm-pcie-msi"
+	to identify the core.
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Examples:
+
+SoC DTSI:
+
+	msi@79000000 {
+	     compatible = "apm,xgene-storm-pcie-msi", "apm,xgene-pcie-msi";
+	     reg = <0x00 0x79000000 0x0 0x900000>;
+	     interrupts = <  0x0 0x10 0x4
+		     0x0 0x11 0x4
+		     0x0 0x12 0x4
+		     0x0 0x13 0x4
+		     0x0 0x14 0x4
+		     0x0 0x15 0x4
+		     0x0 0x16 0x4
+		     0x0 0x17 0x4
+		     0x0 0x18 0x4
+		     0x0 0x19 0x4
+		     0x0 0x1a 0x4
+		     0x0 0x1b 0x4
+		     0x0 0x1c 0x4
+		     0x0 0x1d 0x4
+		     0x0 0x1e 0x4
+		     0x0 0x1f 0x4>;
+	};
-- 
1.9.1


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

* [PATCH 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node.
@ 2015-01-06 16:15   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-01-06 16:15 UTC (permalink / raw)
  To: linux-arm-kernel

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Signed-off-by: Duc Dang <dhdang@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 34 ++++++++++++++++++++++
 1 file changed, 34 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..386782c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,34 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene-pcie-msi" or "apm,xgene-storm-pcie-msi"
+	to identify the core.
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Examples:
+
+SoC DTSI:
+
+	msi at 79000000 {
+	     compatible = "apm,xgene-storm-pcie-msi", "apm,xgene-pcie-msi";
+	     reg = <0x00 0x79000000 0x0 0x900000>;
+	     interrupts = <  0x0 0x10 0x4
+		     0x0 0x11 0x4
+		     0x0 0x12 0x4
+		     0x0 0x13 0x4
+		     0x0 0x14 0x4
+		     0x0 0x15 0x4
+		     0x0 0x16 0x4
+		     0x0 0x17 0x4
+		     0x0 0x18 0x4
+		     0x0 0x19 0x4
+		     0x0 0x1a 0x4
+		     0x0 0x1b 0x4
+		     0x0 0x1c 0x4
+		     0x0 0x1d 0x4
+		     0x0 0x1e 0x4
+		     0x0 0x1f 0x4>;
+	};
-- 
1.9.1

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

* [PATCH 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
  2015-01-06 16:15 ` Duc Dang
@ 2015-01-06 16:15   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-01-06 16:15 UTC (permalink / raw)
  To: Bjorn Helgaas, Grant Likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, Tanmay Inamdar, Loc Ho, Feng Kan, Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Signed-off-by: Duc Dang <dhdang@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddb9ac8..283f09a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7303,6 +7303,14 @@ L:	linux-pci@vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci@vger.kernel.org
+L:	linux-arm-kernel@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia@lists.infradead.org
-- 
1.9.1


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

* [PATCH 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
@ 2015-01-06 16:15   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-01-06 16:15 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Signed-off-by: Duc Dang <dhdang@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddb9ac8..283f09a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7303,6 +7303,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
-- 
1.9.1

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

* Re: [PATCH 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-01-06 16:15   ` Duc Dang
@ 2015-01-06 19:33     ` Arnd Bergmann
  -1 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-01-06 19:33 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Duc Dang, Bjorn Helgaas, Grant Likely, Liviu Dudau, Feng Kan,
	linux-pci, Tanmay Inamdar, Loc Ho

On Tuesday 06 January 2015 08:15:41 Duc Dang wrote:
> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
> 16 HW IRQ lines.
> 
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> Signed-off-by: Duc Dang <dhdang@apm.com>
> 

I might be a little behind the latest development, but why is this
not a struct msi_controller?

	Arnd

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

* [PATCH 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-01-06 19:33     ` Arnd Bergmann
  0 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-01-06 19:33 UTC (permalink / raw)
  To: linux-arm-kernel

On Tuesday 06 January 2015 08:15:41 Duc Dang wrote:
> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
> 16 HW IRQ lines.
> 
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> Signed-off-by: Duc Dang <dhdang@apm.com>
> 

I might be a little behind the latest development, but why is this
not a struct msi_controller?

	Arnd

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

* Re: [PATCH 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node.
  2015-01-06 16:15   ` Duc Dang
@ 2015-01-06 19:34     ` Arnd Bergmann
  -1 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-01-06 19:34 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Duc Dang, Bjorn Helgaas, Grant Likely, Liviu Dudau, Feng Kan,
	linux-pci, Tanmay Inamdar, Loc Ho

On Tuesday 06 January 2015 08:15:43 Duc Dang wrote:
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> @@ -0,0 +1,34 @@
> +* AppliedMicro X-Gene PCIe MSI interface
> +
> +Required properties:
> +
> +- compatible: should contain "apm,xgene-pcie-msi" or "apm,xgene-storm-pcie-msi"
> +       to identify the core.
> +- reg: A list of physical base address and length for each set of controller
> +       registers.
> +- interrupts: A list of interrupt outputs of the controller.
> +
> +Examples:
> +
> +SoC DTSI:
> +
> +       msi@79000000 {
> +            compatible = "apm,xgene-storm-pcie-msi", "apm,xgene-pcie-msi";
> +            reg = <0x00 0x79000000 0x0 0x900000>;
> +            interrupts = <  0x0 0x10 0x4
> +                    0x0 0x11 0x4
> +                    0x0 0x12 0x4
> +                    0x0 0x13 0x4
> +                    0x0 0x14 0x4
> +                    0x0 0x15 0x4
> +                    0x0 0x16 0x4
> +                    0x0 0x17 0x4
> +                    0x0 0x18 0x4
> +                    0x0 0x19 0x4
> +                    0x0 0x1a 0x4
> +                    0x0 0x1b 0x4
> +                    0x0 0x1c 0x4
> +                    0x0 0x1d 0x4
> +                    0x0 0x1e 0x4
> +                    0x0 0x1f 0x4>;
> +       };

I think the example should contain the pci controller with the respective
msi-parent property.

	Arnd

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

* [PATCH 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node.
@ 2015-01-06 19:34     ` Arnd Bergmann
  0 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-01-06 19:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Tuesday 06 January 2015 08:15:43 Duc Dang wrote:
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> @@ -0,0 +1,34 @@
> +* AppliedMicro X-Gene PCIe MSI interface
> +
> +Required properties:
> +
> +- compatible: should contain "apm,xgene-pcie-msi" or "apm,xgene-storm-pcie-msi"
> +       to identify the core.
> +- reg: A list of physical base address and length for each set of controller
> +       registers.
> +- interrupts: A list of interrupt outputs of the controller.
> +
> +Examples:
> +
> +SoC DTSI:
> +
> +       msi at 79000000 {
> +            compatible = "apm,xgene-storm-pcie-msi", "apm,xgene-pcie-msi";
> +            reg = <0x00 0x79000000 0x0 0x900000>;
> +            interrupts = <  0x0 0x10 0x4
> +                    0x0 0x11 0x4
> +                    0x0 0x12 0x4
> +                    0x0 0x13 0x4
> +                    0x0 0x14 0x4
> +                    0x0 0x15 0x4
> +                    0x0 0x16 0x4
> +                    0x0 0x17 0x4
> +                    0x0 0x18 0x4
> +                    0x0 0x19 0x4
> +                    0x0 0x1a 0x4
> +                    0x0 0x1b 0x4
> +                    0x0 0x1c 0x4
> +                    0x0 0x1d 0x4
> +                    0x0 0x1e 0x4
> +                    0x0 0x1f 0x4>;
> +       };

I think the example should contain the pci controller with the respective
msi-parent property.

	Arnd

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

* Re: [PATCH 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-01-06 19:33     ` Arnd Bergmann
@ 2015-01-12 18:53       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-01-12 18:53 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel, Bjorn Helgaas, Grant Likely, Liviu Dudau,
	Feng Kan, linux-pci, Tanmay Inamdar, Loc Ho

On Tue, Jan 6, 2015 at 11:33 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Tuesday 06 January 2015 08:15:41 Duc Dang wrote:
>> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
>> 16 HW IRQ lines.
>>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>
>
> I might be a little behind the latest development, but why is this
> not a struct msi_controller?

X-Gene V1 have a separate MSI block to handle MSI/MSIX and is shared
among 5 PCIe ports.
So in this driver for this MSI block, we implement X-Gene v1 sepcific
arch_teardown_msi_irqs,
arch_setup_msi_irqs and not using msi_controller struct.

Please let me know if this approach needs to be changed to follow
other implementations of MSI
drivers in latest kernel.
>
>         Arnd

Regards,
Duc Dang.

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

* [PATCH 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-01-12 18:53       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-01-12 18:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 6, 2015 at 11:33 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Tuesday 06 January 2015 08:15:41 Duc Dang wrote:
>> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
>> 16 HW IRQ lines.
>>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>
>
> I might be a little behind the latest development, but why is this
> not a struct msi_controller?

X-Gene V1 have a separate MSI block to handle MSI/MSIX and is shared
among 5 PCIe ports.
So in this driver for this MSI block, we implement X-Gene v1 sepcific
arch_teardown_msi_irqs,
arch_setup_msi_irqs and not using msi_controller struct.

Please let me know if this approach needs to be changed to follow
other implementations of MSI
drivers in latest kernel.
>
>         Arnd

Regards,
Duc Dang.

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

* Re: [PATCH 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-01-12 18:53       ` Duc Dang
@ 2015-01-12 19:44         ` Arnd Bergmann
  -1 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-01-12 19:44 UTC (permalink / raw)
  To: Duc Dang
  Cc: linux-arm-kernel, Bjorn Helgaas, Grant Likely, Liviu Dudau,
	Feng Kan, linux-pci, Tanmay Inamdar, Loc Ho

On Monday 12 January 2015 10:53:14 Duc Dang wrote:
> On Tue, Jan 6, 2015 at 11:33 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> > On Tuesday 06 January 2015 08:15:41 Duc Dang wrote:
> >> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
> >> 16 HW IRQ lines.
> >>
> >> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> >> Signed-off-by: Duc Dang <dhdang@apm.com>
> >>
> >
> > I might be a little behind the latest development, but why is this
> > not a struct msi_controller?
> 
> X-Gene V1 have a separate MSI block to handle MSI/MSIX and is shared
> among 5 PCIe ports.
> So in this driver for this MSI block, we implement X-Gene v1 sepcific
> arch_teardown_msi_irqs,
> arch_setup_msi_irqs and not using msi_controller struct.

I see.

> Please let me know if this approach needs to be changed to follow
> other implementations of MSI
> drivers in latest kernel.

Yes, your approach does not work on distro kernels which will always
enable multiple targets. You cannot override generic weak functions from
a device driver.

	Arnd

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

* [PATCH 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-01-12 19:44         ` Arnd Bergmann
  0 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-01-12 19:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 12 January 2015 10:53:14 Duc Dang wrote:
> On Tue, Jan 6, 2015 at 11:33 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> > On Tuesday 06 January 2015 08:15:41 Duc Dang wrote:
> >> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
> >> 16 HW IRQ lines.
> >>
> >> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> >> Signed-off-by: Duc Dang <dhdang@apm.com>
> >>
> >
> > I might be a little behind the latest development, but why is this
> > not a struct msi_controller?
> 
> X-Gene V1 have a separate MSI block to handle MSI/MSIX and is shared
> among 5 PCIe ports.
> So in this driver for this MSI block, we implement X-Gene v1 sepcific
> arch_teardown_msi_irqs,
> arch_setup_msi_irqs and not using msi_controller struct.

I see.

> Please let me know if this approach needs to be changed to follow
> other implementations of MSI
> drivers in latest kernel.

Yes, your approach does not work on distro kernels which will always
enable multiple targets. You cannot override generic weak functions from
a device driver.

	Arnd

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

* [PATCH v2 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-01-12 19:44         ` Arnd Bergmann
@ 2015-03-04 19:39           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-04 19:39 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, Tanmay Inamdar, Loc Ho, Feng Kan, Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports.

v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   4 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  25 ++
 7 files changed, 519 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1


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

* [PATCH v2 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-03-04 19:39           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-04 19:39 UTC (permalink / raw)
  To: linux-arm-kernel

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports.

v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   4 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  25 ++
 7 files changed, 519 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1

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

* [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-03-04 19:39           ` Duc Dang
@ 2015-03-04 19:39           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-04 19:39 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, Tanmay Inamdar, Loc Ho, Feng Kan, Duc Dang

X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
16 HW IRQ lines.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 drivers/pci/host/Kconfig         |   4 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 393 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  25 +++
 4 files changed, 423 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9..6c0f98c 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -84,11 +84,15 @@ config PCIE_XILINX
 	  Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
 	  Host Bridge driver.
 
+config PCI_XGENE_MSI
+	bool
+
 config PCI_XGENE
 	bool "X-Gene PCIe controller"
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c..f39bde3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..e1cab39
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,393 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0		0x000000
+#define MSI_INT0		0x800000
+
+struct xgene_msi_settings {
+	u32	index_per_group;
+	u32	irqs_per_index;
+	u32	nr_msi_vec;
+	u32	nr_hw_irqs;
+};
+
+struct xgene_msi {
+	struct irq_domain		*irqhost;
+	struct msi_controller		msi_chip;
+	struct xgene_msi_settings	*settings;
+	u32				msi_addr_lo;
+	u32				msi_addr_hi;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	int				*msi_virqs;
+};
+
+static inline struct xgene_msi *to_xgene_msi(struct msi_controller *msi_chip)
+{
+	return container_of(msi_chip, struct xgene_msi, msi_chip);
+}
+
+struct xgene_msi_settings storm_msi_settings = {
+	.index_per_group	= 8,
+	.irqs_per_index		= 21,
+	.nr_msi_vec		= 2688,
+	.nr_hw_irqs		= 16,
+};
+
+typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
+
+static inline irq_hw_number_t virq_to_hw(unsigned int virq)
+{
+	struct irq_data *irq_data = irq_get_irq_data(virq);
+
+	return WARN_ON(!irq_data) ? 0 : irq_data->hwirq;
+}
+
+static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
+{
+	xgene_msi->settings = &storm_msi_settings;
+	return 0;
+}
+
+static struct irq_chip xgene_msi_chip = {
+	.name		= "X-Gene-1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static int xgene_msi_host_map(struct irq_domain *h, unsigned int virq,
+			      irq_hw_number_t hw)
+{
+	irq_set_chip_and_handler(virq, &xgene_msi_chip, handle_simple_irq);
+	irq_set_chip_data(virq, h->host_data);
+	set_irq_flags(irq, IRQF_VALID);
+
+	return 0;
+}
+
+static const struct irq_domain_ops xgene_msi_host_ops = {
+	.map = xgene_msi_host_map,
+};
+
+static int xgene_msi_alloc(struct xgene_msi *xgene_msi)
+{
+	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+	int msi;
+
+	mutex_lock(&xgene_msi->bitmap_lock);
+
+	msi = find_first_zero_bit(xgene_msi->bitmap, msi_irq_count);
+	if (msi < msi_irq_count)
+		set_bit(msi, xgene_msi->bitmap);
+	else
+		msi = -ENOSPC;
+
+	mutex_unlock(&xgene_msi->bitmap_lock);
+
+	return msi;
+}
+
+static void xgene_msi_free(struct xgene_msi *xgene_msi, unsigned long irq)
+{
+	mutex_lock(&xgene_msi->bitmap_lock);
+
+	if (!test_bit(irq, xgene_msi->bitmap))
+		pr_err("trying to free unused MSI#%lu\n", irq);
+	else
+		clear_bit(irq, xgene_msi->bitmap);
+
+	mutex_unlock(&xgene_msi->bitmap_lock);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+	u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+	int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+	if (!xgene_msi->msi_virqs)
+		return -ENOMEM;
+	return 0;
+}
+
+static void xgene_msi_teardown_irq(struct msi_controller *chip,
+						unsigned int irq)
+{
+	struct xgene_msi *xgene_msi = to_xgene_msi(chip);
+
+	irq_set_msi_desc(irq, NULL);
+	xgene_msi_free(xgene_msi, virq_to_hw(irq));
+}
+
+static void xgene_compose_msi_msg(struct pci_dev *dev, int hwirq,
+				  struct msi_msg *msg,
+				  struct xgene_msi *xgene_msi)
+{
+	u32 nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	u32 irqs_per_index = xgene_msi->settings->irqs_per_index;
+	u32 reg_set = hwirq / (nr_hw_irqs * irqs_per_index);
+	u32 group = hwirq % nr_hw_irqs;
+
+	msg->address_hi = xgene_msi->msi_addr_hi;
+	msg->address_lo = xgene_msi->msi_addr_lo +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (hwirq / nr_hw_irqs) % irqs_per_index;
+}
+
+static int xgene_msi_setup_irq(struct msi_controller *chip,
+				struct pci_dev *pdev, struct msi_desc *desc)
+{
+	struct xgene_msi *xgene_msi = to_xgene_msi(chip);
+	struct msi_msg msg;
+	unsigned long virq, gic_irq;
+	int hwirq;
+
+	hwirq = xgene_msi_alloc(xgene_msi);
+	if (hwirq < 0) {
+		dev_err(&pdev->dev, "failed to allocate MSI\n");
+		return -ENOSPC;
+	}
+
+	virq = irq_create_mapping(xgene_msi->irqhost, hwirq);
+	if (virq == 0) {
+		dev_err(&pdev->dev, "failed to map hwirq %i\n", hwirq);
+		return -ENOSPC;
+	}
+
+	gic_irq = xgene_msi->msi_virqs[hwirq %
+			xgene_msi->settings->nr_hw_irqs];
+	pr_debug("Mapp HWIRQ %d on GIC IRQ %lu TO VIRQ %lu\n",
+		 hwirq, gic_irq, virq);
+	irq_set_msi_desc(virq, desc);
+	xgene_compose_msi_msg(pdev, hwirq, &msg, xgene_msi);
+	irq_set_handler_data(virq, (void *)gic_irq);
+	write_msi_msg(virq, &msg);
+
+	return 0;
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)
+{
+	struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp, processed = 0;
+	u32 nr_hw_irqs, irqs_per_index, index_per_group;
+
+	msi_grp = irq - xgene_msi->msi_virqs[0];
+	if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+		pr_err("invalid msi received\n");
+		return IRQ_NONE;
+	}
+
+	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	irqs_per_index = xgene_msi->settings->irqs_per_index;
+	index_per_group = xgene_msi->settings->index_per_group;
+
+	grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			hw_irq = (((msir_index * irqs_per_index) + intr_index) *
+				 nr_hw_irqs) + msi_grp;
+			virq = irq_find_mapping(xgene_msi->irqhost, hw_irq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+			processed++;
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+	u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+
+	for (i = 0; i < nr_hw_irqs; i++) {
+		virq = msi->msi_virqs[i];
+		if (virq != 0)
+			free_irq(virq, msi);
+	}
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+				 struct platform_device *pdev,
+				 int irq_index)
+{
+	int virt_msir;
+	cpumask_var_t mask;
+	int err;
+
+	virt_msir = platform_get_irq(pdev, irq_index);
+	if (virt_msir < 0) {
+		dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+			irq_index);
+		return -EINVAL;
+	}
+
+	err = request_irq(virt_msir, xgene_msi_isr, 0, "xgene-msi", msi);
+	if (err) {
+		dev_err(&pdev->dev, "request irq failed\n");
+		return err;
+	}
+
+	if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+		cpumask_setall(mask);
+		irq_set_affinity(virt_msir, mask);
+		free_cpumask_var(mask);
+	}
+
+	msi->msi_virqs[irq_index] = virt_msir;
+
+	return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi",
+	 .data = xgene_msi_init_storm_settings},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct device_node *np;
+	const struct of_device_id *matched_np;
+	struct xgene_msi *xgene_msi;
+	xgene_msi_initcall_t init_fn;
+	u32 nr_hw_irqs, nr_msi_vecs;
+
+	np = of_find_matching_node_and_match(NULL,
+			     xgene_msi_match_table, &matched_np);
+	if (!np)
+		return -ENODEV;
+
+	xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
+	if (!xgene_msi) {
+		dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
+		return -ENOMEM;
+	}
+
+	init_fn = (xgene_msi_initcall_t) matched_np->data;
+	rc = init_fn(xgene_msi);
+	if (rc)
+		return rc;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
+	xgene_msi->irqhost = irq_domain_add_linear(pdev->dev.of_node,
+			     nr_msi_vecs, &xgene_msi_host_ops, xgene_msi);
+	if (!xgene_msi->irqhost) {
+		dev_err(&pdev->dev, "No memory for MSI irqhost\n");
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	xgene_msi->msi_addr_hi = upper_32_bits(res->start);
+	xgene_msi->msi_addr_lo = lower_32_bits(res->start);
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
+		rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+		if (rc)
+			goto error;
+	}
+
+	xgene_msi->msi_chip.dev = &pdev->dev;
+	xgene_msi->msi_chip.of_node = np;
+	xgene_msi->msi_chip.setup_irq = xgene_msi_setup_irq;
+	xgene_msi->msi_chip.teardown_irq = xgene_msi_teardown_irq;
+
+	rc = of_pci_msi_chip_add(&xgene_msi->msi_chip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
+MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
+MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index ee082c0..63d58e6 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,14 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI)) {
+		ret = xgene_pcie_msi_enable(bus);
+		if (ret) {
+			dev_err(port->dev, "failed to enable MSI\n");
+			return ret;
+		}
+	}
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1


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

* [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-03-04 19:39           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-04 19:39 UTC (permalink / raw)
  To: linux-arm-kernel

X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
16 HW IRQ lines.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 drivers/pci/host/Kconfig         |   4 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 393 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  25 +++
 4 files changed, 423 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9..6c0f98c 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -84,11 +84,15 @@ config PCIE_XILINX
 	  Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
 	  Host Bridge driver.
 
+config PCI_XGENE_MSI
+	bool
+
 config PCI_XGENE
 	bool "X-Gene PCIe controller"
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c..f39bde3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..e1cab39
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,393 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0		0x000000
+#define MSI_INT0		0x800000
+
+struct xgene_msi_settings {
+	u32	index_per_group;
+	u32	irqs_per_index;
+	u32	nr_msi_vec;
+	u32	nr_hw_irqs;
+};
+
+struct xgene_msi {
+	struct irq_domain		*irqhost;
+	struct msi_controller		msi_chip;
+	struct xgene_msi_settings	*settings;
+	u32				msi_addr_lo;
+	u32				msi_addr_hi;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	int				*msi_virqs;
+};
+
+static inline struct xgene_msi *to_xgene_msi(struct msi_controller *msi_chip)
+{
+	return container_of(msi_chip, struct xgene_msi, msi_chip);
+}
+
+struct xgene_msi_settings storm_msi_settings = {
+	.index_per_group	= 8,
+	.irqs_per_index		= 21,
+	.nr_msi_vec		= 2688,
+	.nr_hw_irqs		= 16,
+};
+
+typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
+
+static inline irq_hw_number_t virq_to_hw(unsigned int virq)
+{
+	struct irq_data *irq_data = irq_get_irq_data(virq);
+
+	return WARN_ON(!irq_data) ? 0 : irq_data->hwirq;
+}
+
+static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
+{
+	xgene_msi->settings = &storm_msi_settings;
+	return 0;
+}
+
+static struct irq_chip xgene_msi_chip = {
+	.name		= "X-Gene-1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static int xgene_msi_host_map(struct irq_domain *h, unsigned int virq,
+			      irq_hw_number_t hw)
+{
+	irq_set_chip_and_handler(virq, &xgene_msi_chip, handle_simple_irq);
+	irq_set_chip_data(virq, h->host_data);
+	set_irq_flags(irq, IRQF_VALID);
+
+	return 0;
+}
+
+static const struct irq_domain_ops xgene_msi_host_ops = {
+	.map = xgene_msi_host_map,
+};
+
+static int xgene_msi_alloc(struct xgene_msi *xgene_msi)
+{
+	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+	int msi;
+
+	mutex_lock(&xgene_msi->bitmap_lock);
+
+	msi = find_first_zero_bit(xgene_msi->bitmap, msi_irq_count);
+	if (msi < msi_irq_count)
+		set_bit(msi, xgene_msi->bitmap);
+	else
+		msi = -ENOSPC;
+
+	mutex_unlock(&xgene_msi->bitmap_lock);
+
+	return msi;
+}
+
+static void xgene_msi_free(struct xgene_msi *xgene_msi, unsigned long irq)
+{
+	mutex_lock(&xgene_msi->bitmap_lock);
+
+	if (!test_bit(irq, xgene_msi->bitmap))
+		pr_err("trying to free unused MSI#%lu\n", irq);
+	else
+		clear_bit(irq, xgene_msi->bitmap);
+
+	mutex_unlock(&xgene_msi->bitmap_lock);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+	u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+	int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+	if (!xgene_msi->msi_virqs)
+		return -ENOMEM;
+	return 0;
+}
+
+static void xgene_msi_teardown_irq(struct msi_controller *chip,
+						unsigned int irq)
+{
+	struct xgene_msi *xgene_msi = to_xgene_msi(chip);
+
+	irq_set_msi_desc(irq, NULL);
+	xgene_msi_free(xgene_msi, virq_to_hw(irq));
+}
+
+static void xgene_compose_msi_msg(struct pci_dev *dev, int hwirq,
+				  struct msi_msg *msg,
+				  struct xgene_msi *xgene_msi)
+{
+	u32 nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	u32 irqs_per_index = xgene_msi->settings->irqs_per_index;
+	u32 reg_set = hwirq / (nr_hw_irqs * irqs_per_index);
+	u32 group = hwirq % nr_hw_irqs;
+
+	msg->address_hi = xgene_msi->msi_addr_hi;
+	msg->address_lo = xgene_msi->msi_addr_lo +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (hwirq / nr_hw_irqs) % irqs_per_index;
+}
+
+static int xgene_msi_setup_irq(struct msi_controller *chip,
+				struct pci_dev *pdev, struct msi_desc *desc)
+{
+	struct xgene_msi *xgene_msi = to_xgene_msi(chip);
+	struct msi_msg msg;
+	unsigned long virq, gic_irq;
+	int hwirq;
+
+	hwirq = xgene_msi_alloc(xgene_msi);
+	if (hwirq < 0) {
+		dev_err(&pdev->dev, "failed to allocate MSI\n");
+		return -ENOSPC;
+	}
+
+	virq = irq_create_mapping(xgene_msi->irqhost, hwirq);
+	if (virq == 0) {
+		dev_err(&pdev->dev, "failed to map hwirq %i\n", hwirq);
+		return -ENOSPC;
+	}
+
+	gic_irq = xgene_msi->msi_virqs[hwirq %
+			xgene_msi->settings->nr_hw_irqs];
+	pr_debug("Mapp HWIRQ %d on GIC IRQ %lu TO VIRQ %lu\n",
+		 hwirq, gic_irq, virq);
+	irq_set_msi_desc(virq, desc);
+	xgene_compose_msi_msg(pdev, hwirq, &msg, xgene_msi);
+	irq_set_handler_data(virq, (void *)gic_irq);
+	write_msi_msg(virq, &msg);
+
+	return 0;
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)
+{
+	struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp, processed = 0;
+	u32 nr_hw_irqs, irqs_per_index, index_per_group;
+
+	msi_grp = irq - xgene_msi->msi_virqs[0];
+	if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+		pr_err("invalid msi received\n");
+		return IRQ_NONE;
+	}
+
+	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	irqs_per_index = xgene_msi->settings->irqs_per_index;
+	index_per_group = xgene_msi->settings->index_per_group;
+
+	grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			hw_irq = (((msir_index * irqs_per_index) + intr_index) *
+				 nr_hw_irqs) + msi_grp;
+			virq = irq_find_mapping(xgene_msi->irqhost, hw_irq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+			processed++;
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+	u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+
+	for (i = 0; i < nr_hw_irqs; i++) {
+		virq = msi->msi_virqs[i];
+		if (virq != 0)
+			free_irq(virq, msi);
+	}
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+				 struct platform_device *pdev,
+				 int irq_index)
+{
+	int virt_msir;
+	cpumask_var_t mask;
+	int err;
+
+	virt_msir = platform_get_irq(pdev, irq_index);
+	if (virt_msir < 0) {
+		dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+			irq_index);
+		return -EINVAL;
+	}
+
+	err = request_irq(virt_msir, xgene_msi_isr, 0, "xgene-msi", msi);
+	if (err) {
+		dev_err(&pdev->dev, "request irq failed\n");
+		return err;
+	}
+
+	if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+		cpumask_setall(mask);
+		irq_set_affinity(virt_msir, mask);
+		free_cpumask_var(mask);
+	}
+
+	msi->msi_virqs[irq_index] = virt_msir;
+
+	return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi",
+	 .data = xgene_msi_init_storm_settings},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct device_node *np;
+	const struct of_device_id *matched_np;
+	struct xgene_msi *xgene_msi;
+	xgene_msi_initcall_t init_fn;
+	u32 nr_hw_irqs, nr_msi_vecs;
+
+	np = of_find_matching_node_and_match(NULL,
+			     xgene_msi_match_table, &matched_np);
+	if (!np)
+		return -ENODEV;
+
+	xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
+	if (!xgene_msi) {
+		dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
+		return -ENOMEM;
+	}
+
+	init_fn = (xgene_msi_initcall_t) matched_np->data;
+	rc = init_fn(xgene_msi);
+	if (rc)
+		return rc;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
+	xgene_msi->irqhost = irq_domain_add_linear(pdev->dev.of_node,
+			     nr_msi_vecs, &xgene_msi_host_ops, xgene_msi);
+	if (!xgene_msi->irqhost) {
+		dev_err(&pdev->dev, "No memory for MSI irqhost\n");
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	xgene_msi->msi_addr_hi = upper_32_bits(res->start);
+	xgene_msi->msi_addr_lo = lower_32_bits(res->start);
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
+		rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+		if (rc)
+			goto error;
+	}
+
+	xgene_msi->msi_chip.dev = &pdev->dev;
+	xgene_msi->msi_chip.of_node = np;
+	xgene_msi->msi_chip.setup_irq = xgene_msi_setup_irq;
+	xgene_msi->msi_chip.teardown_irq = xgene_msi_teardown_irq;
+
+	rc = of_pci_msi_chip_add(&xgene_msi->msi_chip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
+MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
+MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index ee082c0..63d58e6 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,14 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI)) {
+		ret = xgene_pcie_msi_enable(bus);
+		if (ret) {
+			dev_err(port->dev, "failed to enable MSI\n");
+			return ret;
+		}
+	}
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1

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

* [PATCH v2 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node.
  2015-03-04 19:39           ` Duc Dang
@ 2015-03-04 19:39           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-04 19:39 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, Tanmay Inamdar, Loc Ho, Feng Kan, Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..0fe05dc 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi@79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie@1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		pcie1: pcie@1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		pcie2: pcie@1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		pcie3: pcie@1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		pcie4: pcie@1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		serial0: serial@1c020000 {
-- 
1.9.1


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

* [PATCH v2 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node.
@ 2015-03-04 19:39           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-04 19:39 UTC (permalink / raw)
  To: linux-arm-kernel

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..0fe05dc 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
-- 
1.9.1

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

* [PATCH v2 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node.
  2015-03-04 19:39           ` Duc Dang
@ 2015-03-04 19:39           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-04 19:39 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, Tanmay Inamdar, Loc Ho, Feng Kan, Duc Dang

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 61 ++++++++++++++++++++++
 1 file changed, 61 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..fdd55d6
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,61 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene1-msi" to identify the core.
+- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	msi@79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = <  0x0 0x10 0x4
+				0x0 0x11 0x4
+				0x0 0x12 0x4
+				0x0 0x13 0x4
+				0x0 0x14 0x4
+				0x0 0x15 0x4
+				0x0 0x16 0x4
+				0x0 0x17 0x4
+				0x0 0x18 0x4
+				0x0 0x19 0x4
+				0x0 0x1a 0x4
+				0x0 0x1b 0x4
+				0x0 0x1c 0x4
+				0x0 0x1d 0x4
+				0x0 0x1e 0x4
+				0x0 0x1f 0x4>;
+	};
+
+	pcie0: pcie@1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1


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

* [PATCH v2 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node.
@ 2015-03-04 19:39           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-04 19:39 UTC (permalink / raw)
  To: linux-arm-kernel

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 61 ++++++++++++++++++++++
 1 file changed, 61 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..fdd55d6
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,61 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene1-msi" to identify the core.
+- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = <  0x0 0x10 0x4
+				0x0 0x11 0x4
+				0x0 0x12 0x4
+				0x0 0x13 0x4
+				0x0 0x14 0x4
+				0x0 0x15 0x4
+				0x0 0x16 0x4
+				0x0 0x17 0x4
+				0x0 0x18 0x4
+				0x0 0x19 0x4
+				0x0 0x1a 0x4
+				0x0 0x1b 0x4
+				0x0 0x1c 0x4
+				0x0 0x1d 0x4
+				0x0 0x1e 0x4
+				0x0 0x1f 0x4>;
+	};
+
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1

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

* [PATCH v2 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
  2015-03-04 19:39           ` Duc Dang
@ 2015-03-04 19:40           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-04 19:40 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, Tanmay Inamdar, Loc Ho, Feng Kan, Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddc5a8c..a1b119b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7490,6 +7490,14 @@ L:	linux-pci@vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci@vger.kernel.org
+L:	linux-arm-kernel@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia@lists.infradead.org
-- 
1.9.1


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

* [PATCH v2 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
@ 2015-03-04 19:40           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-04 19:40 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddc5a8c..a1b119b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7490,6 +7490,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
-- 
1.9.1

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

* Re: [PATCH v2 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-03-04 19:39           ` Duc Dang
@ 2015-03-18 17:43             ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-18 17:43 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, Tanmay Inamdar, Loc Ho, Feng Kan, Duc Dang

On Wed, Mar 4, 2015 at 11:39 AM, Duc Dang <dhdang@apm.com> wrote:
> This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
>
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports.
>
> v2 changes:
>         1. Use msi_controller structure
>         2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
>
>  .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
>  MAINTAINERS                                        |   8 +
>  arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
>  drivers/pci/host/Kconfig                           |   4 +
>  drivers/pci/host/Makefile                          |   1 +
>  drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c                       |  25 ++
>  7 files changed, 519 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>
> --
> 1.9.1
>
Hi Bjorn, Arnd, and All,

Did you have a chance to take a look at this v2 patch set for X-Gene 1
MSI support?

Thanks,
Duc Dang.

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

* [PATCH v2 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-03-18 17:43             ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-18 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 4, 2015 at 11:39 AM, Duc Dang <dhdang@apm.com> wrote:
> This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
>
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports.
>
> v2 changes:
>         1. Use msi_controller structure
>         2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
>
>  .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
>  MAINTAINERS                                        |   8 +
>  arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
>  drivers/pci/host/Kconfig                           |   4 +
>  drivers/pci/host/Makefile                          |   1 +
>  drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c                       |  25 ++
>  7 files changed, 519 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>
> --
> 1.9.1
>
Hi Bjorn, Arnd, and All,

Did you have a chance to take a look at this v2 patch set for X-Gene 1
MSI support?

Thanks,
Duc Dang.

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

* Re: [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-03-04 19:39           ` Duc Dang
@ 2015-03-18 18:05             ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-03-18 18:05 UTC (permalink / raw)
  To: Duc Dang, Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau
  Cc: Feng Kan, linux-pci, Tanmay Inamdar, linux-arm-kernel, Loc Ho

On 04/03/15 19:39, Duc Dang wrote:
> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
> 16 HW IRQ lines.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>

I just had a quick look at this, and this seems to be going in the exact
opposite direction compared to what we now have on arm64, where we move
away from using struct msi_controller for most thing, and implement PCI
MSI/MSIX in a generic way, using MSI domains.

I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI
support. You can also have a look at what I did for the Tegra MSI
controller in this patch:

https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081

Eventually, the plan is to kill msi_controller entirely, so introducing
new drivers that rely on it is not something I'm eager to see.

Thanks,

	M.

> ---
>  drivers/pci/host/Kconfig         |   4 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 393 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  25 +++
>  4 files changed, 423 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..6c0f98c 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -84,11 +84,15 @@ config PCIE_XILINX
>  	  Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
>  	  Host Bridge driver.
>  
> +config PCI_XGENE_MSI
> +	bool
> +
>  config PCI_XGENE
>  	bool "X-Gene PCIe controller"
>  	depends on ARCH_XGENE
>  	depends on OF
>  	select PCIEPORTBUS
> +	select PCI_XGENE_MSI if PCI_MSI
>  	help
>  	  Say Y here if you want internal PCI support on APM X-Gene SoC.
>  	  There are 5 internal PCIe ports available. Each port is GEN3 capable
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..e1cab39
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,393 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *	   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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_INDEX0		0x000000
> +#define MSI_INT0		0x800000
> +
> +struct xgene_msi_settings {
> +	u32	index_per_group;
> +	u32	irqs_per_index;
> +	u32	nr_msi_vec;
> +	u32	nr_hw_irqs;
> +};
> +
> +struct xgene_msi {
> +	struct irq_domain		*irqhost;
> +	struct msi_controller		msi_chip;
> +	struct xgene_msi_settings	*settings;
> +	u32				msi_addr_lo;
> +	u32				msi_addr_hi;
> +	void __iomem			*msi_regs;
> +	unsigned long			*bitmap;
> +	struct mutex			bitmap_lock;
> +	int				*msi_virqs;
> +};
> +
> +static inline struct xgene_msi *to_xgene_msi(struct msi_controller *msi_chip)
> +{
> +	return container_of(msi_chip, struct xgene_msi, msi_chip);
> +}
> +
> +struct xgene_msi_settings storm_msi_settings = {
> +	.index_per_group	= 8,
> +	.irqs_per_index		= 21,
> +	.nr_msi_vec		= 2688,
> +	.nr_hw_irqs		= 16,
> +};
> +
> +typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
> +
> +static inline irq_hw_number_t virq_to_hw(unsigned int virq)
> +{
> +	struct irq_data *irq_data = irq_get_irq_data(virq);
> +
> +	return WARN_ON(!irq_data) ? 0 : irq_data->hwirq;
> +}
> +
> +static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
> +{
> +	xgene_msi->settings = &storm_msi_settings;
> +	return 0;
> +}
> +
> +static struct irq_chip xgene_msi_chip = {
> +	.name		= "X-Gene-1 MSI",
> +	.irq_enable	= pci_msi_unmask_irq,
> +	.irq_disable	= pci_msi_mask_irq,
> +	.irq_mask	= pci_msi_mask_irq,
> +	.irq_unmask	= pci_msi_unmask_irq,
> +};
> +
> +static int xgene_msi_host_map(struct irq_domain *h, unsigned int virq,
> +			      irq_hw_number_t hw)
> +{
> +	irq_set_chip_and_handler(virq, &xgene_msi_chip, handle_simple_irq);
> +	irq_set_chip_data(virq, h->host_data);
> +	set_irq_flags(irq, IRQF_VALID);
> +
> +	return 0;
> +}
> +
> +static const struct irq_domain_ops xgene_msi_host_ops = {
> +	.map = xgene_msi_host_map,
> +};
> +
> +static int xgene_msi_alloc(struct xgene_msi *xgene_msi)
> +{
> +	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
> +	int msi;
> +
> +	mutex_lock(&xgene_msi->bitmap_lock);
> +
> +	msi = find_first_zero_bit(xgene_msi->bitmap, msi_irq_count);
> +	if (msi < msi_irq_count)
> +		set_bit(msi, xgene_msi->bitmap);
> +	else
> +		msi = -ENOSPC;
> +
> +	mutex_unlock(&xgene_msi->bitmap_lock);
> +
> +	return msi;
> +}
> +
> +static void xgene_msi_free(struct xgene_msi *xgene_msi, unsigned long irq)
> +{
> +	mutex_lock(&xgene_msi->bitmap_lock);
> +
> +	if (!test_bit(irq, xgene_msi->bitmap))
> +		pr_err("trying to free unused MSI#%lu\n", irq);
> +	else
> +		clear_bit(irq, xgene_msi->bitmap);
> +
> +	mutex_unlock(&xgene_msi->bitmap_lock);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
> +	u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
> +	int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
> +
> +	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +	if (!xgene_msi->bitmap)
> +		return -ENOMEM;
> +	mutex_init(&xgene_msi->bitmap_lock);
> +
> +	xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
> +	if (!xgene_msi->msi_virqs)
> +		return -ENOMEM;
> +	return 0;
> +}
> +
> +static void xgene_msi_teardown_irq(struct msi_controller *chip,
> +						unsigned int irq)
> +{
> +	struct xgene_msi *xgene_msi = to_xgene_msi(chip);
> +
> +	irq_set_msi_desc(irq, NULL);
> +	xgene_msi_free(xgene_msi, virq_to_hw(irq));
> +}
> +
> +static void xgene_compose_msi_msg(struct pci_dev *dev, int hwirq,
> +				  struct msi_msg *msg,
> +				  struct xgene_msi *xgene_msi)
> +{
> +	u32 nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
> +	u32 irqs_per_index = xgene_msi->settings->irqs_per_index;
> +	u32 reg_set = hwirq / (nr_hw_irqs * irqs_per_index);
> +	u32 group = hwirq % nr_hw_irqs;
> +
> +	msg->address_hi = xgene_msi->msi_addr_hi;
> +	msg->address_lo = xgene_msi->msi_addr_lo +
> +			  (((8 * group) + reg_set) << 16);
> +	msg->data = (hwirq / nr_hw_irqs) % irqs_per_index;
> +}
> +
> +static int xgene_msi_setup_irq(struct msi_controller *chip,
> +				struct pci_dev *pdev, struct msi_desc *desc)
> +{
> +	struct xgene_msi *xgene_msi = to_xgene_msi(chip);
> +	struct msi_msg msg;
> +	unsigned long virq, gic_irq;
> +	int hwirq;
> +
> +	hwirq = xgene_msi_alloc(xgene_msi);
> +	if (hwirq < 0) {
> +		dev_err(&pdev->dev, "failed to allocate MSI\n");
> +		return -ENOSPC;
> +	}
> +
> +	virq = irq_create_mapping(xgene_msi->irqhost, hwirq);
> +	if (virq == 0) {
> +		dev_err(&pdev->dev, "failed to map hwirq %i\n", hwirq);
> +		return -ENOSPC;
> +	}
> +
> +	gic_irq = xgene_msi->msi_virqs[hwirq %
> +			xgene_msi->settings->nr_hw_irqs];
> +	pr_debug("Mapp HWIRQ %d on GIC IRQ %lu TO VIRQ %lu\n",
> +		 hwirq, gic_irq, virq);
> +	irq_set_msi_desc(virq, desc);
> +	xgene_compose_msi_msg(pdev, hwirq, &msg, xgene_msi);
> +	irq_set_handler_data(virq, (void *)gic_irq);
> +	write_msi_msg(virq, &msg);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t xgene_msi_isr(int irq, void *data)
> +{
> +	struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
> +	unsigned int virq;
> +	int msir_index, msir_reg, msir_val, hw_irq;
> +	u32 intr_index, grp_select, msi_grp, processed = 0;
> +	u32 nr_hw_irqs, irqs_per_index, index_per_group;
> +
> +	msi_grp = irq - xgene_msi->msi_virqs[0];
> +	if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
> +		pr_err("invalid msi received\n");
> +		return IRQ_NONE;
> +	}
> +
> +	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
> +	irqs_per_index = xgene_msi->settings->irqs_per_index;
> +	index_per_group = xgene_msi->settings->index_per_group;
> +
> +	grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> +	while (grp_select) {
> +		msir_index = ffs(grp_select) - 1;
> +		msir_reg = (msi_grp << 19) + (msir_index << 16);
> +		msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
> +		while (msir_val) {
> +			intr_index = ffs(msir_val) - 1;
> +			hw_irq = (((msir_index * irqs_per_index) + intr_index) *
> +				 nr_hw_irqs) + msi_grp;
> +			virq = irq_find_mapping(xgene_msi->irqhost, hw_irq);
> +			if (virq != 0)
> +				generic_handle_irq(virq);
> +			msir_val &= ~(1 << intr_index);
> +			processed++;
> +		}
> +		grp_select &= ~(1 << msir_index);
> +	}
> +
> +	return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +	int virq, i;
> +	struct xgene_msi *msi = platform_get_drvdata(pdev);
> +	u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
> +
> +	for (i = 0; i < nr_hw_irqs; i++) {
> +		virq = msi->msi_virqs[i];
> +		if (virq != 0)
> +			free_irq(virq, msi);
> +	}
> +
> +	kfree(msi->bitmap);
> +	msi->bitmap = NULL;
> +
> +	return 0;
> +}
> +
> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
> +				 struct platform_device *pdev,
> +				 int irq_index)
> +{
> +	int virt_msir;
> +	cpumask_var_t mask;
> +	int err;
> +
> +	virt_msir = platform_get_irq(pdev, irq_index);
> +	if (virt_msir < 0) {
> +		dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +			irq_index);
> +		return -EINVAL;
> +	}
> +
> +	err = request_irq(virt_msir, xgene_msi_isr, 0, "xgene-msi", msi);
> +	if (err) {
> +		dev_err(&pdev->dev, "request irq failed\n");
> +		return err;
> +	}
> +
> +	if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +		cpumask_setall(mask);
> +		irq_set_affinity(virt_msir, mask);
> +		free_cpumask_var(mask);
> +	}
> +
> +	msi->msi_virqs[irq_index] = virt_msir;
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +	{.compatible = "apm,xgene1-msi",
> +	 .data = xgene_msi_init_storm_settings},
> +	{},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +	int rc, irq_index;
> +	struct device_node *np;
> +	const struct of_device_id *matched_np;
> +	struct xgene_msi *xgene_msi;
> +	xgene_msi_initcall_t init_fn;
> +	u32 nr_hw_irqs, nr_msi_vecs;
> +
> +	np = of_find_matching_node_and_match(NULL,
> +			     xgene_msi_match_table, &matched_np);
> +	if (!np)
> +		return -ENODEV;
> +
> +	xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
> +	if (!xgene_msi) {
> +		dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
> +		return -ENOMEM;
> +	}
> +
> +	init_fn = (xgene_msi_initcall_t) matched_np->data;
> +	rc = init_fn(xgene_msi);
> +	if (rc)
> +		return rc;
> +
> +	platform_set_drvdata(pdev, xgene_msi);
> +
> +	nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
> +	xgene_msi->irqhost = irq_domain_add_linear(pdev->dev.of_node,
> +			     nr_msi_vecs, &xgene_msi_host_ops, xgene_msi);
> +	if (!xgene_msi->irqhost) {
> +		dev_err(&pdev->dev, "No memory for MSI irqhost\n");
> +		rc = -ENOMEM;
> +		goto error;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(xgene_msi->msi_regs)) {
> +		dev_err(&pdev->dev, "no reg space\n");
> +		rc = -EINVAL;
> +		goto error;
> +	}
> +
> +	xgene_msi->msi_addr_hi = upper_32_bits(res->start);
> +	xgene_msi->msi_addr_lo = lower_32_bits(res->start);
> +
> +	rc = xgene_msi_init_allocator(xgene_msi);
> +	if (rc) {
> +		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +		goto error;
> +	}
> +
> +	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
> +	for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
> +		rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
> +		if (rc)
> +			goto error;
> +	}
> +
> +	xgene_msi->msi_chip.dev = &pdev->dev;
> +	xgene_msi->msi_chip.of_node = np;
> +	xgene_msi->msi_chip.setup_irq = xgene_msi_setup_irq;
> +	xgene_msi->msi_chip.teardown_irq = xgene_msi_teardown_irq;
> +
> +	rc = of_pci_msi_chip_add(&xgene_msi->msi_chip);
> +	if (rc) {
> +		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +		goto error;
> +	}
> +
> +	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +	return 0;
> +error:
> +	xgene_msi_remove(pdev);
> +	return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +	.driver = {
> +		.name = "xgene-msi",
> +		.owner = THIS_MODULE,
> +		.of_match_table = xgene_msi_match_table,
> +	},
> +	.probe = xgene_msi_probe,
> +	.remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +	return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> +MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
> +MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index ee082c0..63d58e6 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>  	return 0;
>  }
>  
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +	struct device_node *msi_node;
> +
> +	msi_node = of_parse_phandle(bus->dev.of_node,
> +					"msi-parent", 0);
> +	if (!msi_node)
> +		return -ENODEV;
> +
> +	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +	if (bus->msi)
> +		bus->msi->dev = &bus->dev;
> +	else
> +		return -ENODEV;
> +	return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>  	struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,14 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  	if (!bus)
>  		return -ENOMEM;
>  
> +	if (IS_ENABLED(CONFIG_PCI_MSI)) {
> +		ret = xgene_pcie_msi_enable(bus);
> +		if (ret) {
> +			dev_err(port->dev, "failed to enable MSI\n");
> +			return ret;
> +		}
> +	}
> +
>  	pci_scan_child_bus(bus);
>  	pci_assign_unassigned_bus_resources(bus);
>  	pci_bus_add_devices(bus);
> 


-- 
Jazz is not dead. It just smells funny...

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

* [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-03-18 18:05             ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-03-18 18:05 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/03/15 19:39, Duc Dang wrote:
> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
> 16 HW IRQ lines.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>

I just had a quick look at this, and this seems to be going in the exact
opposite direction compared to what we now have on arm64, where we move
away from using struct msi_controller for most thing, and implement PCI
MSI/MSIX in a generic way, using MSI domains.

I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI
support. You can also have a look at what I did for the Tegra MSI
controller in this patch:

https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081

Eventually, the plan is to kill msi_controller entirely, so introducing
new drivers that rely on it is not something I'm eager to see.

Thanks,

	M.

> ---
>  drivers/pci/host/Kconfig         |   4 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 393 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  25 +++
>  4 files changed, 423 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..6c0f98c 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -84,11 +84,15 @@ config PCIE_XILINX
>  	  Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
>  	  Host Bridge driver.
>  
> +config PCI_XGENE_MSI
> +	bool
> +
>  config PCI_XGENE
>  	bool "X-Gene PCIe controller"
>  	depends on ARCH_XGENE
>  	depends on OF
>  	select PCIEPORTBUS
> +	select PCI_XGENE_MSI if PCI_MSI
>  	help
>  	  Say Y here if you want internal PCI support on APM X-Gene SoC.
>  	  There are 5 internal PCIe ports available. Each port is GEN3 capable
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..e1cab39
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,393 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *	   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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_INDEX0		0x000000
> +#define MSI_INT0		0x800000
> +
> +struct xgene_msi_settings {
> +	u32	index_per_group;
> +	u32	irqs_per_index;
> +	u32	nr_msi_vec;
> +	u32	nr_hw_irqs;
> +};
> +
> +struct xgene_msi {
> +	struct irq_domain		*irqhost;
> +	struct msi_controller		msi_chip;
> +	struct xgene_msi_settings	*settings;
> +	u32				msi_addr_lo;
> +	u32				msi_addr_hi;
> +	void __iomem			*msi_regs;
> +	unsigned long			*bitmap;
> +	struct mutex			bitmap_lock;
> +	int				*msi_virqs;
> +};
> +
> +static inline struct xgene_msi *to_xgene_msi(struct msi_controller *msi_chip)
> +{
> +	return container_of(msi_chip, struct xgene_msi, msi_chip);
> +}
> +
> +struct xgene_msi_settings storm_msi_settings = {
> +	.index_per_group	= 8,
> +	.irqs_per_index		= 21,
> +	.nr_msi_vec		= 2688,
> +	.nr_hw_irqs		= 16,
> +};
> +
> +typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
> +
> +static inline irq_hw_number_t virq_to_hw(unsigned int virq)
> +{
> +	struct irq_data *irq_data = irq_get_irq_data(virq);
> +
> +	return WARN_ON(!irq_data) ? 0 : irq_data->hwirq;
> +}
> +
> +static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
> +{
> +	xgene_msi->settings = &storm_msi_settings;
> +	return 0;
> +}
> +
> +static struct irq_chip xgene_msi_chip = {
> +	.name		= "X-Gene-1 MSI",
> +	.irq_enable	= pci_msi_unmask_irq,
> +	.irq_disable	= pci_msi_mask_irq,
> +	.irq_mask	= pci_msi_mask_irq,
> +	.irq_unmask	= pci_msi_unmask_irq,
> +};
> +
> +static int xgene_msi_host_map(struct irq_domain *h, unsigned int virq,
> +			      irq_hw_number_t hw)
> +{
> +	irq_set_chip_and_handler(virq, &xgene_msi_chip, handle_simple_irq);
> +	irq_set_chip_data(virq, h->host_data);
> +	set_irq_flags(irq, IRQF_VALID);
> +
> +	return 0;
> +}
> +
> +static const struct irq_domain_ops xgene_msi_host_ops = {
> +	.map = xgene_msi_host_map,
> +};
> +
> +static int xgene_msi_alloc(struct xgene_msi *xgene_msi)
> +{
> +	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
> +	int msi;
> +
> +	mutex_lock(&xgene_msi->bitmap_lock);
> +
> +	msi = find_first_zero_bit(xgene_msi->bitmap, msi_irq_count);
> +	if (msi < msi_irq_count)
> +		set_bit(msi, xgene_msi->bitmap);
> +	else
> +		msi = -ENOSPC;
> +
> +	mutex_unlock(&xgene_msi->bitmap_lock);
> +
> +	return msi;
> +}
> +
> +static void xgene_msi_free(struct xgene_msi *xgene_msi, unsigned long irq)
> +{
> +	mutex_lock(&xgene_msi->bitmap_lock);
> +
> +	if (!test_bit(irq, xgene_msi->bitmap))
> +		pr_err("trying to free unused MSI#%lu\n", irq);
> +	else
> +		clear_bit(irq, xgene_msi->bitmap);
> +
> +	mutex_unlock(&xgene_msi->bitmap_lock);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
> +	u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
> +	int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
> +
> +	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +	if (!xgene_msi->bitmap)
> +		return -ENOMEM;
> +	mutex_init(&xgene_msi->bitmap_lock);
> +
> +	xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
> +	if (!xgene_msi->msi_virqs)
> +		return -ENOMEM;
> +	return 0;
> +}
> +
> +static void xgene_msi_teardown_irq(struct msi_controller *chip,
> +						unsigned int irq)
> +{
> +	struct xgene_msi *xgene_msi = to_xgene_msi(chip);
> +
> +	irq_set_msi_desc(irq, NULL);
> +	xgene_msi_free(xgene_msi, virq_to_hw(irq));
> +}
> +
> +static void xgene_compose_msi_msg(struct pci_dev *dev, int hwirq,
> +				  struct msi_msg *msg,
> +				  struct xgene_msi *xgene_msi)
> +{
> +	u32 nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
> +	u32 irqs_per_index = xgene_msi->settings->irqs_per_index;
> +	u32 reg_set = hwirq / (nr_hw_irqs * irqs_per_index);
> +	u32 group = hwirq % nr_hw_irqs;
> +
> +	msg->address_hi = xgene_msi->msi_addr_hi;
> +	msg->address_lo = xgene_msi->msi_addr_lo +
> +			  (((8 * group) + reg_set) << 16);
> +	msg->data = (hwirq / nr_hw_irqs) % irqs_per_index;
> +}
> +
> +static int xgene_msi_setup_irq(struct msi_controller *chip,
> +				struct pci_dev *pdev, struct msi_desc *desc)
> +{
> +	struct xgene_msi *xgene_msi = to_xgene_msi(chip);
> +	struct msi_msg msg;
> +	unsigned long virq, gic_irq;
> +	int hwirq;
> +
> +	hwirq = xgene_msi_alloc(xgene_msi);
> +	if (hwirq < 0) {
> +		dev_err(&pdev->dev, "failed to allocate MSI\n");
> +		return -ENOSPC;
> +	}
> +
> +	virq = irq_create_mapping(xgene_msi->irqhost, hwirq);
> +	if (virq == 0) {
> +		dev_err(&pdev->dev, "failed to map hwirq %i\n", hwirq);
> +		return -ENOSPC;
> +	}
> +
> +	gic_irq = xgene_msi->msi_virqs[hwirq %
> +			xgene_msi->settings->nr_hw_irqs];
> +	pr_debug("Mapp HWIRQ %d on GIC IRQ %lu TO VIRQ %lu\n",
> +		 hwirq, gic_irq, virq);
> +	irq_set_msi_desc(virq, desc);
> +	xgene_compose_msi_msg(pdev, hwirq, &msg, xgene_msi);
> +	irq_set_handler_data(virq, (void *)gic_irq);
> +	write_msi_msg(virq, &msg);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t xgene_msi_isr(int irq, void *data)
> +{
> +	struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
> +	unsigned int virq;
> +	int msir_index, msir_reg, msir_val, hw_irq;
> +	u32 intr_index, grp_select, msi_grp, processed = 0;
> +	u32 nr_hw_irqs, irqs_per_index, index_per_group;
> +
> +	msi_grp = irq - xgene_msi->msi_virqs[0];
> +	if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
> +		pr_err("invalid msi received\n");
> +		return IRQ_NONE;
> +	}
> +
> +	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
> +	irqs_per_index = xgene_msi->settings->irqs_per_index;
> +	index_per_group = xgene_msi->settings->index_per_group;
> +
> +	grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> +	while (grp_select) {
> +		msir_index = ffs(grp_select) - 1;
> +		msir_reg = (msi_grp << 19) + (msir_index << 16);
> +		msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
> +		while (msir_val) {
> +			intr_index = ffs(msir_val) - 1;
> +			hw_irq = (((msir_index * irqs_per_index) + intr_index) *
> +				 nr_hw_irqs) + msi_grp;
> +			virq = irq_find_mapping(xgene_msi->irqhost, hw_irq);
> +			if (virq != 0)
> +				generic_handle_irq(virq);
> +			msir_val &= ~(1 << intr_index);
> +			processed++;
> +		}
> +		grp_select &= ~(1 << msir_index);
> +	}
> +
> +	return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +	int virq, i;
> +	struct xgene_msi *msi = platform_get_drvdata(pdev);
> +	u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
> +
> +	for (i = 0; i < nr_hw_irqs; i++) {
> +		virq = msi->msi_virqs[i];
> +		if (virq != 0)
> +			free_irq(virq, msi);
> +	}
> +
> +	kfree(msi->bitmap);
> +	msi->bitmap = NULL;
> +
> +	return 0;
> +}
> +
> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
> +				 struct platform_device *pdev,
> +				 int irq_index)
> +{
> +	int virt_msir;
> +	cpumask_var_t mask;
> +	int err;
> +
> +	virt_msir = platform_get_irq(pdev, irq_index);
> +	if (virt_msir < 0) {
> +		dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +			irq_index);
> +		return -EINVAL;
> +	}
> +
> +	err = request_irq(virt_msir, xgene_msi_isr, 0, "xgene-msi", msi);
> +	if (err) {
> +		dev_err(&pdev->dev, "request irq failed\n");
> +		return err;
> +	}
> +
> +	if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +		cpumask_setall(mask);
> +		irq_set_affinity(virt_msir, mask);
> +		free_cpumask_var(mask);
> +	}
> +
> +	msi->msi_virqs[irq_index] = virt_msir;
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +	{.compatible = "apm,xgene1-msi",
> +	 .data = xgene_msi_init_storm_settings},
> +	{},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +	int rc, irq_index;
> +	struct device_node *np;
> +	const struct of_device_id *matched_np;
> +	struct xgene_msi *xgene_msi;
> +	xgene_msi_initcall_t init_fn;
> +	u32 nr_hw_irqs, nr_msi_vecs;
> +
> +	np = of_find_matching_node_and_match(NULL,
> +			     xgene_msi_match_table, &matched_np);
> +	if (!np)
> +		return -ENODEV;
> +
> +	xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
> +	if (!xgene_msi) {
> +		dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
> +		return -ENOMEM;
> +	}
> +
> +	init_fn = (xgene_msi_initcall_t) matched_np->data;
> +	rc = init_fn(xgene_msi);
> +	if (rc)
> +		return rc;
> +
> +	platform_set_drvdata(pdev, xgene_msi);
> +
> +	nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
> +	xgene_msi->irqhost = irq_domain_add_linear(pdev->dev.of_node,
> +			     nr_msi_vecs, &xgene_msi_host_ops, xgene_msi);
> +	if (!xgene_msi->irqhost) {
> +		dev_err(&pdev->dev, "No memory for MSI irqhost\n");
> +		rc = -ENOMEM;
> +		goto error;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(xgene_msi->msi_regs)) {
> +		dev_err(&pdev->dev, "no reg space\n");
> +		rc = -EINVAL;
> +		goto error;
> +	}
> +
> +	xgene_msi->msi_addr_hi = upper_32_bits(res->start);
> +	xgene_msi->msi_addr_lo = lower_32_bits(res->start);
> +
> +	rc = xgene_msi_init_allocator(xgene_msi);
> +	if (rc) {
> +		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +		goto error;
> +	}
> +
> +	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
> +	for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
> +		rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
> +		if (rc)
> +			goto error;
> +	}
> +
> +	xgene_msi->msi_chip.dev = &pdev->dev;
> +	xgene_msi->msi_chip.of_node = np;
> +	xgene_msi->msi_chip.setup_irq = xgene_msi_setup_irq;
> +	xgene_msi->msi_chip.teardown_irq = xgene_msi_teardown_irq;
> +
> +	rc = of_pci_msi_chip_add(&xgene_msi->msi_chip);
> +	if (rc) {
> +		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +		goto error;
> +	}
> +
> +	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +	return 0;
> +error:
> +	xgene_msi_remove(pdev);
> +	return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +	.driver = {
> +		.name = "xgene-msi",
> +		.owner = THIS_MODULE,
> +		.of_match_table = xgene_msi_match_table,
> +	},
> +	.probe = xgene_msi_probe,
> +	.remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +	return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> +MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
> +MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index ee082c0..63d58e6 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>  	return 0;
>  }
>  
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +	struct device_node *msi_node;
> +
> +	msi_node = of_parse_phandle(bus->dev.of_node,
> +					"msi-parent", 0);
> +	if (!msi_node)
> +		return -ENODEV;
> +
> +	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +	if (bus->msi)
> +		bus->msi->dev = &bus->dev;
> +	else
> +		return -ENODEV;
> +	return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>  	struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,14 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  	if (!bus)
>  		return -ENOMEM;
>  
> +	if (IS_ENABLED(CONFIG_PCI_MSI)) {
> +		ret = xgene_pcie_msi_enable(bus);
> +		if (ret) {
> +			dev_err(port->dev, "failed to enable MSI\n");
> +			return ret;
> +		}
> +	}
> +
>  	pci_scan_child_bus(bus);
>  	pci_assign_unassigned_bus_resources(bus);
>  	pci_bus_add_devices(bus);
> 


-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-03-18 18:05             ` Marc Zyngier
@ 2015-03-18 18:29               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-18 18:29 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau,
	Feng Kan, linux-pci, Tanmay Inamdar, linux-arm-kernel, Loc Ho

On Wed, Mar 18, 2015 at 11:05 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 04/03/15 19:39, Duc Dang wrote:
>> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
>> 16 HW IRQ lines.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>
> I just had a quick look at this, and this seems to be going in the exact
> opposite direction compared to what we now have on arm64, where we move
> away from using struct msi_controller for most thing, and implement PCI
> MSI/MSIX in a generic way, using MSI domains.
>
> I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI
> support. You can also have a look at what I did for the Tegra MSI
> controller in this patch:
>
> https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081
>
> Eventually, the plan is to kill msi_controller entirely, so introducing
> new drivers that rely on it is not something I'm eager to see.

Thanks, Marc.

 X-Gene 1 MSI is handled by separate MSI controller block, so its
driver implementation is different from GICv2m and GICv3. I will refer
to what you did for Tegra MSI, but I don't see your latest changes in
4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?

Regards,
Duc Dang.
>
> Thanks,
>
>         M.
>
>> ---
>>  drivers/pci/host/Kconfig         |   4 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 393 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  25 +++
>>  4 files changed, 423 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 7b892a9..6c0f98c 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -84,11 +84,15 @@ config PCIE_XILINX
>>         Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
>>         Host Bridge driver.
>>
>> +config PCI_XGENE_MSI
>> +     bool
>> +
>>  config PCI_XGENE
>>       bool "X-Gene PCIe controller"
>>       depends on ARCH_XGENE
>>       depends on OF
>>       select PCIEPORTBUS
>> +     select PCI_XGENE_MSI if PCI_MSI
>>       help
>>         Say Y here if you want internal PCI support on APM X-Gene SoC.
>>         There are 5 internal PCIe ports available. Each port is GEN3 capable
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index e61d91c..f39bde3 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..e1cab39
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,393 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *      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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_INDEX0           0x000000
>> +#define MSI_INT0             0x800000
>> +
>> +struct xgene_msi_settings {
>> +     u32     index_per_group;
>> +     u32     irqs_per_index;
>> +     u32     nr_msi_vec;
>> +     u32     nr_hw_irqs;
>> +};
>> +
>> +struct xgene_msi {
>> +     struct irq_domain               *irqhost;
>> +     struct msi_controller           msi_chip;
>> +     struct xgene_msi_settings       *settings;
>> +     u32                             msi_addr_lo;
>> +     u32                             msi_addr_hi;
>> +     void __iomem                    *msi_regs;
>> +     unsigned long                   *bitmap;
>> +     struct mutex                    bitmap_lock;
>> +     int                             *msi_virqs;
>> +};
>> +
>> +static inline struct xgene_msi *to_xgene_msi(struct msi_controller *msi_chip)
>> +{
>> +     return container_of(msi_chip, struct xgene_msi, msi_chip);
>> +}
>> +
>> +struct xgene_msi_settings storm_msi_settings = {
>> +     .index_per_group        = 8,
>> +     .irqs_per_index         = 21,
>> +     .nr_msi_vec             = 2688,
>> +     .nr_hw_irqs             = 16,
>> +};
>> +
>> +typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
>> +
>> +static inline irq_hw_number_t virq_to_hw(unsigned int virq)
>> +{
>> +     struct irq_data *irq_data = irq_get_irq_data(virq);
>> +
>> +     return WARN_ON(!irq_data) ? 0 : irq_data->hwirq;
>> +}
>> +
>> +static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
>> +{
>> +     xgene_msi->settings = &storm_msi_settings;
>> +     return 0;
>> +}
>> +
>> +static struct irq_chip xgene_msi_chip = {
>> +     .name           = "X-Gene-1 MSI",
>> +     .irq_enable     = pci_msi_unmask_irq,
>> +     .irq_disable    = pci_msi_mask_irq,
>> +     .irq_mask       = pci_msi_mask_irq,
>> +     .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static int xgene_msi_host_map(struct irq_domain *h, unsigned int virq,
>> +                           irq_hw_number_t hw)
>> +{
>> +     irq_set_chip_and_handler(virq, &xgene_msi_chip, handle_simple_irq);
>> +     irq_set_chip_data(virq, h->host_data);
>> +     set_irq_flags(irq, IRQF_VALID);
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct irq_domain_ops xgene_msi_host_ops = {
>> +     .map = xgene_msi_host_map,
>> +};
>> +
>> +static int xgene_msi_alloc(struct xgene_msi *xgene_msi)
>> +{
>> +     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>> +     int msi;
>> +
>> +     mutex_lock(&xgene_msi->bitmap_lock);
>> +
>> +     msi = find_first_zero_bit(xgene_msi->bitmap, msi_irq_count);
>> +     if (msi < msi_irq_count)
>> +             set_bit(msi, xgene_msi->bitmap);
>> +     else
>> +             msi = -ENOSPC;
>> +
>> +     mutex_unlock(&xgene_msi->bitmap_lock);
>> +
>> +     return msi;
>> +}
>> +
>> +static void xgene_msi_free(struct xgene_msi *xgene_msi, unsigned long irq)
>> +{
>> +     mutex_lock(&xgene_msi->bitmap_lock);
>> +
>> +     if (!test_bit(irq, xgene_msi->bitmap))
>> +             pr_err("trying to free unused MSI#%lu\n", irq);
>> +     else
>> +             clear_bit(irq, xgene_msi->bitmap);
>> +
>> +     mutex_unlock(&xgene_msi->bitmap_lock);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>> +     u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
>> +     int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
>> +
>> +     xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +     if (!xgene_msi->bitmap)
>> +             return -ENOMEM;
>> +     mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +     xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
>> +     if (!xgene_msi->msi_virqs)
>> +             return -ENOMEM;
>> +     return 0;
>> +}
>> +
>> +static void xgene_msi_teardown_irq(struct msi_controller *chip,
>> +                                             unsigned int irq)
>> +{
>> +     struct xgene_msi *xgene_msi = to_xgene_msi(chip);
>> +
>> +     irq_set_msi_desc(irq, NULL);
>> +     xgene_msi_free(xgene_msi, virq_to_hw(irq));
>> +}
>> +
>> +static void xgene_compose_msi_msg(struct pci_dev *dev, int hwirq,
>> +                               struct msi_msg *msg,
>> +                               struct xgene_msi *xgene_msi)
>> +{
>> +     u32 nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +     u32 irqs_per_index = xgene_msi->settings->irqs_per_index;
>> +     u32 reg_set = hwirq / (nr_hw_irqs * irqs_per_index);
>> +     u32 group = hwirq % nr_hw_irqs;
>> +
>> +     msg->address_hi = xgene_msi->msi_addr_hi;
>> +     msg->address_lo = xgene_msi->msi_addr_lo +
>> +                       (((8 * group) + reg_set) << 16);
>> +     msg->data = (hwirq / nr_hw_irqs) % irqs_per_index;
>> +}
>> +
>> +static int xgene_msi_setup_irq(struct msi_controller *chip,
>> +                             struct pci_dev *pdev, struct msi_desc *desc)
>> +{
>> +     struct xgene_msi *xgene_msi = to_xgene_msi(chip);
>> +     struct msi_msg msg;
>> +     unsigned long virq, gic_irq;
>> +     int hwirq;
>> +
>> +     hwirq = xgene_msi_alloc(xgene_msi);
>> +     if (hwirq < 0) {
>> +             dev_err(&pdev->dev, "failed to allocate MSI\n");
>> +             return -ENOSPC;
>> +     }
>> +
>> +     virq = irq_create_mapping(xgene_msi->irqhost, hwirq);
>> +     if (virq == 0) {
>> +             dev_err(&pdev->dev, "failed to map hwirq %i\n", hwirq);
>> +             return -ENOSPC;
>> +     }
>> +
>> +     gic_irq = xgene_msi->msi_virqs[hwirq %
>> +                     xgene_msi->settings->nr_hw_irqs];
>> +     pr_debug("Mapp HWIRQ %d on GIC IRQ %lu TO VIRQ %lu\n",
>> +              hwirq, gic_irq, virq);
>> +     irq_set_msi_desc(virq, desc);
>> +     xgene_compose_msi_msg(pdev, hwirq, &msg, xgene_msi);
>> +     irq_set_handler_data(virq, (void *)gic_irq);
>> +     write_msi_msg(virq, &msg);
>> +
>> +     return 0;
>> +}
>> +
>> +static irqreturn_t xgene_msi_isr(int irq, void *data)
>> +{
>> +     struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
>> +     unsigned int virq;
>> +     int msir_index, msir_reg, msir_val, hw_irq;
>> +     u32 intr_index, grp_select, msi_grp, processed = 0;
>> +     u32 nr_hw_irqs, irqs_per_index, index_per_group;
>> +
>> +     msi_grp = irq - xgene_msi->msi_virqs[0];
>> +     if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
>> +             pr_err("invalid msi received\n");
>> +             return IRQ_NONE;
>> +     }
>> +
>> +     nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +     irqs_per_index = xgene_msi->settings->irqs_per_index;
>> +     index_per_group = xgene_msi->settings->index_per_group;
>> +
>> +     grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +     while (grp_select) {
>> +             msir_index = ffs(grp_select) - 1;
>> +             msir_reg = (msi_grp << 19) + (msir_index << 16);
>> +             msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
>> +             while (msir_val) {
>> +                     intr_index = ffs(msir_val) - 1;
>> +                     hw_irq = (((msir_index * irqs_per_index) + intr_index) *
>> +                              nr_hw_irqs) + msi_grp;
>> +                     virq = irq_find_mapping(xgene_msi->irqhost, hw_irq);
>> +                     if (virq != 0)
>> +                             generic_handle_irq(virq);
>> +                     msir_val &= ~(1 << intr_index);
>> +                     processed++;
>> +             }
>> +             grp_select &= ~(1 << msir_index);
>> +     }
>> +
>> +     return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
>> +}
>> +
>> +static int xgene_msi_remove(struct platform_device *pdev)
>> +{
>> +     int virq, i;
>> +     struct xgene_msi *msi = platform_get_drvdata(pdev);
>> +     u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
>> +
>> +     for (i = 0; i < nr_hw_irqs; i++) {
>> +             virq = msi->msi_virqs[i];
>> +             if (virq != 0)
>> +                     free_irq(virq, msi);
>> +     }
>> +
>> +     kfree(msi->bitmap);
>> +     msi->bitmap = NULL;
>> +
>> +     return 0;
>> +}
>> +
>> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
>> +                              struct platform_device *pdev,
>> +                              int irq_index)
>> +{
>> +     int virt_msir;
>> +     cpumask_var_t mask;
>> +     int err;
>> +
>> +     virt_msir = platform_get_irq(pdev, irq_index);
>> +     if (virt_msir < 0) {
>> +             dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
>> +                     irq_index);
>> +             return -EINVAL;
>> +     }
>> +
>> +     err = request_irq(virt_msir, xgene_msi_isr, 0, "xgene-msi", msi);
>> +     if (err) {
>> +             dev_err(&pdev->dev, "request irq failed\n");
>> +             return err;
>> +     }
>> +
>> +     if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
>> +             cpumask_setall(mask);
>> +             irq_set_affinity(virt_msir, mask);
>> +             free_cpumask_var(mask);
>> +     }
>> +
>> +     msi->msi_virqs[irq_index] = virt_msir;
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct of_device_id xgene_msi_match_table[] = {
>> +     {.compatible = "apm,xgene1-msi",
>> +      .data = xgene_msi_init_storm_settings},
>> +     {},
>> +};
>> +
>> +static int xgene_msi_probe(struct platform_device *pdev)
>> +{
>> +     struct resource *res;
>> +     int rc, irq_index;
>> +     struct device_node *np;
>> +     const struct of_device_id *matched_np;
>> +     struct xgene_msi *xgene_msi;
>> +     xgene_msi_initcall_t init_fn;
>> +     u32 nr_hw_irqs, nr_msi_vecs;
>> +
>> +     np = of_find_matching_node_and_match(NULL,
>> +                          xgene_msi_match_table, &matched_np);
>> +     if (!np)
>> +             return -ENODEV;
>> +
>> +     xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
>> +     if (!xgene_msi) {
>> +             dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     init_fn = (xgene_msi_initcall_t) matched_np->data;
>> +     rc = init_fn(xgene_msi);
>> +     if (rc)
>> +             return rc;
>> +
>> +     platform_set_drvdata(pdev, xgene_msi);
>> +
>> +     nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
>> +     xgene_msi->irqhost = irq_domain_add_linear(pdev->dev.of_node,
>> +                          nr_msi_vecs, &xgene_msi_host_ops, xgene_msi);
>> +     if (!xgene_msi->irqhost) {
>> +             dev_err(&pdev->dev, "No memory for MSI irqhost\n");
>> +             rc = -ENOMEM;
>> +             goto error;
>> +     }
>> +
>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +     xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
>> +     if (IS_ERR(xgene_msi->msi_regs)) {
>> +             dev_err(&pdev->dev, "no reg space\n");
>> +             rc = -EINVAL;
>> +             goto error;
>> +     }
>> +
>> +     xgene_msi->msi_addr_hi = upper_32_bits(res->start);
>> +     xgene_msi->msi_addr_lo = lower_32_bits(res->start);
>> +
>> +     rc = xgene_msi_init_allocator(xgene_msi);
>> +     if (rc) {
>> +             dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
>> +             goto error;
>> +     }
>> +
>> +     nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +     for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
>> +             rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
>> +             if (rc)
>> +                     goto error;
>> +     }
>> +
>> +     xgene_msi->msi_chip.dev = &pdev->dev;
>> +     xgene_msi->msi_chip.of_node = np;
>> +     xgene_msi->msi_chip.setup_irq = xgene_msi_setup_irq;
>> +     xgene_msi->msi_chip.teardown_irq = xgene_msi_teardown_irq;
>> +
>> +     rc = of_pci_msi_chip_add(&xgene_msi->msi_chip);
>> +     if (rc) {
>> +             dev_err(&pdev->dev, "failed to add MSI controller chip\n");
>> +             goto error;
>> +     }
>> +
>> +     dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
>> +
>> +     return 0;
>> +error:
>> +     xgene_msi_remove(pdev);
>> +     return rc;
>> +}
>> +
>> +static struct platform_driver xgene_msi_driver = {
>> +     .driver = {
>> +             .name = "xgene-msi",
>> +             .owner = THIS_MODULE,
>> +             .of_match_table = xgene_msi_match_table,
>> +     },
>> +     .probe = xgene_msi_probe,
>> +     .remove = xgene_msi_remove,
>> +};
>> +
>> +static int __init xgene_pcie_msi_init(void)
>> +{
>> +     return platform_driver_register(&xgene_msi_driver);
>> +}
>> +subsys_initcall(xgene_pcie_msi_init);
>> +
>> +MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
>> +MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
>> index ee082c0..63d58e6 100644
>> --- a/drivers/pci/host/pci-xgene.c
>> +++ b/drivers/pci/host/pci-xgene.c
>> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>>       return 0;
>>  }
>>
>> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
>> +{
>> +     struct device_node *msi_node;
>> +
>> +     msi_node = of_parse_phandle(bus->dev.of_node,
>> +                                     "msi-parent", 0);
>> +     if (!msi_node)
>> +             return -ENODEV;
>> +
>> +     bus->msi = of_pci_find_msi_chip_by_node(msi_node);
>> +     if (bus->msi)
>> +             bus->msi->dev = &bus->dev;
>> +     else
>> +             return -ENODEV;
>> +     return 0;
>> +}
>> +
>>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>  {
>>       struct device_node *dn = pdev->dev.of_node;
>> @@ -504,6 +521,14 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>       if (!bus)
>>               return -ENOMEM;
>>
>> +     if (IS_ENABLED(CONFIG_PCI_MSI)) {
>> +             ret = xgene_pcie_msi_enable(bus);
>> +             if (ret) {
>> +                     dev_err(port->dev, "failed to enable MSI\n");
>> +                     return ret;
>> +             }
>> +     }
>> +
>>       pci_scan_child_bus(bus);
>>       pci_assign_unassigned_bus_resources(bus);
>>       pci_bus_add_devices(bus);
>>
>
>
> --
> Jazz is not dead. It just smells funny...

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

* [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-03-18 18:29               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-18 18:29 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 18, 2015 at 11:05 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 04/03/15 19:39, Duc Dang wrote:
>> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
>> 16 HW IRQ lines.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>
> I just had a quick look at this, and this seems to be going in the exact
> opposite direction compared to what we now have on arm64, where we move
> away from using struct msi_controller for most thing, and implement PCI
> MSI/MSIX in a generic way, using MSI domains.
>
> I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI
> support. You can also have a look at what I did for the Tegra MSI
> controller in this patch:
>
> https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081
>
> Eventually, the plan is to kill msi_controller entirely, so introducing
> new drivers that rely on it is not something I'm eager to see.

Thanks, Marc.

 X-Gene 1 MSI is handled by separate MSI controller block, so its
driver implementation is different from GICv2m and GICv3. I will refer
to what you did for Tegra MSI, but I don't see your latest changes in
4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?

Regards,
Duc Dang.
>
> Thanks,
>
>         M.
>
>> ---
>>  drivers/pci/host/Kconfig         |   4 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 393 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  25 +++
>>  4 files changed, 423 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 7b892a9..6c0f98c 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -84,11 +84,15 @@ config PCIE_XILINX
>>         Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
>>         Host Bridge driver.
>>
>> +config PCI_XGENE_MSI
>> +     bool
>> +
>>  config PCI_XGENE
>>       bool "X-Gene PCIe controller"
>>       depends on ARCH_XGENE
>>       depends on OF
>>       select PCIEPORTBUS
>> +     select PCI_XGENE_MSI if PCI_MSI
>>       help
>>         Say Y here if you want internal PCI support on APM X-Gene SoC.
>>         There are 5 internal PCIe ports available. Each port is GEN3 capable
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index e61d91c..f39bde3 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..e1cab39
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,393 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *      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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_INDEX0           0x000000
>> +#define MSI_INT0             0x800000
>> +
>> +struct xgene_msi_settings {
>> +     u32     index_per_group;
>> +     u32     irqs_per_index;
>> +     u32     nr_msi_vec;
>> +     u32     nr_hw_irqs;
>> +};
>> +
>> +struct xgene_msi {
>> +     struct irq_domain               *irqhost;
>> +     struct msi_controller           msi_chip;
>> +     struct xgene_msi_settings       *settings;
>> +     u32                             msi_addr_lo;
>> +     u32                             msi_addr_hi;
>> +     void __iomem                    *msi_regs;
>> +     unsigned long                   *bitmap;
>> +     struct mutex                    bitmap_lock;
>> +     int                             *msi_virqs;
>> +};
>> +
>> +static inline struct xgene_msi *to_xgene_msi(struct msi_controller *msi_chip)
>> +{
>> +     return container_of(msi_chip, struct xgene_msi, msi_chip);
>> +}
>> +
>> +struct xgene_msi_settings storm_msi_settings = {
>> +     .index_per_group        = 8,
>> +     .irqs_per_index         = 21,
>> +     .nr_msi_vec             = 2688,
>> +     .nr_hw_irqs             = 16,
>> +};
>> +
>> +typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
>> +
>> +static inline irq_hw_number_t virq_to_hw(unsigned int virq)
>> +{
>> +     struct irq_data *irq_data = irq_get_irq_data(virq);
>> +
>> +     return WARN_ON(!irq_data) ? 0 : irq_data->hwirq;
>> +}
>> +
>> +static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
>> +{
>> +     xgene_msi->settings = &storm_msi_settings;
>> +     return 0;
>> +}
>> +
>> +static struct irq_chip xgene_msi_chip = {
>> +     .name           = "X-Gene-1 MSI",
>> +     .irq_enable     = pci_msi_unmask_irq,
>> +     .irq_disable    = pci_msi_mask_irq,
>> +     .irq_mask       = pci_msi_mask_irq,
>> +     .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static int xgene_msi_host_map(struct irq_domain *h, unsigned int virq,
>> +                           irq_hw_number_t hw)
>> +{
>> +     irq_set_chip_and_handler(virq, &xgene_msi_chip, handle_simple_irq);
>> +     irq_set_chip_data(virq, h->host_data);
>> +     set_irq_flags(irq, IRQF_VALID);
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct irq_domain_ops xgene_msi_host_ops = {
>> +     .map = xgene_msi_host_map,
>> +};
>> +
>> +static int xgene_msi_alloc(struct xgene_msi *xgene_msi)
>> +{
>> +     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>> +     int msi;
>> +
>> +     mutex_lock(&xgene_msi->bitmap_lock);
>> +
>> +     msi = find_first_zero_bit(xgene_msi->bitmap, msi_irq_count);
>> +     if (msi < msi_irq_count)
>> +             set_bit(msi, xgene_msi->bitmap);
>> +     else
>> +             msi = -ENOSPC;
>> +
>> +     mutex_unlock(&xgene_msi->bitmap_lock);
>> +
>> +     return msi;
>> +}
>> +
>> +static void xgene_msi_free(struct xgene_msi *xgene_msi, unsigned long irq)
>> +{
>> +     mutex_lock(&xgene_msi->bitmap_lock);
>> +
>> +     if (!test_bit(irq, xgene_msi->bitmap))
>> +             pr_err("trying to free unused MSI#%lu\n", irq);
>> +     else
>> +             clear_bit(irq, xgene_msi->bitmap);
>> +
>> +     mutex_unlock(&xgene_msi->bitmap_lock);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>> +     u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
>> +     int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
>> +
>> +     xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +     if (!xgene_msi->bitmap)
>> +             return -ENOMEM;
>> +     mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +     xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
>> +     if (!xgene_msi->msi_virqs)
>> +             return -ENOMEM;
>> +     return 0;
>> +}
>> +
>> +static void xgene_msi_teardown_irq(struct msi_controller *chip,
>> +                                             unsigned int irq)
>> +{
>> +     struct xgene_msi *xgene_msi = to_xgene_msi(chip);
>> +
>> +     irq_set_msi_desc(irq, NULL);
>> +     xgene_msi_free(xgene_msi, virq_to_hw(irq));
>> +}
>> +
>> +static void xgene_compose_msi_msg(struct pci_dev *dev, int hwirq,
>> +                               struct msi_msg *msg,
>> +                               struct xgene_msi *xgene_msi)
>> +{
>> +     u32 nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +     u32 irqs_per_index = xgene_msi->settings->irqs_per_index;
>> +     u32 reg_set = hwirq / (nr_hw_irqs * irqs_per_index);
>> +     u32 group = hwirq % nr_hw_irqs;
>> +
>> +     msg->address_hi = xgene_msi->msi_addr_hi;
>> +     msg->address_lo = xgene_msi->msi_addr_lo +
>> +                       (((8 * group) + reg_set) << 16);
>> +     msg->data = (hwirq / nr_hw_irqs) % irqs_per_index;
>> +}
>> +
>> +static int xgene_msi_setup_irq(struct msi_controller *chip,
>> +                             struct pci_dev *pdev, struct msi_desc *desc)
>> +{
>> +     struct xgene_msi *xgene_msi = to_xgene_msi(chip);
>> +     struct msi_msg msg;
>> +     unsigned long virq, gic_irq;
>> +     int hwirq;
>> +
>> +     hwirq = xgene_msi_alloc(xgene_msi);
>> +     if (hwirq < 0) {
>> +             dev_err(&pdev->dev, "failed to allocate MSI\n");
>> +             return -ENOSPC;
>> +     }
>> +
>> +     virq = irq_create_mapping(xgene_msi->irqhost, hwirq);
>> +     if (virq == 0) {
>> +             dev_err(&pdev->dev, "failed to map hwirq %i\n", hwirq);
>> +             return -ENOSPC;
>> +     }
>> +
>> +     gic_irq = xgene_msi->msi_virqs[hwirq %
>> +                     xgene_msi->settings->nr_hw_irqs];
>> +     pr_debug("Mapp HWIRQ %d on GIC IRQ %lu TO VIRQ %lu\n",
>> +              hwirq, gic_irq, virq);
>> +     irq_set_msi_desc(virq, desc);
>> +     xgene_compose_msi_msg(pdev, hwirq, &msg, xgene_msi);
>> +     irq_set_handler_data(virq, (void *)gic_irq);
>> +     write_msi_msg(virq, &msg);
>> +
>> +     return 0;
>> +}
>> +
>> +static irqreturn_t xgene_msi_isr(int irq, void *data)
>> +{
>> +     struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
>> +     unsigned int virq;
>> +     int msir_index, msir_reg, msir_val, hw_irq;
>> +     u32 intr_index, grp_select, msi_grp, processed = 0;
>> +     u32 nr_hw_irqs, irqs_per_index, index_per_group;
>> +
>> +     msi_grp = irq - xgene_msi->msi_virqs[0];
>> +     if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
>> +             pr_err("invalid msi received\n");
>> +             return IRQ_NONE;
>> +     }
>> +
>> +     nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +     irqs_per_index = xgene_msi->settings->irqs_per_index;
>> +     index_per_group = xgene_msi->settings->index_per_group;
>> +
>> +     grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +     while (grp_select) {
>> +             msir_index = ffs(grp_select) - 1;
>> +             msir_reg = (msi_grp << 19) + (msir_index << 16);
>> +             msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
>> +             while (msir_val) {
>> +                     intr_index = ffs(msir_val) - 1;
>> +                     hw_irq = (((msir_index * irqs_per_index) + intr_index) *
>> +                              nr_hw_irqs) + msi_grp;
>> +                     virq = irq_find_mapping(xgene_msi->irqhost, hw_irq);
>> +                     if (virq != 0)
>> +                             generic_handle_irq(virq);
>> +                     msir_val &= ~(1 << intr_index);
>> +                     processed++;
>> +             }
>> +             grp_select &= ~(1 << msir_index);
>> +     }
>> +
>> +     return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
>> +}
>> +
>> +static int xgene_msi_remove(struct platform_device *pdev)
>> +{
>> +     int virq, i;
>> +     struct xgene_msi *msi = platform_get_drvdata(pdev);
>> +     u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
>> +
>> +     for (i = 0; i < nr_hw_irqs; i++) {
>> +             virq = msi->msi_virqs[i];
>> +             if (virq != 0)
>> +                     free_irq(virq, msi);
>> +     }
>> +
>> +     kfree(msi->bitmap);
>> +     msi->bitmap = NULL;
>> +
>> +     return 0;
>> +}
>> +
>> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
>> +                              struct platform_device *pdev,
>> +                              int irq_index)
>> +{
>> +     int virt_msir;
>> +     cpumask_var_t mask;
>> +     int err;
>> +
>> +     virt_msir = platform_get_irq(pdev, irq_index);
>> +     if (virt_msir < 0) {
>> +             dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
>> +                     irq_index);
>> +             return -EINVAL;
>> +     }
>> +
>> +     err = request_irq(virt_msir, xgene_msi_isr, 0, "xgene-msi", msi);
>> +     if (err) {
>> +             dev_err(&pdev->dev, "request irq failed\n");
>> +             return err;
>> +     }
>> +
>> +     if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
>> +             cpumask_setall(mask);
>> +             irq_set_affinity(virt_msir, mask);
>> +             free_cpumask_var(mask);
>> +     }
>> +
>> +     msi->msi_virqs[irq_index] = virt_msir;
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct of_device_id xgene_msi_match_table[] = {
>> +     {.compatible = "apm,xgene1-msi",
>> +      .data = xgene_msi_init_storm_settings},
>> +     {},
>> +};
>> +
>> +static int xgene_msi_probe(struct platform_device *pdev)
>> +{
>> +     struct resource *res;
>> +     int rc, irq_index;
>> +     struct device_node *np;
>> +     const struct of_device_id *matched_np;
>> +     struct xgene_msi *xgene_msi;
>> +     xgene_msi_initcall_t init_fn;
>> +     u32 nr_hw_irqs, nr_msi_vecs;
>> +
>> +     np = of_find_matching_node_and_match(NULL,
>> +                          xgene_msi_match_table, &matched_np);
>> +     if (!np)
>> +             return -ENODEV;
>> +
>> +     xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
>> +     if (!xgene_msi) {
>> +             dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     init_fn = (xgene_msi_initcall_t) matched_np->data;
>> +     rc = init_fn(xgene_msi);
>> +     if (rc)
>> +             return rc;
>> +
>> +     platform_set_drvdata(pdev, xgene_msi);
>> +
>> +     nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
>> +     xgene_msi->irqhost = irq_domain_add_linear(pdev->dev.of_node,
>> +                          nr_msi_vecs, &xgene_msi_host_ops, xgene_msi);
>> +     if (!xgene_msi->irqhost) {
>> +             dev_err(&pdev->dev, "No memory for MSI irqhost\n");
>> +             rc = -ENOMEM;
>> +             goto error;
>> +     }
>> +
>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +     xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
>> +     if (IS_ERR(xgene_msi->msi_regs)) {
>> +             dev_err(&pdev->dev, "no reg space\n");
>> +             rc = -EINVAL;
>> +             goto error;
>> +     }
>> +
>> +     xgene_msi->msi_addr_hi = upper_32_bits(res->start);
>> +     xgene_msi->msi_addr_lo = lower_32_bits(res->start);
>> +
>> +     rc = xgene_msi_init_allocator(xgene_msi);
>> +     if (rc) {
>> +             dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
>> +             goto error;
>> +     }
>> +
>> +     nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +     for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
>> +             rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
>> +             if (rc)
>> +                     goto error;
>> +     }
>> +
>> +     xgene_msi->msi_chip.dev = &pdev->dev;
>> +     xgene_msi->msi_chip.of_node = np;
>> +     xgene_msi->msi_chip.setup_irq = xgene_msi_setup_irq;
>> +     xgene_msi->msi_chip.teardown_irq = xgene_msi_teardown_irq;
>> +
>> +     rc = of_pci_msi_chip_add(&xgene_msi->msi_chip);
>> +     if (rc) {
>> +             dev_err(&pdev->dev, "failed to add MSI controller chip\n");
>> +             goto error;
>> +     }
>> +
>> +     dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
>> +
>> +     return 0;
>> +error:
>> +     xgene_msi_remove(pdev);
>> +     return rc;
>> +}
>> +
>> +static struct platform_driver xgene_msi_driver = {
>> +     .driver = {
>> +             .name = "xgene-msi",
>> +             .owner = THIS_MODULE,
>> +             .of_match_table = xgene_msi_match_table,
>> +     },
>> +     .probe = xgene_msi_probe,
>> +     .remove = xgene_msi_remove,
>> +};
>> +
>> +static int __init xgene_pcie_msi_init(void)
>> +{
>> +     return platform_driver_register(&xgene_msi_driver);
>> +}
>> +subsys_initcall(xgene_pcie_msi_init);
>> +
>> +MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
>> +MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
>> index ee082c0..63d58e6 100644
>> --- a/drivers/pci/host/pci-xgene.c
>> +++ b/drivers/pci/host/pci-xgene.c
>> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>>       return 0;
>>  }
>>
>> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
>> +{
>> +     struct device_node *msi_node;
>> +
>> +     msi_node = of_parse_phandle(bus->dev.of_node,
>> +                                     "msi-parent", 0);
>> +     if (!msi_node)
>> +             return -ENODEV;
>> +
>> +     bus->msi = of_pci_find_msi_chip_by_node(msi_node);
>> +     if (bus->msi)
>> +             bus->msi->dev = &bus->dev;
>> +     else
>> +             return -ENODEV;
>> +     return 0;
>> +}
>> +
>>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>  {
>>       struct device_node *dn = pdev->dev.of_node;
>> @@ -504,6 +521,14 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>       if (!bus)
>>               return -ENOMEM;
>>
>> +     if (IS_ENABLED(CONFIG_PCI_MSI)) {
>> +             ret = xgene_pcie_msi_enable(bus);
>> +             if (ret) {
>> +                     dev_err(port->dev, "failed to enable MSI\n");
>> +                     return ret;
>> +             }
>> +     }
>> +
>>       pci_scan_child_bus(bus);
>>       pci_assign_unassigned_bus_resources(bus);
>>       pci_bus_add_devices(bus);
>>
>
>
> --
> Jazz is not dead. It just smells funny...

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

* Re: [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-03-18 18:29               ` Duc Dang
@ 2015-03-18 18:52                 ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-03-18 18:52 UTC (permalink / raw)
  To: Duc Dang
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	Feng Kan, linux-pci, Tanmay Inamdar, linux-arm-kernel, Loc Ho

On 18/03/15 18:29, Duc Dang wrote:
> On Wed, Mar 18, 2015 at 11:05 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> On 04/03/15 19:39, Duc Dang wrote:
>>> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
>>> 16 HW IRQ lines.
>>>
>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>
>> I just had a quick look at this, and this seems to be going in the exact
>> opposite direction compared to what we now have on arm64, where we move
>> away from using struct msi_controller for most thing, and implement PCI
>> MSI/MSIX in a generic way, using MSI domains.
>>
>> I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI
>> support. You can also have a look at what I did for the Tegra MSI
>> controller in this patch:
>>
>> https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081
>>
>> Eventually, the plan is to kill msi_controller entirely, so introducing
>> new drivers that rely on it is not something I'm eager to see.
> 
> Thanks, Marc.
> 
>  X-Gene 1 MSI is handled by separate MSI controller block, so its
> driver implementation is different from GICv2m and GICv3. I will refer

It will certainly be different in the sense that you won't use a stacked
domain on top of the GIC. But what I want to see is the use of a generic
pci_msi_domain on top of an irq_domain, just like we have on v2m and v3.
Thomas has also been vocal enough about that in the past, and x86 is
going down that road as well.

> to what you did for Tegra MSI, but I don't see your latest changes in
> 4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?

Not yet. As you can see in this branch, this relies on some other
cleanups. But you can already convert most of your driver and put it in
the shape that matches what we have for v2m and v3. Once the required
cleanups are in, I'll remove the last traces of msi_controller myself if
necessary.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-03-18 18:52                 ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-03-18 18:52 UTC (permalink / raw)
  To: linux-arm-kernel

On 18/03/15 18:29, Duc Dang wrote:
> On Wed, Mar 18, 2015 at 11:05 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> On 04/03/15 19:39, Duc Dang wrote:
>>> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
>>> 16 HW IRQ lines.
>>>
>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>
>> I just had a quick look at this, and this seems to be going in the exact
>> opposite direction compared to what we now have on arm64, where we move
>> away from using struct msi_controller for most thing, and implement PCI
>> MSI/MSIX in a generic way, using MSI domains.
>>
>> I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI
>> support. You can also have a look at what I did for the Tegra MSI
>> controller in this patch:
>>
>> https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081
>>
>> Eventually, the plan is to kill msi_controller entirely, so introducing
>> new drivers that rely on it is not something I'm eager to see.
> 
> Thanks, Marc.
> 
>  X-Gene 1 MSI is handled by separate MSI controller block, so its
> driver implementation is different from GICv2m and GICv3. I will refer

It will certainly be different in the sense that you won't use a stacked
domain on top of the GIC. But what I want to see is the use of a generic
pci_msi_domain on top of an irq_domain, just like we have on v2m and v3.
Thomas has also been vocal enough about that in the past, and x86 is
going down that road as well.

> to what you did for Tegra MSI, but I don't see your latest changes in
> 4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?

Not yet. As you can see in this branch, this relies on some other
cleanups. But you can already convert most of your driver and put it in
the shape that matches what we have for v2m and v3. Once the required
cleanups are in, I'll remove the last traces of msi_controller myself if
necessary.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH v2 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-03-18 17:43             ` Duc Dang
@ 2015-03-19 20:49               ` Bjorn Helgaas
  -1 siblings, 0 replies; 230+ messages in thread
From: Bjorn Helgaas @ 2015-03-19 20:49 UTC (permalink / raw)
  To: Duc Dang
  Cc: Arnd Bergmann, Grant Likely, Liviu Dudau, linux-pci,
	linux-arm-kernel, Tanmay Inamdar, Loc Ho, Feng Kan, Marc Zyngier

[+cc Marc]

On Wed, Mar 18, 2015 at 10:43:10AM -0700, Duc Dang wrote:
> On Wed, Mar 4, 2015 at 11:39 AM, Duc Dang <dhdang@apm.com> wrote:
> > This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
> > APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> > to GIC V2M specification for MSI Termination.
> >
> > There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> > block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines
> > and shared across all 5 PCIe ports.
> >
> > v2 changes:
> >         1. Use msi_controller structure
> >         2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
> >
> >  .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
> >  MAINTAINERS                                        |   8 +
> >  arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
> >  drivers/pci/host/Kconfig                           |   4 +
> >  drivers/pci/host/Makefile                          |   1 +
> >  drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
> >  drivers/pci/host/pci-xgene.c                       |  25 ++
> >  7 files changed, 519 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> >  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> >
> > --
> > 1.9.1
> >
> Hi Bjorn, Arnd, and All,
> 
> Did you have a chance to take a look at this v2 patch set for X-Gene 1
> MSI support?

Marc had some comments, and as far as I can tell, you haven't addressed
them yet.  Am I mistaken?

Bjorn

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

* [PATCH v2 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-03-19 20:49               ` Bjorn Helgaas
  0 siblings, 0 replies; 230+ messages in thread
From: Bjorn Helgaas @ 2015-03-19 20:49 UTC (permalink / raw)
  To: linux-arm-kernel

[+cc Marc]

On Wed, Mar 18, 2015 at 10:43:10AM -0700, Duc Dang wrote:
> On Wed, Mar 4, 2015 at 11:39 AM, Duc Dang <dhdang@apm.com> wrote:
> > This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
> > APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> > to GIC V2M specification for MSI Termination.
> >
> > There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> > block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines
> > and shared across all 5 PCIe ports.
> >
> > v2 changes:
> >         1. Use msi_controller structure
> >         2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
> >
> >  .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
> >  MAINTAINERS                                        |   8 +
> >  arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
> >  drivers/pci/host/Kconfig                           |   4 +
> >  drivers/pci/host/Makefile                          |   1 +
> >  drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
> >  drivers/pci/host/pci-xgene.c                       |  25 ++
> >  7 files changed, 519 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> >  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> >
> > --
> > 1.9.1
> >
> Hi Bjorn, Arnd, and All,
> 
> Did you have a chance to take a look at this v2 patch set for X-Gene 1
> MSI support?

Marc had some comments, and as far as I can tell, you haven't addressed
them yet.  Am I mistaken?

Bjorn

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

* Re: [PATCH v2 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-03-19 20:49               ` Bjorn Helgaas
@ 2015-03-19 20:59                 ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-19 20:59 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: Arnd Bergmann, Grant Likely, Liviu Dudau, linux-pci,
	linux-arm-kernel, Tanmay Inamdar, Loc Ho, Feng Kan, Marc Zyngier

On Thu, Mar 19, 2015 at 1:49 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
> [+cc Marc]
>
> On Wed, Mar 18, 2015 at 10:43:10AM -0700, Duc Dang wrote:
>> On Wed, Mar 4, 2015 at 11:39 AM, Duc Dang <dhdang@apm.com> wrote:
>> > This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
>> > APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
>> > to GIC V2M specification for MSI Termination.
>> >
>> > There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
>> > block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines
>> > and shared across all 5 PCIe ports.
>> >
>> > v2 changes:
>> >         1. Use msi_controller structure
>> >         2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
>> >
>> >  .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
>> >  MAINTAINERS                                        |   8 +
>> >  arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
>> >  drivers/pci/host/Kconfig                           |   4 +
>> >  drivers/pci/host/Makefile                          |   1 +
>> >  drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
>> >  drivers/pci/host/pci-xgene.c                       |  25 ++
>> >  7 files changed, 519 insertions(+)
>> >  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
>> >  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>> >
>> > --
>> > 1.9.1
>> >
>> Hi Bjorn, Arnd, and All,
>>
>> Did you have a chance to take a look at this v2 patch set for X-Gene 1
>> MSI support?
>
> Marc had some comments, and as far as I can tell, you haven't addressed
> them yet.  Am I mistaken?
>
Hi Bjorn,

You are correct. I am making the changes as Marc pointed out. Will
update a new version soon.

Regards,
Duc Dang.

> Bjorn

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

* [PATCH v2 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-03-19 20:59                 ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-03-19 20:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Mar 19, 2015 at 1:49 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
> [+cc Marc]
>
> On Wed, Mar 18, 2015 at 10:43:10AM -0700, Duc Dang wrote:
>> On Wed, Mar 4, 2015 at 11:39 AM, Duc Dang <dhdang@apm.com> wrote:
>> > This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
>> > APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
>> > to GIC V2M specification for MSI Termination.
>> >
>> > There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
>> > block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines
>> > and shared across all 5 PCIe ports.
>> >
>> > v2 changes:
>> >         1. Use msi_controller structure
>> >         2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
>> >
>> >  .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
>> >  MAINTAINERS                                        |   8 +
>> >  arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
>> >  drivers/pci/host/Kconfig                           |   4 +
>> >  drivers/pci/host/Makefile                          |   1 +
>> >  drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
>> >  drivers/pci/host/pci-xgene.c                       |  25 ++
>> >  7 files changed, 519 insertions(+)
>> >  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
>> >  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>> >
>> > --
>> > 1.9.1
>> >
>> Hi Bjorn, Arnd, and All,
>>
>> Did you have a chance to take a look at this v2 patch set for X-Gene 1
>> MSI support?
>
> Marc had some comments, and as far as I can tell, you haven't addressed
> them yet.  Am I mistaken?
>
Hi Bjorn,

You are correct. I am making the changes as Marc pointed out. Will
update a new version soon.

Regards,
Duc Dang.

> Bjorn

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

* Re: [PATCH v2 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-03-19 20:59                 ` Duc Dang
@ 2015-03-19 21:08                   ` Bjorn Helgaas
  -1 siblings, 0 replies; 230+ messages in thread
From: Bjorn Helgaas @ 2015-03-19 21:08 UTC (permalink / raw)
  To: Duc Dang
  Cc: Arnd Bergmann, Grant Likely, Liviu Dudau, linux-pci,
	linux-arm-kernel, Tanmay Inamdar, Loc Ho, Feng Kan, Marc Zyngier

On Thu, Mar 19, 2015 at 01:59:35PM -0700, Duc Dang wrote:
> On Thu, Mar 19, 2015 at 1:49 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
> > [+cc Marc]
> >
> > On Wed, Mar 18, 2015 at 10:43:10AM -0700, Duc Dang wrote:
> >> On Wed, Mar 4, 2015 at 11:39 AM, Duc Dang <dhdang@apm.com> wrote:
> >> > This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
> >> > APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> >> > to GIC V2M specification for MSI Termination.
> >> >
> >> > There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> >> > block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines
> >> > and shared across all 5 PCIe ports.
> >> >
> >> > v2 changes:
> >> >         1. Use msi_controller structure
> >> >         2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
> >> >
> >> >  .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
> >> >  MAINTAINERS                                        |   8 +
> >> >  arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
> >> >  drivers/pci/host/Kconfig                           |   4 +
> >> >  drivers/pci/host/Makefile                          |   1 +
> >> >  drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
> >> >  drivers/pci/host/pci-xgene.c                       |  25 ++
> >> >  7 files changed, 519 insertions(+)
> >> >  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> >> >  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> >> >
> >> > --
> >> > 1.9.1
> >> >
> >> Hi Bjorn, Arnd, and All,
> >>
> >> Did you have a chance to take a look at this v2 patch set for X-Gene 1
> >> MSI support?
> >
> > Marc had some comments, and as far as I can tell, you haven't addressed
> > them yet.  Am I mistaken?
> >
> Hi Bjorn,
> 
> You are correct. I am making the changes as Marc pointed out. Will
> update a new version soon.

OK, thanks.  If other people provide feedback, I usually wait until that's
addressed before I spend much time looking at it, because I don't want to
spend time rediscovering things people have already commented on, and I
can't keep up with the list as it is :)

Bjorn

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

* [PATCH v2 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-03-19 21:08                   ` Bjorn Helgaas
  0 siblings, 0 replies; 230+ messages in thread
From: Bjorn Helgaas @ 2015-03-19 21:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Mar 19, 2015 at 01:59:35PM -0700, Duc Dang wrote:
> On Thu, Mar 19, 2015 at 1:49 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
> > [+cc Marc]
> >
> > On Wed, Mar 18, 2015 at 10:43:10AM -0700, Duc Dang wrote:
> >> On Wed, Mar 4, 2015 at 11:39 AM, Duc Dang <dhdang@apm.com> wrote:
> >> > This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
> >> > APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> >> > to GIC V2M specification for MSI Termination.
> >> >
> >> > There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> >> > block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines
> >> > and shared across all 5 PCIe ports.
> >> >
> >> > v2 changes:
> >> >         1. Use msi_controller structure
> >> >         2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
> >> >
> >> >  .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
> >> >  MAINTAINERS                                        |   8 +
> >> >  arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
> >> >  drivers/pci/host/Kconfig                           |   4 +
> >> >  drivers/pci/host/Makefile                          |   1 +
> >> >  drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
> >> >  drivers/pci/host/pci-xgene.c                       |  25 ++
> >> >  7 files changed, 519 insertions(+)
> >> >  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> >> >  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> >> >
> >> > --
> >> > 1.9.1
> >> >
> >> Hi Bjorn, Arnd, and All,
> >>
> >> Did you have a chance to take a look at this v2 patch set for X-Gene 1
> >> MSI support?
> >
> > Marc had some comments, and as far as I can tell, you haven't addressed
> > them yet.  Am I mistaken?
> >
> Hi Bjorn,
> 
> You are correct. I am making the changes as Marc pointed out. Will
> update a new version soon.

OK, thanks.  If other people provide feedback, I usually wait until that's
addressed before I spend much time looking at it, because I don't want to
spend time rediscovering things people have already commented on, and I
can't keep up with the list as it is :)

Bjorn

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

* Re: [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-03-18 18:52                 ` Marc Zyngier
@ 2015-04-07 19:56                   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-07 19:56 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	Feng Kan, linux-pci, Tanmay Inamdar, linux-arm-kernel, Loc Ho

On Wed, Mar 18, 2015 at 11:52 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 18/03/15 18:29, Duc Dang wrote:
>> On Wed, Mar 18, 2015 at 11:05 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>>> On 04/03/15 19:39, Duc Dang wrote:
>>>> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
>>>> 16 HW IRQ lines.
>>>>
>>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>>
>>> I just had a quick look at this, and this seems to be going in the exact
>>> opposite direction compared to what we now have on arm64, where we move
>>> away from using struct msi_controller for most thing, and implement PCI
>>> MSI/MSIX in a generic way, using MSI domains.
>>>
>>> I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI
>>> support. You can also have a look at what I did for the Tegra MSI
>>> controller in this patch:
>>>
>>> https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081
>>>
>>> Eventually, the plan is to kill msi_controller entirely, so introducing
>>> new drivers that rely on it is not something I'm eager to see.
>>
>> Thanks, Marc.
>>
>>  X-Gene 1 MSI is handled by separate MSI controller block, so its
>> driver implementation is different from GICv2m and GICv3. I will refer
>
> It will certainly be different in the sense that you won't use a stacked
> domain on top of the GIC. But what I want to see is the use of a generic
> pci_msi_domain on top of an irq_domain, just like we have on v2m and v3.
> Thomas has also been vocal enough about that in the past, and x86 is
> going down that road as well.
>
>> to what you did for Tegra MSI, but I don't see your latest changes in
>> 4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?
>
> Not yet. As you can see in this branch, this relies on some other
> cleanups. But you can already convert most of your driver and put it in
> the shape that matches what we have for v2m and v3. Once the required
> cleanups are in, I'll remove the last traces of msi_controller myself if
> necessary.
>

Hi Marc, Bjorn,

I follow Marc's suggestion and convert my X-Gene 1 MSI driver to
remove msi_controller struct and use generic pci_msi_domain on top of
an irq_domain. The code requires Marc's changes in
irq/kill-msi-controller branch to be compiled and function correctly,
so I plan to post the patch on top of Marc's tree. Please let me know
if you think I should have different approach to post this patch.

Another question I have is when do you plan to roll out this hierarchy
irq domain implementation for MSI, as I see some of Marc's changes to
kill msi_controller structure and implement MSI irq domain still does
not get into Bjorn's tree.

Regards,
Duc Dang.

> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...

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

* [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-07 19:56                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-07 19:56 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 18, 2015 at 11:52 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 18/03/15 18:29, Duc Dang wrote:
>> On Wed, Mar 18, 2015 at 11:05 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>>> On 04/03/15 19:39, Duc Dang wrote:
>>>> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
>>>> 16 HW IRQ lines.
>>>>
>>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>>
>>> I just had a quick look at this, and this seems to be going in the exact
>>> opposite direction compared to what we now have on arm64, where we move
>>> away from using struct msi_controller for most thing, and implement PCI
>>> MSI/MSIX in a generic way, using MSI domains.
>>>
>>> I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI
>>> support. You can also have a look at what I did for the Tegra MSI
>>> controller in this patch:
>>>
>>> https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081
>>>
>>> Eventually, the plan is to kill msi_controller entirely, so introducing
>>> new drivers that rely on it is not something I'm eager to see.
>>
>> Thanks, Marc.
>>
>>  X-Gene 1 MSI is handled by separate MSI controller block, so its
>> driver implementation is different from GICv2m and GICv3. I will refer
>
> It will certainly be different in the sense that you won't use a stacked
> domain on top of the GIC. But what I want to see is the use of a generic
> pci_msi_domain on top of an irq_domain, just like we have on v2m and v3.
> Thomas has also been vocal enough about that in the past, and x86 is
> going down that road as well.
>
>> to what you did for Tegra MSI, but I don't see your latest changes in
>> 4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?
>
> Not yet. As you can see in this branch, this relies on some other
> cleanups. But you can already convert most of your driver and put it in
> the shape that matches what we have for v2m and v3. Once the required
> cleanups are in, I'll remove the last traces of msi_controller myself if
> necessary.
>

Hi Marc, Bjorn,

I follow Marc's suggestion and convert my X-Gene 1 MSI driver to
remove msi_controller struct and use generic pci_msi_domain on top of
an irq_domain. The code requires Marc's changes in
irq/kill-msi-controller branch to be compiled and function correctly,
so I plan to post the patch on top of Marc's tree. Please let me know
if you think I should have different approach to post this patch.

Another question I have is when do you plan to roll out this hierarchy
irq domain implementation for MSI, as I see some of Marc's changes to
kill msi_controller structure and implement MSI irq domain still does
not get into Bjorn's tree.

Regards,
Duc Dang.

> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...

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

* Re: [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-07 19:56                   ` Duc Dang
@ 2015-04-08  7:44                     ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-08  7:44 UTC (permalink / raw)
  To: Duc Dang
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	Feng Kan, linux-pci, Tanmay Inamdar, linux-arm-kernel, Loc Ho

On Tue, 7 Apr 2015 20:56:48 +0100
Duc Dang <dhdang@apm.com> wrote:

Hi Duc,

> On Wed, Mar 18, 2015 at 11:52 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> > On 18/03/15 18:29, Duc Dang wrote:
> >> On Wed, Mar 18, 2015 at 11:05 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> >>> On 04/03/15 19:39, Duc Dang wrote:
> >>>> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
> >>>> 16 HW IRQ lines.
> >>>>
> >>>> Signed-off-by: Duc Dang <dhdang@apm.com>
> >>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> >>>
> >>> I just had a quick look at this, and this seems to be going in the exact
> >>> opposite direction compared to what we now have on arm64, where we move
> >>> away from using struct msi_controller for most thing, and implement PCI
> >>> MSI/MSIX in a generic way, using MSI domains.
> >>>
> >>> I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI
> >>> support. You can also have a look at what I did for the Tegra MSI
> >>> controller in this patch:
> >>>
> >>> https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081
> >>>
> >>> Eventually, the plan is to kill msi_controller entirely, so introducing
> >>> new drivers that rely on it is not something I'm eager to see.
> >>
> >> Thanks, Marc.
> >>
> >>  X-Gene 1 MSI is handled by separate MSI controller block, so its
> >> driver implementation is different from GICv2m and GICv3. I will refer
> >
> > It will certainly be different in the sense that you won't use a stacked
> > domain on top of the GIC. But what I want to see is the use of a generic
> > pci_msi_domain on top of an irq_domain, just like we have on v2m and v3.
> > Thomas has also been vocal enough about that in the past, and x86 is
> > going down that road as well.
> >
> >> to what you did for Tegra MSI, but I don't see your latest changes in
> >> 4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?
> >
> > Not yet. As you can see in this branch, this relies on some other
> > cleanups. But you can already convert most of your driver and put it in
> > the shape that matches what we have for v2m and v3. Once the required
> > cleanups are in, I'll remove the last traces of msi_controller myself if
> > necessary.
> >
> 
> Hi Marc, Bjorn,
> 
> I follow Marc's suggestion and convert my X-Gene 1 MSI driver to
> remove msi_controller struct and use generic pci_msi_domain on top of
> an irq_domain. The code requires Marc's changes in
> irq/kill-msi-controller branch to be compiled and function correctly,
> so I plan to post the patch on top of Marc's tree. Please let me know
> if you think I should have different approach to post this patch.

I don't think you should rely on this branch just yet (this is why I
suggested matching what we currently have for GICv2m and v3). Keeping
msi_controller is fine at the moment, even if we only use to store the
MSI domain. If and when this series makes it to mainline, I'll go over
the individual drivers to convert them to the new scheme, just like I
did for GIC and Tegra.

> Another question I have is when do you plan to roll out this hierarchy
> irq domain implementation for MSI, as I see some of Marc's changes to
> kill msi_controller structure and implement MSI irq domain still does
> not get into Bjorn's tree.

I'm still tinkering with it (I have some long standing comments from
Bjorn to address), and I'm working on non-PCI MSI support at the moment.

But the core MSI irq domain stuff is already in (this is what GICv2m
and GICv3 ITS are using), and this should be an easy thing to convert
your code.

Please let me know if I can be of any help.

Thanks,

	M.
-- 
Without deviation from the norm, progress is not possible.

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

* [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-08  7:44                     ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-08  7:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 7 Apr 2015 20:56:48 +0100
Duc Dang <dhdang@apm.com> wrote:

Hi Duc,

> On Wed, Mar 18, 2015 at 11:52 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> > On 18/03/15 18:29, Duc Dang wrote:
> >> On Wed, Mar 18, 2015 at 11:05 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> >>> On 04/03/15 19:39, Duc Dang wrote:
> >>>> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
> >>>> 16 HW IRQ lines.
> >>>>
> >>>> Signed-off-by: Duc Dang <dhdang@apm.com>
> >>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> >>>
> >>> I just had a quick look at this, and this seems to be going in the exact
> >>> opposite direction compared to what we now have on arm64, where we move
> >>> away from using struct msi_controller for most thing, and implement PCI
> >>> MSI/MSIX in a generic way, using MSI domains.
> >>>
> >>> I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI
> >>> support. You can also have a look at what I did for the Tegra MSI
> >>> controller in this patch:
> >>>
> >>> https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081
> >>>
> >>> Eventually, the plan is to kill msi_controller entirely, so introducing
> >>> new drivers that rely on it is not something I'm eager to see.
> >>
> >> Thanks, Marc.
> >>
> >>  X-Gene 1 MSI is handled by separate MSI controller block, so its
> >> driver implementation is different from GICv2m and GICv3. I will refer
> >
> > It will certainly be different in the sense that you won't use a stacked
> > domain on top of the GIC. But what I want to see is the use of a generic
> > pci_msi_domain on top of an irq_domain, just like we have on v2m and v3.
> > Thomas has also been vocal enough about that in the past, and x86 is
> > going down that road as well.
> >
> >> to what you did for Tegra MSI, but I don't see your latest changes in
> >> 4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?
> >
> > Not yet. As you can see in this branch, this relies on some other
> > cleanups. But you can already convert most of your driver and put it in
> > the shape that matches what we have for v2m and v3. Once the required
> > cleanups are in, I'll remove the last traces of msi_controller myself if
> > necessary.
> >
> 
> Hi Marc, Bjorn,
> 
> I follow Marc's suggestion and convert my X-Gene 1 MSI driver to
> remove msi_controller struct and use generic pci_msi_domain on top of
> an irq_domain. The code requires Marc's changes in
> irq/kill-msi-controller branch to be compiled and function correctly,
> so I plan to post the patch on top of Marc's tree. Please let me know
> if you think I should have different approach to post this patch.

I don't think you should rely on this branch just yet (this is why I
suggested matching what we currently have for GICv2m and v3). Keeping
msi_controller is fine at the moment, even if we only use to store the
MSI domain. If and when this series makes it to mainline, I'll go over
the individual drivers to convert them to the new scheme, just like I
did for GIC and Tegra.

> Another question I have is when do you plan to roll out this hierarchy
> irq domain implementation for MSI, as I see some of Marc's changes to
> kill msi_controller structure and implement MSI irq domain still does
> not get into Bjorn's tree.

I'm still tinkering with it (I have some long standing comments from
Bjorn to address), and I'm working on non-PCI MSI support at the moment.

But the core MSI irq domain stuff is already in (this is what GICv2m
and GICv3 ITS are using), and this should be an easy thing to convert
your code.

Please let me know if I can be of any help.

Thanks,

	M.
-- 
Without deviation from the norm, progress is not possible.

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

* [PATCH v3 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-04-08  7:44                     ` Marc Zyngier
@ 2015-04-09 17:05                       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 17:05 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports.

v3 changes:
	1. Implement MSI support using PCI MSI IRQ domain
	2. Only use msi_controller to store IRQ domain
v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
 
 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  63 ++++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   6 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 407 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 ++
 7 files changed, 533 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1


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

* [PATCH v3 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-04-09 17:05                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 17:05 UTC (permalink / raw)
  To: linux-arm-kernel

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports.

v3 changes:
	1. Implement MSI support using PCI MSI IRQ domain
	2. Only use msi_controller to store IRQ domain
v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
 
 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  63 ++++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   6 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 407 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 ++
 7 files changed, 533 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-09 17:05                       ` Duc Dang
@ 2015-04-09 17:05                       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 17:05 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
16 HW IRQ lines.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 435 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9..c9b61fa 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,17 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c..f39bde3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..4f0ff42
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,407 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0		0x000000
+#define MSI_INT0		0x800000
+
+struct xgene_msi_settings {
+	u32	index_per_group;
+	u32	irqs_per_index;
+	u32	nr_msi_vec;
+	u32	nr_hw_irqs;
+};
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	struct xgene_msi_settings	*settings;
+	u32				msi_addr_lo;
+	u32				msi_addr_hi;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	int				*msi_virqs;
+};
+
+struct xgene_msi_settings storm_msi_settings = {
+	.index_per_group	= 8,
+	.irqs_per_index		= 21,
+	.nr_msi_vec		= 2688,
+	.nr_hw_irqs		= 16,
+};
+
+typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
+
+static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
+{
+	xgene_msi->settings = &storm_msi_settings;
+	return 0;
+}
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+	u32 irqs_per_index = msi->settings->irqs_per_index;
+	u32 reg_set = data->hwirq / (nr_hw_irqs * irqs_per_index);
+	u32 group = data->hwirq % nr_hw_irqs;
+
+	msg->address_hi = msi->msi_addr_hi;
+	msg->address_lo = msi->msi_addr_lo +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (data->hwirq / nr_hw_irqs) % irqs_per_index;
+}
+
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+				  const struct cpumask *mask, bool force)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+	unsigned int gic_irq;
+	int ret;
+
+	gic_irq = msi->msi_virqs[irq_data->hwirq % msi->settings->nr_hw_irqs];
+	ret = irq_set_affinity(gic_irq, mask);
+	if (ret == IRQ_SET_MASK_OK)
+		ret = IRQ_SET_MASK_OK_DONE;
+
+	return ret;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+
+	struct xgene_msi *msi = domain->host_data;
+	u32 msi_irq_count = msi->settings->nr_msi_vec;
+	int msi_irq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = find_first_zero_bit(msi->bitmap, msi_irq_count);
+	if (msi_irq < msi_irq_count)
+		set_bit(msi_irq, msi->bitmap);
+	else
+		msi_irq = -ENOSPC;
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
+			    domain->host_data, handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+
+	mutex_lock(&msi->bitmap_lock);
+
+	if (!test_bit(d->hwirq, msi->bitmap))
+		pr_err("trying to free unused MSI#%lu\n", d->hwirq);
+	else
+		clear_bit(d->hwirq, msi->bitmap);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
+					    &msi_domain_ops, msi);
+
+	if (!msi->domain) {
+		pr_err("failed to create parent MSI domain\n");
+		return -ENOMEM;
+	}
+
+	msi->mchip.of_node = msi->node;
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		pr_err("failed to create MSI domain\n");
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+	u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+	int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+	if (!xgene_msi->msi_virqs)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)
+{
+	struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp, processed = 0;
+	u32 nr_hw_irqs, irqs_per_index, index_per_group;
+
+	msi_grp = irq - xgene_msi->msi_virqs[0];
+	if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+		pr_err("invalid msi received\n");
+		return IRQ_NONE;
+	}
+
+	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	irqs_per_index = xgene_msi->settings->irqs_per_index;
+	index_per_group = xgene_msi->settings->index_per_group;
+
+	grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			hw_irq = (((msir_index * irqs_per_index) + intr_index) *
+				 nr_hw_irqs) + msi_grp;
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+			processed++;
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+	u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+
+	for (i = 0; i < nr_hw_irqs; i++) {
+		virq = msi->msi_virqs[i];
+		if (virq != 0)
+			free_irq(virq, msi);
+	}
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+				 struct platform_device *pdev,
+				 int irq_index)
+{
+	int virt_msir;
+	cpumask_var_t mask;
+	int err;
+
+	virt_msir = platform_get_irq(pdev, irq_index);
+	if (virt_msir < 0) {
+		dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+			irq_index);
+		return -EINVAL;
+	}
+
+	err = request_irq(virt_msir, xgene_msi_isr, 0,
+			  xgene_msi_top_irq_chip.name, msi);
+	if (err) {
+		dev_err(&pdev->dev, "request irq failed\n");
+		return err;
+	}
+
+	if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+		cpumask_setall(mask);
+		irq_set_affinity(virt_msir, mask);
+		free_cpumask_var(mask);
+	}
+
+	msi->msi_virqs[irq_index] = virt_msir;
+
+	return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi",
+	 .data = xgene_msi_init_storm_settings},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct device_node *np;
+	const struct of_device_id *matched_np;
+	struct xgene_msi *xgene_msi;
+	xgene_msi_initcall_t init_fn;
+	u32 nr_hw_irqs, nr_msi_vecs;
+
+	np = of_find_matching_node_and_match(NULL,
+			     xgene_msi_match_table, &matched_np);
+	if (!np)
+		return -ENODEV;
+
+	xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
+	if (!xgene_msi) {
+		dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
+		return -ENOMEM;
+	}
+
+	xgene_msi->node = np;
+
+	init_fn = (xgene_msi_initcall_t) matched_np->data;
+	rc = init_fn(xgene_msi);
+	if (rc)
+		return rc;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	xgene_msi->msi_addr_hi = upper_32_bits(res->start);
+	xgene_msi->msi_addr_lo = lower_32_bits(res->start);
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
+		rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+		if (rc)
+			goto error;
+	}
+
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
+MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
+MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 22751ed..3e6faa1 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1


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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-09 17:05                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 17:05 UTC (permalink / raw)
  To: linux-arm-kernel

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
16 HW IRQ lines.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 435 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9..c9b61fa 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,17 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c..f39bde3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..4f0ff42
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,407 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0		0x000000
+#define MSI_INT0		0x800000
+
+struct xgene_msi_settings {
+	u32	index_per_group;
+	u32	irqs_per_index;
+	u32	nr_msi_vec;
+	u32	nr_hw_irqs;
+};
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	struct xgene_msi_settings	*settings;
+	u32				msi_addr_lo;
+	u32				msi_addr_hi;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	int				*msi_virqs;
+};
+
+struct xgene_msi_settings storm_msi_settings = {
+	.index_per_group	= 8,
+	.irqs_per_index		= 21,
+	.nr_msi_vec		= 2688,
+	.nr_hw_irqs		= 16,
+};
+
+typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
+
+static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
+{
+	xgene_msi->settings = &storm_msi_settings;
+	return 0;
+}
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+	u32 irqs_per_index = msi->settings->irqs_per_index;
+	u32 reg_set = data->hwirq / (nr_hw_irqs * irqs_per_index);
+	u32 group = data->hwirq % nr_hw_irqs;
+
+	msg->address_hi = msi->msi_addr_hi;
+	msg->address_lo = msi->msi_addr_lo +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (data->hwirq / nr_hw_irqs) % irqs_per_index;
+}
+
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+				  const struct cpumask *mask, bool force)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+	unsigned int gic_irq;
+	int ret;
+
+	gic_irq = msi->msi_virqs[irq_data->hwirq % msi->settings->nr_hw_irqs];
+	ret = irq_set_affinity(gic_irq, mask);
+	if (ret == IRQ_SET_MASK_OK)
+		ret = IRQ_SET_MASK_OK_DONE;
+
+	return ret;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+
+	struct xgene_msi *msi = domain->host_data;
+	u32 msi_irq_count = msi->settings->nr_msi_vec;
+	int msi_irq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = find_first_zero_bit(msi->bitmap, msi_irq_count);
+	if (msi_irq < msi_irq_count)
+		set_bit(msi_irq, msi->bitmap);
+	else
+		msi_irq = -ENOSPC;
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
+			    domain->host_data, handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+
+	mutex_lock(&msi->bitmap_lock);
+
+	if (!test_bit(d->hwirq, msi->bitmap))
+		pr_err("trying to free unused MSI#%lu\n", d->hwirq);
+	else
+		clear_bit(d->hwirq, msi->bitmap);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
+					    &msi_domain_ops, msi);
+
+	if (!msi->domain) {
+		pr_err("failed to create parent MSI domain\n");
+		return -ENOMEM;
+	}
+
+	msi->mchip.of_node = msi->node;
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		pr_err("failed to create MSI domain\n");
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+	u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+	int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+	if (!xgene_msi->msi_virqs)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)
+{
+	struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp, processed = 0;
+	u32 nr_hw_irqs, irqs_per_index, index_per_group;
+
+	msi_grp = irq - xgene_msi->msi_virqs[0];
+	if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+		pr_err("invalid msi received\n");
+		return IRQ_NONE;
+	}
+
+	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	irqs_per_index = xgene_msi->settings->irqs_per_index;
+	index_per_group = xgene_msi->settings->index_per_group;
+
+	grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			hw_irq = (((msir_index * irqs_per_index) + intr_index) *
+				 nr_hw_irqs) + msi_grp;
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+			processed++;
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+	u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+
+	for (i = 0; i < nr_hw_irqs; i++) {
+		virq = msi->msi_virqs[i];
+		if (virq != 0)
+			free_irq(virq, msi);
+	}
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+				 struct platform_device *pdev,
+				 int irq_index)
+{
+	int virt_msir;
+	cpumask_var_t mask;
+	int err;
+
+	virt_msir = platform_get_irq(pdev, irq_index);
+	if (virt_msir < 0) {
+		dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+			irq_index);
+		return -EINVAL;
+	}
+
+	err = request_irq(virt_msir, xgene_msi_isr, 0,
+			  xgene_msi_top_irq_chip.name, msi);
+	if (err) {
+		dev_err(&pdev->dev, "request irq failed\n");
+		return err;
+	}
+
+	if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+		cpumask_setall(mask);
+		irq_set_affinity(virt_msir, mask);
+		free_cpumask_var(mask);
+	}
+
+	msi->msi_virqs[irq_index] = virt_msir;
+
+	return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi",
+	 .data = xgene_msi_init_storm_settings},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct device_node *np;
+	const struct of_device_id *matched_np;
+	struct xgene_msi *xgene_msi;
+	xgene_msi_initcall_t init_fn;
+	u32 nr_hw_irqs, nr_msi_vecs;
+
+	np = of_find_matching_node_and_match(NULL,
+			     xgene_msi_match_table, &matched_np);
+	if (!np)
+		return -ENODEV;
+
+	xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
+	if (!xgene_msi) {
+		dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
+		return -ENOMEM;
+	}
+
+	xgene_msi->node = np;
+
+	init_fn = (xgene_msi_initcall_t) matched_np->data;
+	rc = init_fn(xgene_msi);
+	if (rc)
+		return rc;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	xgene_msi->msi_addr_hi = upper_32_bits(res->start);
+	xgene_msi->msi_addr_lo = lower_32_bits(res->start);
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
+		rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+		if (rc)
+			goto error;
+	}
+
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
+MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
+MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 22751ed..3e6faa1 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1

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

* [PATCH v3 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
  2015-04-09 17:05                       ` Duc Dang
@ 2015-04-09 17:05                       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 17:05 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..4b719c9 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi@79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie@1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie@1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie@1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie@1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie@1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial@1c020000 {
-- 
1.9.1


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

* [PATCH v3 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
@ 2015-04-09 17:05                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 17:05 UTC (permalink / raw)
  To: linux-arm-kernel

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..4b719c9 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
-- 
1.9.1

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

* [PATCH v3 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
  2015-04-09 17:05                       ` Duc Dang
@ 2015-04-09 17:05                       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 17:05 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..0ffdcb3
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,63 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene1-msi" to identify the core.
+- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi@79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = <  0x0 0x10 0x4
+				0x0 0x11 0x4
+				0x0 0x12 0x4
+				0x0 0x13 0x4
+				0x0 0x14 0x4
+				0x0 0x15 0x4
+				0x0 0x16 0x4
+				0x0 0x17 0x4
+				0x0 0x18 0x4
+				0x0 0x19 0x4
+				0x0 0x1a 0x4
+				0x0 0x1b 0x4
+				0x0 0x1c 0x4
+				0x0 0x1d 0x4
+				0x0 0x1e 0x4
+				0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie@1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1


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

* [PATCH v3 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
@ 2015-04-09 17:05                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 17:05 UTC (permalink / raw)
  To: linux-arm-kernel

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..0ffdcb3
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,63 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene1-msi" to identify the core.
+- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = <  0x0 0x10 0x4
+				0x0 0x11 0x4
+				0x0 0x12 0x4
+				0x0 0x13 0x4
+				0x0 0x14 0x4
+				0x0 0x15 0x4
+				0x0 0x16 0x4
+				0x0 0x17 0x4
+				0x0 0x18 0x4
+				0x0 0x19 0x4
+				0x0 0x1a 0x4
+				0x0 0x1b 0x4
+				0x0 0x1c 0x4
+				0x0 0x1d 0x4
+				0x0 0x1e 0x4
+				0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1

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

* [PATCH v3 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
  2015-04-09 17:05                       ` Duc Dang
@ 2015-04-09 17:05                       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 17:05 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddc5a8c..a1b119b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7490,6 +7490,14 @@ L:	linux-pci@vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci@vger.kernel.org
+L:	linux-arm-kernel@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia@lists.infradead.org
-- 
1.9.1


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

* [PATCH v3 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
@ 2015-04-09 17:05                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 17:05 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddc5a8c..a1b119b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7490,6 +7490,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
-- 
1.9.1

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

* Re: [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-08  7:44                     ` Marc Zyngier
@ 2015-04-09 17:20                       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 17:20 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	Feng Kan, linux-pci, Tanmay Inamdar, linux-arm-kernel, Loc Ho

Hi Marc,

On Wed, Apr 8, 2015 at 12:44 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On Tue, 7 Apr 2015 20:56:48 +0100
> Duc Dang <dhdang@apm.com> wrote:
>
> Hi Duc,
>
>> On Wed, Mar 18, 2015 at 11:52 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> > On 18/03/15 18:29, Duc Dang wrote:
>> >> On Wed, Mar 18, 2015 at 11:05 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> >>> On 04/03/15 19:39, Duc Dang wrote:
>> >>>> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
>> >>>> 16 HW IRQ lines.
>> >>>>
>> >>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> >>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> >>>
>> >>> I just had a quick look at this, and this seems to be going in the exact
>> >>> opposite direction compared to what we now have on arm64, where we move
>> >>> away from using struct msi_controller for most thing, and implement PCI
>> >>> MSI/MSIX in a generic way, using MSI domains.
>> >>>
>> >>> I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI
>> >>> support. You can also have a look at what I did for the Tegra MSI
>> >>> controller in this patch:
>> >>>
>> >>> https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081
>> >>>
>> >>> Eventually, the plan is to kill msi_controller entirely, so introducing
>> >>> new drivers that rely on it is not something I'm eager to see.
>> >>
>> >> Thanks, Marc.
>> >>
>> >>  X-Gene 1 MSI is handled by separate MSI controller block, so its
>> >> driver implementation is different from GICv2m and GICv3. I will refer
>> >
>> > It will certainly be different in the sense that you won't use a stacked
>> > domain on top of the GIC. But what I want to see is the use of a generic
>> > pci_msi_domain on top of an irq_domain, just like we have on v2m and v3.
>> > Thomas has also been vocal enough about that in the past, and x86 is
>> > going down that road as well.
>> >
>> >> to what you did for Tegra MSI, but I don't see your latest changes in
>> >> 4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?
>> >
>> > Not yet. As you can see in this branch, this relies on some other
>> > cleanups. But you can already convert most of your driver and put it in
>> > the shape that matches what we have for v2m and v3. Once the required
>> > cleanups are in, I'll remove the last traces of msi_controller myself if
>> > necessary.
>> >
>>
>> Hi Marc, Bjorn,
>>
>> I follow Marc's suggestion and convert my X-Gene 1 MSI driver to
>> remove msi_controller struct and use generic pci_msi_domain on top of
>> an irq_domain. The code requires Marc's changes in
>> irq/kill-msi-controller branch to be compiled and function correctly,
>> so I plan to post the patch on top of Marc's tree. Please let me know
>> if you think I should have different approach to post this patch.
>
> I don't think you should rely on this branch just yet (this is why I
> suggested matching what we currently have for GICv2m and v3). Keeping
> msi_controller is fine at the moment, even if we only use to store the
> MSI domain. If and when this series makes it to mainline, I'll go over
> the individual drivers to convert them to the new scheme, just like I
> did for GIC and Tegra.
>
>> Another question I have is when do you plan to roll out this hierarchy
>> irq domain implementation for MSI, as I see some of Marc's changes to
>> kill msi_controller structure and implement MSI irq domain still does
>> not get into Bjorn's tree.
>
> I'm still tinkering with it (I have some long standing comments from
> Bjorn to address), and I'm working on non-PCI MSI support at the moment.
>
> But the core MSI irq domain stuff is already in (this is what GICv2m
> and GICv3 ITS are using), and this should be an easy thing to convert
> your code.
>
> Please let me know if I can be of any help.
>

Thanks for your suggestion. I converted my code using the core MSI irq
domain (similar to GICv2m, still have msi_controller structure) and
posted
a new version (v3) of this patch. Can you please take a look when you
have some time?

Regards,
Duc Dang.

> Thanks,
>
>         M.
> --
> Without deviation from the norm, progress is not possible.

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

* [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-09 17:20                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 17:20 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Marc,

On Wed, Apr 8, 2015 at 12:44 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On Tue, 7 Apr 2015 20:56:48 +0100
> Duc Dang <dhdang@apm.com> wrote:
>
> Hi Duc,
>
>> On Wed, Mar 18, 2015 at 11:52 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> > On 18/03/15 18:29, Duc Dang wrote:
>> >> On Wed, Mar 18, 2015 at 11:05 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> >>> On 04/03/15 19:39, Duc Dang wrote:
>> >>>> X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into
>> >>>> 16 HW IRQ lines.
>> >>>>
>> >>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> >>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> >>>
>> >>> I just had a quick look at this, and this seems to be going in the exact
>> >>> opposite direction compared to what we now have on arm64, where we move
>> >>> away from using struct msi_controller for most thing, and implement PCI
>> >>> MSI/MSIX in a generic way, using MSI domains.
>> >>>
>> >>> I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI
>> >>> support. You can also have a look at what I did for the Tegra MSI
>> >>> controller in this patch:
>> >>>
>> >>> https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081
>> >>>
>> >>> Eventually, the plan is to kill msi_controller entirely, so introducing
>> >>> new drivers that rely on it is not something I'm eager to see.
>> >>
>> >> Thanks, Marc.
>> >>
>> >>  X-Gene 1 MSI is handled by separate MSI controller block, so its
>> >> driver implementation is different from GICv2m and GICv3. I will refer
>> >
>> > It will certainly be different in the sense that you won't use a stacked
>> > domain on top of the GIC. But what I want to see is the use of a generic
>> > pci_msi_domain on top of an irq_domain, just like we have on v2m and v3.
>> > Thomas has also been vocal enough about that in the past, and x86 is
>> > going down that road as well.
>> >
>> >> to what you did for Tegra MSI, but I don't see your latest changes in
>> >> 4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?
>> >
>> > Not yet. As you can see in this branch, this relies on some other
>> > cleanups. But you can already convert most of your driver and put it in
>> > the shape that matches what we have for v2m and v3. Once the required
>> > cleanups are in, I'll remove the last traces of msi_controller myself if
>> > necessary.
>> >
>>
>> Hi Marc, Bjorn,
>>
>> I follow Marc's suggestion and convert my X-Gene 1 MSI driver to
>> remove msi_controller struct and use generic pci_msi_domain on top of
>> an irq_domain. The code requires Marc's changes in
>> irq/kill-msi-controller branch to be compiled and function correctly,
>> so I plan to post the patch on top of Marc's tree. Please let me know
>> if you think I should have different approach to post this patch.
>
> I don't think you should rely on this branch just yet (this is why I
> suggested matching what we currently have for GICv2m and v3). Keeping
> msi_controller is fine at the moment, even if we only use to store the
> MSI domain. If and when this series makes it to mainline, I'll go over
> the individual drivers to convert them to the new scheme, just like I
> did for GIC and Tegra.
>
>> Another question I have is when do you plan to roll out this hierarchy
>> irq domain implementation for MSI, as I see some of Marc's changes to
>> kill msi_controller structure and implement MSI irq domain still does
>> not get into Bjorn's tree.
>
> I'm still tinkering with it (I have some long standing comments from
> Bjorn to address), and I'm working on non-PCI MSI support at the moment.
>
> But the core MSI irq domain stuff is already in (this is what GICv2m
> and GICv3 ITS are using), and this should be an easy thing to convert
> your code.
>
> Please let me know if I can be of any help.
>

Thanks for your suggestion. I converted my code using the core MSI irq
domain (similar to GICv2m, still have msi_controller structure) and
posted
a new version (v3) of this patch. Can you please take a look when you
have some time?

Regards,
Duc Dang.

> Thanks,
>
>         M.
> --
> Without deviation from the norm, progress is not possible.

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-09 17:05                       ` Duc Dang
@ 2015-04-09 20:11                         ` Bjorn Helgaas
  -1 siblings, 0 replies; 230+ messages in thread
From: Bjorn Helgaas @ 2015-04-09 20:11 UTC (permalink / raw)
  To: Duc Dang
  Cc: Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Thu, Apr 09, 2015 at 10:05:03AM -0700, Duc Dang wrote:
> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
> 16 HW IRQ lines.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ...

> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,407 @@
> ...

> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +				  unsigned int virq, unsigned int nr_irqs)
> +{
> +	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +
> +	mutex_lock(&msi->bitmap_lock);
> +
> +	if (!test_bit(d->hwirq, msi->bitmap))
> +		pr_err("trying to free unused MSI#%lu\n", d->hwirq);

I'll let Marc comment on the substance of this patch, but I'd sure like to
see more information in this and the other pr_errs below.  The text "failed
to create parent MSI domain" in the dmesg log is not enough.  Ideally it
would identify the relevant host bridge.

> +	else
> +		clear_bit(d->hwirq, msi->bitmap);
> +
> +	mutex_unlock(&msi->bitmap_lock);
> +
> +	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +	.alloc  = xgene_irq_domain_alloc,
> +	.free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +	msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
> +					    &msi_domain_ops, msi);
> +
> +	if (!msi->domain) {
> +		pr_err("failed to create parent MSI domain\n");
> +		return -ENOMEM;
> +	}
> +
> +	msi->mchip.of_node = msi->node;
> +	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +						      &xgene_msi_domain_info,
> +						      msi->domain);
> +
> +	if (!msi->mchip.domain) {
> +		pr_err("failed to create MSI domain\n");
> +		irq_domain_remove(msi->domain);
> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +	if (msi->mchip.domain)
> +		irq_domain_remove(msi->mchip.domain);
> +	if (msi->domain)
> +		irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
> +	u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
> +	int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
> +
> +	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +	if (!xgene_msi->bitmap)
> +		return -ENOMEM;
> +
> +	mutex_init(&xgene_msi->bitmap_lock);
> +
> +	xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
> +	if (!xgene_msi->msi_virqs)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +
> +static irqreturn_t xgene_msi_isr(int irq, void *data)
> +{
> +	struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
> +	unsigned int virq;
> +	int msir_index, msir_reg, msir_val, hw_irq;
> +	u32 intr_index, grp_select, msi_grp, processed = 0;
> +	u32 nr_hw_irqs, irqs_per_index, index_per_group;
> +
> +	msi_grp = irq - xgene_msi->msi_virqs[0];
> +	if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
> +		pr_err("invalid msi received\n");
> +		return IRQ_NONE;
> +	}

Bjorn

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-09 20:11                         ` Bjorn Helgaas
  0 siblings, 0 replies; 230+ messages in thread
From: Bjorn Helgaas @ 2015-04-09 20:11 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Apr 09, 2015 at 10:05:03AM -0700, Duc Dang wrote:
> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
> 16 HW IRQ lines.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ...

> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,407 @@
> ...

> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +				  unsigned int virq, unsigned int nr_irqs)
> +{
> +	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +
> +	mutex_lock(&msi->bitmap_lock);
> +
> +	if (!test_bit(d->hwirq, msi->bitmap))
> +		pr_err("trying to free unused MSI#%lu\n", d->hwirq);

I'll let Marc comment on the substance of this patch, but I'd sure like to
see more information in this and the other pr_errs below.  The text "failed
to create parent MSI domain" in the dmesg log is not enough.  Ideally it
would identify the relevant host bridge.

> +	else
> +		clear_bit(d->hwirq, msi->bitmap);
> +
> +	mutex_unlock(&msi->bitmap_lock);
> +
> +	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +	.alloc  = xgene_irq_domain_alloc,
> +	.free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +	msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
> +					    &msi_domain_ops, msi);
> +
> +	if (!msi->domain) {
> +		pr_err("failed to create parent MSI domain\n");
> +		return -ENOMEM;
> +	}
> +
> +	msi->mchip.of_node = msi->node;
> +	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +						      &xgene_msi_domain_info,
> +						      msi->domain);
> +
> +	if (!msi->mchip.domain) {
> +		pr_err("failed to create MSI domain\n");
> +		irq_domain_remove(msi->domain);
> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +	if (msi->mchip.domain)
> +		irq_domain_remove(msi->mchip.domain);
> +	if (msi->domain)
> +		irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
> +	u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
> +	int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
> +
> +	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +	if (!xgene_msi->bitmap)
> +		return -ENOMEM;
> +
> +	mutex_init(&xgene_msi->bitmap_lock);
> +
> +	xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
> +	if (!xgene_msi->msi_virqs)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +
> +static irqreturn_t xgene_msi_isr(int irq, void *data)
> +{
> +	struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
> +	unsigned int virq;
> +	int msir_index, msir_reg, msir_val, hw_irq;
> +	u32 intr_index, grp_select, msi_grp, processed = 0;
> +	u32 nr_hw_irqs, irqs_per_index, index_per_group;
> +
> +	msi_grp = irq - xgene_msi->msi_virqs[0];
> +	if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
> +		pr_err("invalid msi received\n");
> +		return IRQ_NONE;
> +	}

Bjorn

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-09 20:11                         ` Bjorn Helgaas
@ 2015-04-09 21:52                           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 21:52 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Thu, Apr 9, 2015 at 1:11 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
> On Thu, Apr 09, 2015 at 10:05:03AM -0700, Duc Dang wrote:
>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>> 16 HW IRQ lines.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ...
>
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,407 @@
>> ...
>
>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>> +                               unsigned int virq, unsigned int nr_irqs)
>> +{
>> +     struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> +     struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>> +
>> +     mutex_lock(&msi->bitmap_lock);
>> +
>> +     if (!test_bit(d->hwirq, msi->bitmap))
>> +             pr_err("trying to free unused MSI#%lu\n", d->hwirq);
>
> I'll let Marc comment on the substance of this patch, but I'd sure like to
> see more information in this and the other pr_errs below.  The text "failed
> to create parent MSI domain" in the dmesg log is not enough.  Ideally it
> would identify the relevant host bridge.
>
Hi Bjorn,

X-Gene has single MSI block that serves 5 PCIe ports. so in this
implementation, 5 X-Gene PCIe host bridges share the same MSI domain.

I can add X-Gene 1 MSI driver name and device tree node name into
these messages, but the host bridge information is not available.

As I understand, in case of error, the upper layer MSI core and driver
code will check and report the error with additional device
information (bus number, function number, etc)?

Regards,
Duc Dang.

>> +     else
>> +             clear_bit(d->hwirq, msi->bitmap);
>> +
>> +     mutex_unlock(&msi->bitmap_lock);
>> +
>> +     irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops msi_domain_ops = {
>> +     .alloc  = xgene_irq_domain_alloc,
>> +     .free   = xgene_irq_domain_free,
>> +};
>> +
>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>> +{
>> +     msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
>> +                                         &msi_domain_ops, msi);
>> +
>> +     if (!msi->domain) {
>> +             pr_err("failed to create parent MSI domain\n");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     msi->mchip.of_node = msi->node;
>> +     msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>> +                                                   &xgene_msi_domain_info,
>> +                                                   msi->domain);
>> +
>> +     if (!msi->mchip.domain) {
>> +             pr_err("failed to create MSI domain\n");
>> +             irq_domain_remove(msi->domain);
>> +             return -ENOMEM;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static void xgene_free_domains(struct xgene_msi *msi)
>> +{
>> +     if (msi->mchip.domain)
>> +             irq_domain_remove(msi->mchip.domain);
>> +     if (msi->domain)
>> +             irq_domain_remove(msi->domain);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>> +     u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
>> +     int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
>> +
>> +     xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +     if (!xgene_msi->bitmap)
>> +             return -ENOMEM;
>> +
>> +     mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +     xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
>> +     if (!xgene_msi->msi_virqs)
>> +             return -ENOMEM;
>> +
>> +     return 0;
>> +}
>> +
>> +static irqreturn_t xgene_msi_isr(int irq, void *data)
>> +{
>> +     struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
>> +     unsigned int virq;
>> +     int msir_index, msir_reg, msir_val, hw_irq;
>> +     u32 intr_index, grp_select, msi_grp, processed = 0;
>> +     u32 nr_hw_irqs, irqs_per_index, index_per_group;
>> +
>> +     msi_grp = irq - xgene_msi->msi_virqs[0];
>> +     if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
>> +             pr_err("invalid msi received\n");
>> +             return IRQ_NONE;
>> +     }
>
> Bjorn

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-09 21:52                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 21:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Apr 9, 2015 at 1:11 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
> On Thu, Apr 09, 2015 at 10:05:03AM -0700, Duc Dang wrote:
>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>> 16 HW IRQ lines.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ...
>
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,407 @@
>> ...
>
>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>> +                               unsigned int virq, unsigned int nr_irqs)
>> +{
>> +     struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> +     struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>> +
>> +     mutex_lock(&msi->bitmap_lock);
>> +
>> +     if (!test_bit(d->hwirq, msi->bitmap))
>> +             pr_err("trying to free unused MSI#%lu\n", d->hwirq);
>
> I'll let Marc comment on the substance of this patch, but I'd sure like to
> see more information in this and the other pr_errs below.  The text "failed
> to create parent MSI domain" in the dmesg log is not enough.  Ideally it
> would identify the relevant host bridge.
>
Hi Bjorn,

X-Gene has single MSI block that serves 5 PCIe ports. so in this
implementation, 5 X-Gene PCIe host bridges share the same MSI domain.

I can add X-Gene 1 MSI driver name and device tree node name into
these messages, but the host bridge information is not available.

As I understand, in case of error, the upper layer MSI core and driver
code will check and report the error with additional device
information (bus number, function number, etc)?

Regards,
Duc Dang.

>> +     else
>> +             clear_bit(d->hwirq, msi->bitmap);
>> +
>> +     mutex_unlock(&msi->bitmap_lock);
>> +
>> +     irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops msi_domain_ops = {
>> +     .alloc  = xgene_irq_domain_alloc,
>> +     .free   = xgene_irq_domain_free,
>> +};
>> +
>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>> +{
>> +     msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
>> +                                         &msi_domain_ops, msi);
>> +
>> +     if (!msi->domain) {
>> +             pr_err("failed to create parent MSI domain\n");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     msi->mchip.of_node = msi->node;
>> +     msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>> +                                                   &xgene_msi_domain_info,
>> +                                                   msi->domain);
>> +
>> +     if (!msi->mchip.domain) {
>> +             pr_err("failed to create MSI domain\n");
>> +             irq_domain_remove(msi->domain);
>> +             return -ENOMEM;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static void xgene_free_domains(struct xgene_msi *msi)
>> +{
>> +     if (msi->mchip.domain)
>> +             irq_domain_remove(msi->mchip.domain);
>> +     if (msi->domain)
>> +             irq_domain_remove(msi->domain);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>> +     u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
>> +     int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
>> +
>> +     xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +     if (!xgene_msi->bitmap)
>> +             return -ENOMEM;
>> +
>> +     mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +     xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
>> +     if (!xgene_msi->msi_virqs)
>> +             return -ENOMEM;
>> +
>> +     return 0;
>> +}
>> +
>> +static irqreturn_t xgene_msi_isr(int irq, void *data)
>> +{
>> +     struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
>> +     unsigned int virq;
>> +     int msir_index, msir_reg, msir_val, hw_irq;
>> +     u32 intr_index, grp_select, msi_grp, processed = 0;
>> +     u32 nr_hw_irqs, irqs_per_index, index_per_group;
>> +
>> +     msi_grp = irq - xgene_msi->msi_virqs[0];
>> +     if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
>> +             pr_err("invalid msi received\n");
>> +             return IRQ_NONE;
>> +     }
>
> Bjorn

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-09 21:52                           ` Duc Dang
@ 2015-04-09 22:39                             ` Bjorn Helgaas
  -1 siblings, 0 replies; 230+ messages in thread
From: Bjorn Helgaas @ 2015-04-09 22:39 UTC (permalink / raw)
  To: Duc Dang
  Cc: Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier,
	linux-pci, linux-arm, linux-kernel, Tanmay Inamdar, Loc Ho,
	Feng Kan

On Thu, Apr 9, 2015 at 4:52 PM, Duc Dang <dhdang@apm.com> wrote:
> On Thu, Apr 9, 2015 at 1:11 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
>> On Thu, Apr 09, 2015 at 10:05:03AM -0700, Duc Dang wrote:
>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>> 16 HW IRQ lines.
>>>
>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>> ...
>>
>>> --- /dev/null
>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>> @@ -0,0 +1,407 @@
>>> ...
>>
>>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>>> +                               unsigned int virq, unsigned int nr_irqs)
>>> +{
>>> +     struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>>> +     struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>>> +
>>> +     mutex_lock(&msi->bitmap_lock);
>>> +
>>> +     if (!test_bit(d->hwirq, msi->bitmap))
>>> +             pr_err("trying to free unused MSI#%lu\n", d->hwirq);
>>
>> I'll let Marc comment on the substance of this patch, but I'd sure like to
>> see more information in this and the other pr_errs below.  The text "failed
>> to create parent MSI domain" in the dmesg log is not enough.  Ideally it
>> would identify the relevant host bridge.
>>
> Hi Bjorn,
>
> X-Gene has single MSI block that serves 5 PCIe ports. so in this
> implementation, 5 X-Gene PCIe host bridges share the same MSI domain.
>
> I can add X-Gene 1 MSI driver name and device tree node name into
> these messages, but the host bridge information is not available.
>
> As I understand, in case of error, the upper layer MSI core and driver
> code will check and report the error with additional device
> information (bus number, function number, etc)?

If you think the upper layers already report the error, maybe you
could just drop the output from the X-Gene code.

I just think it's pointless to print messages that are constant
strings with no hook for the user to associate them with a device or
event.

>>> +     else
>>> +             clear_bit(d->hwirq, msi->bitmap);
>>> +
>>> +     mutex_unlock(&msi->bitmap_lock);
>>> +
>>> +     irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>>> +}
>>> +
>>> +static const struct irq_domain_ops msi_domain_ops = {
>>> +     .alloc  = xgene_irq_domain_alloc,
>>> +     .free   = xgene_irq_domain_free,
>>> +};
>>> +
>>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>>> +{
>>> +     msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
>>> +                                         &msi_domain_ops, msi);
>>> +
>>> +     if (!msi->domain) {
>>> +             pr_err("failed to create parent MSI domain\n");
>>> +             return -ENOMEM;
>>> +     }
>>> +
>>> +     msi->mchip.of_node = msi->node;
>>> +     msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>>> +                                                   &xgene_msi_domain_info,
>>> +                                                   msi->domain);
>>> +
>>> +     if (!msi->mchip.domain) {
>>> +             pr_err("failed to create MSI domain\n");
>>> +             irq_domain_remove(msi->domain);
>>> +             return -ENOMEM;
>>> +     }
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static void xgene_free_domains(struct xgene_msi *msi)
>>> +{
>>> +     if (msi->mchip.domain)
>>> +             irq_domain_remove(msi->mchip.domain);
>>> +     if (msi->domain)
>>> +             irq_domain_remove(msi->domain);
>>> +}
>>> +
>>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>>> +{
>>> +     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>>> +     u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
>>> +     int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
>>> +
>>> +     xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>>> +     if (!xgene_msi->bitmap)
>>> +             return -ENOMEM;
>>> +
>>> +     mutex_init(&xgene_msi->bitmap_lock);
>>> +
>>> +     xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
>>> +     if (!xgene_msi->msi_virqs)
>>> +             return -ENOMEM;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static irqreturn_t xgene_msi_isr(int irq, void *data)
>>> +{
>>> +     struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
>>> +     unsigned int virq;
>>> +     int msir_index, msir_reg, msir_val, hw_irq;
>>> +     u32 intr_index, grp_select, msi_grp, processed = 0;
>>> +     u32 nr_hw_irqs, irqs_per_index, index_per_group;
>>> +
>>> +     msi_grp = irq - xgene_msi->msi_virqs[0];
>>> +     if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
>>> +             pr_err("invalid msi received\n");
>>> +             return IRQ_NONE;
>>> +     }
>>
>> Bjorn
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-09 22:39                             ` Bjorn Helgaas
  0 siblings, 0 replies; 230+ messages in thread
From: Bjorn Helgaas @ 2015-04-09 22:39 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Apr 9, 2015 at 4:52 PM, Duc Dang <dhdang@apm.com> wrote:
> On Thu, Apr 9, 2015 at 1:11 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
>> On Thu, Apr 09, 2015 at 10:05:03AM -0700, Duc Dang wrote:
>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>> 16 HW IRQ lines.
>>>
>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>> ...
>>
>>> --- /dev/null
>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>> @@ -0,0 +1,407 @@
>>> ...
>>
>>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>>> +                               unsigned int virq, unsigned int nr_irqs)
>>> +{
>>> +     struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>>> +     struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>>> +
>>> +     mutex_lock(&msi->bitmap_lock);
>>> +
>>> +     if (!test_bit(d->hwirq, msi->bitmap))
>>> +             pr_err("trying to free unused MSI#%lu\n", d->hwirq);
>>
>> I'll let Marc comment on the substance of this patch, but I'd sure like to
>> see more information in this and the other pr_errs below.  The text "failed
>> to create parent MSI domain" in the dmesg log is not enough.  Ideally it
>> would identify the relevant host bridge.
>>
> Hi Bjorn,
>
> X-Gene has single MSI block that serves 5 PCIe ports. so in this
> implementation, 5 X-Gene PCIe host bridges share the same MSI domain.
>
> I can add X-Gene 1 MSI driver name and device tree node name into
> these messages, but the host bridge information is not available.
>
> As I understand, in case of error, the upper layer MSI core and driver
> code will check and report the error with additional device
> information (bus number, function number, etc)?

If you think the upper layers already report the error, maybe you
could just drop the output from the X-Gene code.

I just think it's pointless to print messages that are constant
strings with no hook for the user to associate them with a device or
event.

>>> +     else
>>> +             clear_bit(d->hwirq, msi->bitmap);
>>> +
>>> +     mutex_unlock(&msi->bitmap_lock);
>>> +
>>> +     irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>>> +}
>>> +
>>> +static const struct irq_domain_ops msi_domain_ops = {
>>> +     .alloc  = xgene_irq_domain_alloc,
>>> +     .free   = xgene_irq_domain_free,
>>> +};
>>> +
>>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>>> +{
>>> +     msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
>>> +                                         &msi_domain_ops, msi);
>>> +
>>> +     if (!msi->domain) {
>>> +             pr_err("failed to create parent MSI domain\n");
>>> +             return -ENOMEM;
>>> +     }
>>> +
>>> +     msi->mchip.of_node = msi->node;
>>> +     msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>>> +                                                   &xgene_msi_domain_info,
>>> +                                                   msi->domain);
>>> +
>>> +     if (!msi->mchip.domain) {
>>> +             pr_err("failed to create MSI domain\n");
>>> +             irq_domain_remove(msi->domain);
>>> +             return -ENOMEM;
>>> +     }
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static void xgene_free_domains(struct xgene_msi *msi)
>>> +{
>>> +     if (msi->mchip.domain)
>>> +             irq_domain_remove(msi->mchip.domain);
>>> +     if (msi->domain)
>>> +             irq_domain_remove(msi->domain);
>>> +}
>>> +
>>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>>> +{
>>> +     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>>> +     u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
>>> +     int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
>>> +
>>> +     xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>>> +     if (!xgene_msi->bitmap)
>>> +             return -ENOMEM;
>>> +
>>> +     mutex_init(&xgene_msi->bitmap_lock);
>>> +
>>> +     xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
>>> +     if (!xgene_msi->msi_virqs)
>>> +             return -ENOMEM;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static irqreturn_t xgene_msi_isr(int irq, void *data)
>>> +{
>>> +     struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
>>> +     unsigned int virq;
>>> +     int msir_index, msir_reg, msir_val, hw_irq;
>>> +     u32 intr_index, grp_select, msi_grp, processed = 0;
>>> +     u32 nr_hw_irqs, irqs_per_index, index_per_group;
>>> +
>>> +     msi_grp = irq - xgene_msi->msi_virqs[0];
>>> +     if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
>>> +             pr_err("invalid msi received\n");
>>> +             return IRQ_NONE;
>>> +     }
>>
>> Bjorn
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-09 22:39                             ` Bjorn Helgaas
@ 2015-04-09 23:26                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 23:26 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier,
	linux-pci, linux-arm, linux-kernel, Tanmay Inamdar, Loc Ho,
	Feng Kan

On Thu, Apr 9, 2015 at 3:39 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
> On Thu, Apr 9, 2015 at 4:52 PM, Duc Dang <dhdang@apm.com> wrote:
>> On Thu, Apr 9, 2015 at 1:11 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
>>> On Thu, Apr 09, 2015 at 10:05:03AM -0700, Duc Dang wrote:
>>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>>> 16 HW IRQ lines.
>>>>
>>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>>> ...
>>>
>>>> --- /dev/null
>>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>>> @@ -0,0 +1,407 @@
>>>> ...
>>>
>>>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>>>> +                               unsigned int virq, unsigned int nr_irqs)
>>>> +{
>>>> +     struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>>>> +     struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>>>> +
>>>> +     mutex_lock(&msi->bitmap_lock);
>>>> +
>>>> +     if (!test_bit(d->hwirq, msi->bitmap))
>>>> +             pr_err("trying to free unused MSI#%lu\n", d->hwirq);
>>>
>>> I'll let Marc comment on the substance of this patch, but I'd sure like to
>>> see more information in this and the other pr_errs below.  The text "failed
>>> to create parent MSI domain" in the dmesg log is not enough.  Ideally it
>>> would identify the relevant host bridge.
>>>
>> Hi Bjorn,
>>
>> X-Gene has single MSI block that serves 5 PCIe ports. so in this
>> implementation, 5 X-Gene PCIe host bridges share the same MSI domain.
>>
>> I can add X-Gene 1 MSI driver name and device tree node name into
>> these messages, but the host bridge information is not available.
>>
>> As I understand, in case of error, the upper layer MSI core and driver
>> code will check and report the error with additional device
>> information (bus number, function number, etc)?
>
> If you think the upper layers already report the error, maybe you
> could just drop the output from the X-Gene code.
>
> I just think it's pointless to print messages that are constant
> strings with no hook for the user to associate them with a device or
> event.

Hi Bjorn,

Yes, I will do so.

Regards,
Duc Dang.
>
>>>> +     else
>>>> +             clear_bit(d->hwirq, msi->bitmap);
>>>> +
>>>> +     mutex_unlock(&msi->bitmap_lock);
>>>> +
>>>> +     irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>>>> +}
>>>> +
>>>> +static const struct irq_domain_ops msi_domain_ops = {
>>>> +     .alloc  = xgene_irq_domain_alloc,
>>>> +     .free   = xgene_irq_domain_free,
>>>> +};
>>>> +
>>>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>>>> +{
>>>> +     msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
>>>> +                                         &msi_domain_ops, msi);
>>>> +
>>>> +     if (!msi->domain) {
>>>> +             pr_err("failed to create parent MSI domain\n");
>>>> +             return -ENOMEM;
>>>> +     }
>>>> +
>>>> +     msi->mchip.of_node = msi->node;
>>>> +     msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>>>> +                                                   &xgene_msi_domain_info,
>>>> +                                                   msi->domain);
>>>> +
>>>> +     if (!msi->mchip.domain) {
>>>> +             pr_err("failed to create MSI domain\n");
>>>> +             irq_domain_remove(msi->domain);
>>>> +             return -ENOMEM;
>>>> +     }
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static void xgene_free_domains(struct xgene_msi *msi)
>>>> +{
>>>> +     if (msi->mchip.domain)
>>>> +             irq_domain_remove(msi->mchip.domain);
>>>> +     if (msi->domain)
>>>> +             irq_domain_remove(msi->domain);
>>>> +}
>>>> +
>>>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>>>> +{
>>>> +     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>>>> +     u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
>>>> +     int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
>>>> +
>>>> +     xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>>>> +     if (!xgene_msi->bitmap)
>>>> +             return -ENOMEM;
>>>> +
>>>> +     mutex_init(&xgene_msi->bitmap_lock);
>>>> +
>>>> +     xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
>>>> +     if (!xgene_msi->msi_virqs)
>>>> +             return -ENOMEM;
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static irqreturn_t xgene_msi_isr(int irq, void *data)
>>>> +{
>>>> +     struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
>>>> +     unsigned int virq;
>>>> +     int msir_index, msir_reg, msir_val, hw_irq;
>>>> +     u32 intr_index, grp_select, msi_grp, processed = 0;
>>>> +     u32 nr_hw_irqs, irqs_per_index, index_per_group;
>>>> +
>>>> +     msi_grp = irq - xgene_msi->msi_virqs[0];
>>>> +     if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
>>>> +             pr_err("invalid msi received\n");
>>>> +             return IRQ_NONE;
>>>> +     }
>>>
>>> Bjorn
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> Please read the FAQ at  http://www.tux.org/lkml/

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-09 23:26                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-09 23:26 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Apr 9, 2015 at 3:39 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
> On Thu, Apr 9, 2015 at 4:52 PM, Duc Dang <dhdang@apm.com> wrote:
>> On Thu, Apr 9, 2015 at 1:11 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
>>> On Thu, Apr 09, 2015 at 10:05:03AM -0700, Duc Dang wrote:
>>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>>> 16 HW IRQ lines.
>>>>
>>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>>> ...
>>>
>>>> --- /dev/null
>>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>>> @@ -0,0 +1,407 @@
>>>> ...
>>>
>>>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>>>> +                               unsigned int virq, unsigned int nr_irqs)
>>>> +{
>>>> +     struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>>>> +     struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>>>> +
>>>> +     mutex_lock(&msi->bitmap_lock);
>>>> +
>>>> +     if (!test_bit(d->hwirq, msi->bitmap))
>>>> +             pr_err("trying to free unused MSI#%lu\n", d->hwirq);
>>>
>>> I'll let Marc comment on the substance of this patch, but I'd sure like to
>>> see more information in this and the other pr_errs below.  The text "failed
>>> to create parent MSI domain" in the dmesg log is not enough.  Ideally it
>>> would identify the relevant host bridge.
>>>
>> Hi Bjorn,
>>
>> X-Gene has single MSI block that serves 5 PCIe ports. so in this
>> implementation, 5 X-Gene PCIe host bridges share the same MSI domain.
>>
>> I can add X-Gene 1 MSI driver name and device tree node name into
>> these messages, but the host bridge information is not available.
>>
>> As I understand, in case of error, the upper layer MSI core and driver
>> code will check and report the error with additional device
>> information (bus number, function number, etc)?
>
> If you think the upper layers already report the error, maybe you
> could just drop the output from the X-Gene code.
>
> I just think it's pointless to print messages that are constant
> strings with no hook for the user to associate them with a device or
> event.

Hi Bjorn,

Yes, I will do so.

Regards,
Duc Dang.
>
>>>> +     else
>>>> +             clear_bit(d->hwirq, msi->bitmap);
>>>> +
>>>> +     mutex_unlock(&msi->bitmap_lock);
>>>> +
>>>> +     irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>>>> +}
>>>> +
>>>> +static const struct irq_domain_ops msi_domain_ops = {
>>>> +     .alloc  = xgene_irq_domain_alloc,
>>>> +     .free   = xgene_irq_domain_free,
>>>> +};
>>>> +
>>>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>>>> +{
>>>> +     msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
>>>> +                                         &msi_domain_ops, msi);
>>>> +
>>>> +     if (!msi->domain) {
>>>> +             pr_err("failed to create parent MSI domain\n");
>>>> +             return -ENOMEM;
>>>> +     }
>>>> +
>>>> +     msi->mchip.of_node = msi->node;
>>>> +     msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>>>> +                                                   &xgene_msi_domain_info,
>>>> +                                                   msi->domain);
>>>> +
>>>> +     if (!msi->mchip.domain) {
>>>> +             pr_err("failed to create MSI domain\n");
>>>> +             irq_domain_remove(msi->domain);
>>>> +             return -ENOMEM;
>>>> +     }
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static void xgene_free_domains(struct xgene_msi *msi)
>>>> +{
>>>> +     if (msi->mchip.domain)
>>>> +             irq_domain_remove(msi->mchip.domain);
>>>> +     if (msi->domain)
>>>> +             irq_domain_remove(msi->domain);
>>>> +}
>>>> +
>>>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>>>> +{
>>>> +     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>>>> +     u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
>>>> +     int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
>>>> +
>>>> +     xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>>>> +     if (!xgene_msi->bitmap)
>>>> +             return -ENOMEM;
>>>> +
>>>> +     mutex_init(&xgene_msi->bitmap_lock);
>>>> +
>>>> +     xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
>>>> +     if (!xgene_msi->msi_virqs)
>>>> +             return -ENOMEM;
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static irqreturn_t xgene_msi_isr(int irq, void *data)
>>>> +{
>>>> +     struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
>>>> +     unsigned int virq;
>>>> +     int msir_index, msir_reg, msir_val, hw_irq;
>>>> +     u32 intr_index, grp_select, msi_grp, processed = 0;
>>>> +     u32 nr_hw_irqs, irqs_per_index, index_per_group;
>>>> +
>>>> +     msi_grp = irq - xgene_msi->msi_virqs[0];
>>>> +     if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
>>>> +             pr_err("invalid msi received\n");
>>>> +             return IRQ_NONE;
>>>> +     }
>>>
>>> Bjorn
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> Please read the FAQ at  http://www.tux.org/lkml/

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-09 17:05                       ` Duc Dang
  (?)
@ 2015-04-10 17:20                         ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-10 17:20 UTC (permalink / raw)
  To: Duc Dang, Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On 09/04/15 18:05, Duc Dang wrote:
> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
> 16 HW IRQ lines.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |   6 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 435 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..c9b61fa 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,17 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..4f0ff42
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,407 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_INDEX0             0x000000
> +#define MSI_INT0               0x800000
> +
> +struct xgene_msi_settings {
> +       u32     index_per_group;
> +       u32     irqs_per_index;
> +       u32     nr_msi_vec;
> +       u32     nr_hw_irqs;
> +};
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       struct xgene_msi_settings       *settings;
> +       u32                             msi_addr_lo;
> +       u32                             msi_addr_hi;

I'd rather see the mailbox address directly, and only do the split when
assigning it to the message (you seem to play all kind of tricks on the
address anyway).

> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       int                             *msi_virqs;
> +};
> +
> +struct xgene_msi_settings storm_msi_settings = {
> +       .index_per_group        = 8,
> +       .irqs_per_index         = 21,
> +       .nr_msi_vec             = 2688,
> +       .nr_hw_irqs             = 16,
> +};

It would really help understanding how index, group and hw irq lines are
structured. nr_msi_vec is obviously the product of these three numbers,
so maybe you can loose it (we have computers, remember... ;-).

Do you expect more than this single "storm" implementation? If so, maybe
they should be described in the DT. If not, why do we need a separate
structure with an indirection if we know these are constants?

> +
> +typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
> +
> +static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
> +{
> +       xgene_msi->settings = &storm_msi_settings;
> +       return 0;
> +}
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
> +       u32 irqs_per_index = msi->settings->irqs_per_index;
> +       u32 reg_set = data->hwirq / (nr_hw_irqs * irqs_per_index);
> +       u32 group = data->hwirq % nr_hw_irqs;
> +
> +       msg->address_hi = msi->msi_addr_hi;
> +       msg->address_lo = msi->msi_addr_lo +
> +                         (((8 * group) + reg_set) << 16);
> +       msg->data = (data->hwirq / nr_hw_irqs) % irqs_per_index;

... (sound of head exploding...). I hate it when I have to reverse
engineer the hardware by looking at the code...

Please give us some clue on how interrupts are spread across the various
pages, how the various indexes are combined to give an actual address.

> +}
> +
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       unsigned int gic_irq;
> +       int ret;
> +
> +       gic_irq = msi->msi_virqs[irq_data->hwirq % msi->settings->nr_hw_irqs];
> +       ret = irq_set_affinity(gic_irq, mask);

Erm... This as the effect of moving *all* the MSIs hanging off this
interrupt to another CPU. I'm not sure that's an acceptable effect...
What if another MSI requires a different affinity?

> +       if (ret == IRQ_SET_MASK_OK)
> +               ret = IRQ_SET_MASK_OK_DONE;
> +
> +       return ret;
> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +
> +       struct xgene_msi *msi = domain->host_data;
> +       u32 msi_irq_count = msi->settings->nr_msi_vec;
> +       int msi_irq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = find_first_zero_bit(msi->bitmap, msi_irq_count);
> +       if (msi_irq < msi_irq_count)
> +               set_bit(msi_irq, msi->bitmap);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
> +                           domain->host_data, handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       if (!test_bit(d->hwirq, msi->bitmap))
> +               pr_err("trying to free unused MSI#%lu\n", d->hwirq);
> +       else
> +               clear_bit(d->hwirq, msi->bitmap);
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
> +                                           &msi_domain_ops, msi);
> +
> +       if (!msi->domain) {
> +               pr_err("failed to create parent MSI domain\n");
> +               return -ENOMEM;
> +       }
> +
> +       msi->mchip.of_node = msi->node;
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               pr_err("failed to create MSI domain\n");
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
> +       u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
> +       int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
> +       if (!xgene_msi->msi_virqs)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static irqreturn_t xgene_msi_isr(int irq, void *data)

This is not a "normal" interrupt handler. This is really a chained
interrupt controller. Please use the proper infrastructure for this
(irq_set_chained_handler, chained_irq_enter, chained_irq_exit).
Otherwise, your usage of irq_find_mapping is illegal wrt RCU.

> +{
> +       struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
> +       unsigned int virq;
> +       int msir_index, msir_reg, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp, processed = 0;
> +       u32 nr_hw_irqs, irqs_per_index, index_per_group;
> +
> +       msi_grp = irq - xgene_msi->msi_virqs[0];
> +       if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
> +               pr_err("invalid msi received\n");
> +               return IRQ_NONE;
> +       }
> +
> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
> +       irqs_per_index = xgene_msi->settings->irqs_per_index;
> +       index_per_group = xgene_msi->settings->index_per_group;
> +
> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> +               msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       hw_irq = (((msir_index * irqs_per_index) + intr_index) *
> +                                nr_hw_irqs) + msi_grp;

Same thing here. This requires some explaination on how the HW is organised.

> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> +                       if (virq != 0)
> +                               generic_handle_irq(virq);
> +                       msir_val &= ~(1 << intr_index);
> +                       processed++;
> +               }
> +               grp_select &= ~(1 << msir_index);
> +       }
> +
> +       return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
> +
> +       for (i = 0; i < nr_hw_irqs; i++) {
> +               virq = msi->msi_virqs[i];
> +               if (virq != 0)
> +                       free_irq(virq, msi);
> +       }
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);
> +
> +       return 0;
> +}
> +
> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
> +                                struct platform_device *pdev,
> +                                int irq_index)
> +{
> +       int virt_msir;
> +       cpumask_var_t mask;
> +       int err;
> +
> +       virt_msir = platform_get_irq(pdev, irq_index);
> +       if (virt_msir < 0) {
> +               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                       irq_index);
> +               return -EINVAL;
> +       }
> +
> +       err = request_irq(virt_msir, xgene_msi_isr, 0,
> +                         xgene_msi_top_irq_chip.name, msi);

This is where you should use irq_set_chained_handler.

> +       if (err) {
> +               dev_err(&pdev->dev, "request irq failed\n");
> +               return err;
> +       }
> +
> +       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +               cpumask_setall(mask);
> +               irq_set_affinity(virt_msir, mask);
> +               free_cpumask_var(mask);
> +       }
> +
> +       msi->msi_virqs[irq_index] = virt_msir;
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi",
> +        .data = xgene_msi_init_storm_settings},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct device_node *np;
> +       const struct of_device_id *matched_np;
> +       struct xgene_msi *xgene_msi;
> +       xgene_msi_initcall_t init_fn;
> +       u32 nr_hw_irqs, nr_msi_vecs;
> +
> +       np = of_find_matching_node_and_match(NULL,
> +                            xgene_msi_match_table, &matched_np);
> +       if (!np)
> +               return -ENODEV;
> +
> +       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
> +       if (!xgene_msi) {
> +               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
> +               return -ENOMEM;
> +       }
> +
> +       xgene_msi->node = np;
> +
> +       init_fn = (xgene_msi_initcall_t) matched_np->data;
> +       rc = init_fn(xgene_msi);
> +       if (rc)
> +               return rc;
> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +
> +       xgene_msi->msi_addr_hi = upper_32_bits(res->start);
> +       xgene_msi->msi_addr_lo = lower_32_bits(res->start);
> +
> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
> +       for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
> +               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
> +               if (rc)
> +                       goto error;
> +       }
> +
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +error:
> +       xgene_msi_remove(pdev);
> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> +MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
> +MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-10 17:20                         ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-10 17:20 UTC (permalink / raw)
  To: Duc Dang, Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On 09/04/15 18:05, Duc Dang wrote:
> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
> 16 HW IRQ lines.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |   6 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 435 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..c9b61fa 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,17 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..4f0ff42
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,407 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_INDEX0             0x000000
> +#define MSI_INT0               0x800000
> +
> +struct xgene_msi_settings {
> +       u32     index_per_group;
> +       u32     irqs_per_index;
> +       u32     nr_msi_vec;
> +       u32     nr_hw_irqs;
> +};
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       struct xgene_msi_settings       *settings;
> +       u32                             msi_addr_lo;
> +       u32                             msi_addr_hi;

I'd rather see the mailbox address directly, and only do the split when
assigning it to the message (you seem to play all kind of tricks on the
address anyway).

> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       int                             *msi_virqs;
> +};
> +
> +struct xgene_msi_settings storm_msi_settings = {
> +       .index_per_group        = 8,
> +       .irqs_per_index         = 21,
> +       .nr_msi_vec             = 2688,
> +       .nr_hw_irqs             = 16,
> +};

It would really help understanding how index, group and hw irq lines are
structured. nr_msi_vec is obviously the product of these three numbers,
so maybe you can loose it (we have computers, remember... ;-).

Do you expect more than this single "storm" implementation? If so, maybe
they should be described in the DT. If not, why do we need a separate
structure with an indirection if we know these are constants?

> +
> +typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
> +
> +static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
> +{
> +       xgene_msi->settings = &storm_msi_settings;
> +       return 0;
> +}
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
> +       u32 irqs_per_index = msi->settings->irqs_per_index;
> +       u32 reg_set = data->hwirq / (nr_hw_irqs * irqs_per_index);
> +       u32 group = data->hwirq % nr_hw_irqs;
> +
> +       msg->address_hi = msi->msi_addr_hi;
> +       msg->address_lo = msi->msi_addr_lo +
> +                         (((8 * group) + reg_set) << 16);
> +       msg->data = (data->hwirq / nr_hw_irqs) % irqs_per_index;

... (sound of head exploding...). I hate it when I have to reverse
engineer the hardware by looking at the code...

Please give us some clue on how interrupts are spread across the various
pages, how the various indexes are combined to give an actual address.

> +}
> +
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       unsigned int gic_irq;
> +       int ret;
> +
> +       gic_irq = msi->msi_virqs[irq_data->hwirq % msi->settings->nr_hw_irqs];
> +       ret = irq_set_affinity(gic_irq, mask);

Erm... This as the effect of moving *all* the MSIs hanging off this
interrupt to another CPU. I'm not sure that's an acceptable effect...
What if another MSI requires a different affinity?

> +       if (ret == IRQ_SET_MASK_OK)
> +               ret = IRQ_SET_MASK_OK_DONE;
> +
> +       return ret;
> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +
> +       struct xgene_msi *msi = domain->host_data;
> +       u32 msi_irq_count = msi->settings->nr_msi_vec;
> +       int msi_irq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = find_first_zero_bit(msi->bitmap, msi_irq_count);
> +       if (msi_irq < msi_irq_count)
> +               set_bit(msi_irq, msi->bitmap);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
> +                           domain->host_data, handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       if (!test_bit(d->hwirq, msi->bitmap))
> +               pr_err("trying to free unused MSI#%lu\n", d->hwirq);
> +       else
> +               clear_bit(d->hwirq, msi->bitmap);
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
> +                                           &msi_domain_ops, msi);
> +
> +       if (!msi->domain) {
> +               pr_err("failed to create parent MSI domain\n");
> +               return -ENOMEM;
> +       }
> +
> +       msi->mchip.of_node = msi->node;
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               pr_err("failed to create MSI domain\n");
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
> +       u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
> +       int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
> +       if (!xgene_msi->msi_virqs)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static irqreturn_t xgene_msi_isr(int irq, void *data)

This is not a "normal" interrupt handler. This is really a chained
interrupt controller. Please use the proper infrastructure for this
(irq_set_chained_handler, chained_irq_enter, chained_irq_exit).
Otherwise, your usage of irq_find_mapping is illegal wrt RCU.

> +{
> +       struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
> +       unsigned int virq;
> +       int msir_index, msir_reg, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp, processed = 0;
> +       u32 nr_hw_irqs, irqs_per_index, index_per_group;
> +
> +       msi_grp = irq - xgene_msi->msi_virqs[0];
> +       if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
> +               pr_err("invalid msi received\n");
> +               return IRQ_NONE;
> +       }
> +
> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
> +       irqs_per_index = xgene_msi->settings->irqs_per_index;
> +       index_per_group = xgene_msi->settings->index_per_group;
> +
> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> +               msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       hw_irq = (((msir_index * irqs_per_index) + intr_index) *
> +                                nr_hw_irqs) + msi_grp;

Same thing here. This requires some explaination on how the HW is organised.

> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> +                       if (virq != 0)
> +                               generic_handle_irq(virq);
> +                       msir_val &= ~(1 << intr_index);
> +                       processed++;
> +               }
> +               grp_select &= ~(1 << msir_index);
> +       }
> +
> +       return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
> +
> +       for (i = 0; i < nr_hw_irqs; i++) {
> +               virq = msi->msi_virqs[i];
> +               if (virq != 0)
> +                       free_irq(virq, msi);
> +       }
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);
> +
> +       return 0;
> +}
> +
> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
> +                                struct platform_device *pdev,
> +                                int irq_index)
> +{
> +       int virt_msir;
> +       cpumask_var_t mask;
> +       int err;
> +
> +       virt_msir = platform_get_irq(pdev, irq_index);
> +       if (virt_msir < 0) {
> +               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                       irq_index);
> +               return -EINVAL;
> +       }
> +
> +       err = request_irq(virt_msir, xgene_msi_isr, 0,
> +                         xgene_msi_top_irq_chip.name, msi);

This is where you should use irq_set_chained_handler.

> +       if (err) {
> +               dev_err(&pdev->dev, "request irq failed\n");
> +               return err;
> +       }
> +
> +       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +               cpumask_setall(mask);
> +               irq_set_affinity(virt_msir, mask);
> +               free_cpumask_var(mask);
> +       }
> +
> +       msi->msi_virqs[irq_index] = virt_msir;
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi",
> +        .data = xgene_msi_init_storm_settings},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct device_node *np;
> +       const struct of_device_id *matched_np;
> +       struct xgene_msi *xgene_msi;
> +       xgene_msi_initcall_t init_fn;
> +       u32 nr_hw_irqs, nr_msi_vecs;
> +
> +       np = of_find_matching_node_and_match(NULL,
> +                            xgene_msi_match_table, &matched_np);
> +       if (!np)
> +               return -ENODEV;
> +
> +       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
> +       if (!xgene_msi) {
> +               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
> +               return -ENOMEM;
> +       }
> +
> +       xgene_msi->node = np;
> +
> +       init_fn = (xgene_msi_initcall_t) matched_np->data;
> +       rc = init_fn(xgene_msi);
> +       if (rc)
> +               return rc;
> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +
> +       xgene_msi->msi_addr_hi = upper_32_bits(res->start);
> +       xgene_msi->msi_addr_lo = lower_32_bits(res->start);
> +
> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
> +       for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
> +               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
> +               if (rc)
> +                       goto error;
> +       }
> +
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +error:
> +       xgene_msi_remove(pdev);
> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> +MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
> +MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-10 17:20                         ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-10 17:20 UTC (permalink / raw)
  To: linux-arm-kernel

On 09/04/15 18:05, Duc Dang wrote:
> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
> 16 HW IRQ lines.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |   6 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 435 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..c9b61fa 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,17 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..4f0ff42
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,407 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_INDEX0             0x000000
> +#define MSI_INT0               0x800000
> +
> +struct xgene_msi_settings {
> +       u32     index_per_group;
> +       u32     irqs_per_index;
> +       u32     nr_msi_vec;
> +       u32     nr_hw_irqs;
> +};
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       struct xgene_msi_settings       *settings;
> +       u32                             msi_addr_lo;
> +       u32                             msi_addr_hi;

I'd rather see the mailbox address directly, and only do the split when
assigning it to the message (you seem to play all kind of tricks on the
address anyway).

> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       int                             *msi_virqs;
> +};
> +
> +struct xgene_msi_settings storm_msi_settings = {
> +       .index_per_group        = 8,
> +       .irqs_per_index         = 21,
> +       .nr_msi_vec             = 2688,
> +       .nr_hw_irqs             = 16,
> +};

It would really help understanding how index, group and hw irq lines are
structured. nr_msi_vec is obviously the product of these three numbers,
so maybe you can loose it (we have computers, remember... ;-).

Do you expect more than this single "storm" implementation? If so, maybe
they should be described in the DT. If not, why do we need a separate
structure with an indirection if we know these are constants?

> +
> +typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
> +
> +static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
> +{
> +       xgene_msi->settings = &storm_msi_settings;
> +       return 0;
> +}
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
> +       u32 irqs_per_index = msi->settings->irqs_per_index;
> +       u32 reg_set = data->hwirq / (nr_hw_irqs * irqs_per_index);
> +       u32 group = data->hwirq % nr_hw_irqs;
> +
> +       msg->address_hi = msi->msi_addr_hi;
> +       msg->address_lo = msi->msi_addr_lo +
> +                         (((8 * group) + reg_set) << 16);
> +       msg->data = (data->hwirq / nr_hw_irqs) % irqs_per_index;

... (sound of head exploding...). I hate it when I have to reverse
engineer the hardware by looking at the code...

Please give us some clue on how interrupts are spread across the various
pages, how the various indexes are combined to give an actual address.

> +}
> +
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       unsigned int gic_irq;
> +       int ret;
> +
> +       gic_irq = msi->msi_virqs[irq_data->hwirq % msi->settings->nr_hw_irqs];
> +       ret = irq_set_affinity(gic_irq, mask);

Erm... This as the effect of moving *all* the MSIs hanging off this
interrupt to another CPU. I'm not sure that's an acceptable effect...
What if another MSI requires a different affinity?

> +       if (ret == IRQ_SET_MASK_OK)
> +               ret = IRQ_SET_MASK_OK_DONE;
> +
> +       return ret;
> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +
> +       struct xgene_msi *msi = domain->host_data;
> +       u32 msi_irq_count = msi->settings->nr_msi_vec;
> +       int msi_irq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = find_first_zero_bit(msi->bitmap, msi_irq_count);
> +       if (msi_irq < msi_irq_count)
> +               set_bit(msi_irq, msi->bitmap);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
> +                           domain->host_data, handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       if (!test_bit(d->hwirq, msi->bitmap))
> +               pr_err("trying to free unused MSI#%lu\n", d->hwirq);
> +       else
> +               clear_bit(d->hwirq, msi->bitmap);
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
> +                                           &msi_domain_ops, msi);
> +
> +       if (!msi->domain) {
> +               pr_err("failed to create parent MSI domain\n");
> +               return -ENOMEM;
> +       }
> +
> +       msi->mchip.of_node = msi->node;
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               pr_err("failed to create MSI domain\n");
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
> +       u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
> +       int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
> +       if (!xgene_msi->msi_virqs)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static irqreturn_t xgene_msi_isr(int irq, void *data)

This is not a "normal" interrupt handler. This is really a chained
interrupt controller. Please use the proper infrastructure for this
(irq_set_chained_handler, chained_irq_enter, chained_irq_exit).
Otherwise, your usage of irq_find_mapping is illegal wrt RCU.

> +{
> +       struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
> +       unsigned int virq;
> +       int msir_index, msir_reg, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp, processed = 0;
> +       u32 nr_hw_irqs, irqs_per_index, index_per_group;
> +
> +       msi_grp = irq - xgene_msi->msi_virqs[0];
> +       if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
> +               pr_err("invalid msi received\n");
> +               return IRQ_NONE;
> +       }
> +
> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
> +       irqs_per_index = xgene_msi->settings->irqs_per_index;
> +       index_per_group = xgene_msi->settings->index_per_group;
> +
> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> +               msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       hw_irq = (((msir_index * irqs_per_index) + intr_index) *
> +                                nr_hw_irqs) + msi_grp;

Same thing here. This requires some explaination on how the HW is organised.

> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> +                       if (virq != 0)
> +                               generic_handle_irq(virq);
> +                       msir_val &= ~(1 << intr_index);
> +                       processed++;
> +               }
> +               grp_select &= ~(1 << msir_index);
> +       }
> +
> +       return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
> +
> +       for (i = 0; i < nr_hw_irqs; i++) {
> +               virq = msi->msi_virqs[i];
> +               if (virq != 0)
> +                       free_irq(virq, msi);
> +       }
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);
> +
> +       return 0;
> +}
> +
> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
> +                                struct platform_device *pdev,
> +                                int irq_index)
> +{
> +       int virt_msir;
> +       cpumask_var_t mask;
> +       int err;
> +
> +       virt_msir = platform_get_irq(pdev, irq_index);
> +       if (virt_msir < 0) {
> +               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                       irq_index);
> +               return -EINVAL;
> +       }
> +
> +       err = request_irq(virt_msir, xgene_msi_isr, 0,
> +                         xgene_msi_top_irq_chip.name, msi);

This is where you should use irq_set_chained_handler.

> +       if (err) {
> +               dev_err(&pdev->dev, "request irq failed\n");
> +               return err;
> +       }
> +
> +       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +               cpumask_setall(mask);
> +               irq_set_affinity(virt_msir, mask);
> +               free_cpumask_var(mask);
> +       }
> +
> +       msi->msi_virqs[irq_index] = virt_msir;
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi",
> +        .data = xgene_msi_init_storm_settings},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct device_node *np;
> +       const struct of_device_id *matched_np;
> +       struct xgene_msi *xgene_msi;
> +       xgene_msi_initcall_t init_fn;
> +       u32 nr_hw_irqs, nr_msi_vecs;
> +
> +       np = of_find_matching_node_and_match(NULL,
> +                            xgene_msi_match_table, &matched_np);
> +       if (!np)
> +               return -ENODEV;
> +
> +       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
> +       if (!xgene_msi) {
> +               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
> +               return -ENOMEM;
> +       }
> +
> +       xgene_msi->node = np;
> +
> +       init_fn = (xgene_msi_initcall_t) matched_np->data;
> +       rc = init_fn(xgene_msi);
> +       if (rc)
> +               return rc;
> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +
> +       xgene_msi->msi_addr_hi = upper_32_bits(res->start);
> +       xgene_msi->msi_addr_lo = lower_32_bits(res->start);
> +
> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
> +       for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
> +               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
> +               if (rc)
> +                       goto error;
> +       }
> +
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +error:
> +       xgene_msi_remove(pdev);
> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> +MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
> +MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-09 17:05                       ` Duc Dang
@ 2015-04-10 18:13                         ` Paul Bolle
  -1 siblings, 0 replies; 230+ messages in thread
From: Paul Bolle @ 2015-04-10 18:13 UTC (permalink / raw)
  To: Duc Dang
  Cc: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau,
	Marc Zyngier, linux-pci, linux-arm-kernel, linux-kernel,
	Tanmay Inamdar, Loc Ho, Feng Kan

Just a nit about a license mismatch.

On Thu, 2015-04-09 at 10:05 -0700, Duc Dang wrote:
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
 
> +config PCI_XGENE_MSI
> +	bool "X-Gene v1 PCIe MSI feature"
> +	depends on PCI_XGENE && PCI_MSI

> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile

> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o

> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c

> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.

This states the license of this file is GPL v2 or later.

> +MODULE_LICENSE("GPL v2");

And, according to include/linux/module.h, this states the license of
this driver is GPL v2. So either the comment at the top of this file or
the license ident used in this macro needs to change.

(Now, as far as I can tell, this driver is built-in only. So I think
this macro - and all other module specific code in this file - is
actually unneeded. But I already know Bjorn's position here, so I won't
bother you about that.)

Thanks,


Paul Bolle


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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-10 18:13                         ` Paul Bolle
  0 siblings, 0 replies; 230+ messages in thread
From: Paul Bolle @ 2015-04-10 18:13 UTC (permalink / raw)
  To: linux-arm-kernel

Just a nit about a license mismatch.

On Thu, 2015-04-09 at 10:05 -0700, Duc Dang wrote:
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
 
> +config PCI_XGENE_MSI
> +	bool "X-Gene v1 PCIe MSI feature"
> +	depends on PCI_XGENE && PCI_MSI

> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile

> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o

> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c

> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.

This states the license of this file is GPL v2 or later.

> +MODULE_LICENSE("GPL v2");

And, according to include/linux/module.h, this states the license of
this driver is GPL v2. So either the comment at the top of this file or
the license ident used in this macro needs to change.

(Now, as far as I can tell, this driver is built-in only. So I think
this macro - and all other module specific code in this file - is
actually unneeded. But I already know Bjorn's position here, so I won't
bother you about that.)

Thanks,


Paul Bolle

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-10 17:20                         ` Marc Zyngier
  (?)
@ 2015-04-10 23:42                           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-10 23:42 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 09/04/15 18:05, Duc Dang wrote:
>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>> 16 HW IRQ lines.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  drivers/pci/host/Kconfig         |   6 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 435 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 7b892a9..c9b61fa 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>> +       select PCI_XGENE_MSI if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index e61d91c..f39bde3 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..4f0ff42
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,407 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_INDEX0             0x000000
>> +#define MSI_INT0               0x800000
>> +
>> +struct xgene_msi_settings {
>> +       u32     index_per_group;
>> +       u32     irqs_per_index;
>> +       u32     nr_msi_vec;
>> +       u32     nr_hw_irqs;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       struct xgene_msi_settings       *settings;
>> +       u32                             msi_addr_lo;
>> +       u32                             msi_addr_hi;
>
> I'd rather see the mailbox address directly, and only do the split when
> assigning it to the message (you seem to play all kind of tricks on the
> address anyway).

msi_addr_lo and msi_addr_hi store the physical base address of MSI
controller registers. I will add comment to clarify this.
>
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       int                             *msi_virqs;
>> +};
>> +
>> +struct xgene_msi_settings storm_msi_settings = {
>> +       .index_per_group        = 8,
>> +       .irqs_per_index         = 21,
>> +       .nr_msi_vec             = 2688,
>> +       .nr_hw_irqs             = 16,
>> +};
>
> It would really help understanding how index, group and hw irq lines are
> structured. nr_msi_vec is obviously the product of these three numbers,
> so maybe you can loose it (we have computers, remember... ;-).
>
> Do you expect more than this single "storm" implementation? If so, maybe
> they should be described in the DT. If not, why do we need a separate
> structure with an indirection if we know these are constants?
>
Yes, I will add description about index, group, and hw irq lines
structure and also replace the fields in this storm_msi_settings
structure with constant dedinitions.

>> +
>> +typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
>> +
>> +static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
>> +{
>> +       xgene_msi->settings = &storm_msi_settings;
>> +       return 0;
>> +}
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
>> +       u32 irqs_per_index = msi->settings->irqs_per_index;
>> +       u32 reg_set = data->hwirq / (nr_hw_irqs * irqs_per_index);
>> +       u32 group = data->hwirq % nr_hw_irqs;
>> +
>> +       msg->address_hi = msi->msi_addr_hi;
>> +       msg->address_lo = msi->msi_addr_lo +
>> +                         (((8 * group) + reg_set) << 16);
>> +       msg->data = (data->hwirq / nr_hw_irqs) % irqs_per_index;
>
> ... (sound of head exploding...). I hate it when I have to reverse
> engineer the hardware by looking at the code...
>
> Please give us some clue on how interrupts are spread across the various
> pages, how the various indexes are combined to give an actual address.

I will add description about this.
>
>> +}
>> +
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
>> +       unsigned int gic_irq;
>> +       int ret;
>> +
>> +       gic_irq = msi->msi_virqs[irq_data->hwirq % msi->settings->nr_hw_irqs];
>> +       ret = irq_set_affinity(gic_irq, mask);
>
> Erm... This as the effect of moving *all* the MSIs hanging off this
> interrupt to another CPU. I'm not sure that's an acceptable effect...
> What if another MSI requires a different affinity?

We have 16 'real' hardware IRQs. Each of these has multiple MSIs attached to it.
So this will move all MSIs handing off this interrupt to another CPU;
and we don't support different affinity settings for different MSIs
that are attached to the same hardware IRQ.

>
>> +       if (ret == IRQ_SET_MASK_OK)
>> +               ret = IRQ_SET_MASK_OK_DONE;
>> +
>> +       return ret;
>> +}
>> +
>> +static struct irq_chip xgene_msi_bottom_irq_chip = {
>> +       .name                   = "MSI",
>> +       .irq_set_affinity       = xgene_msi_set_affinity,
>> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
>> +};
>> +
>> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
>> +                                 unsigned int nr_irqs, void *args)
>> +{
>> +
>> +       struct xgene_msi *msi = domain->host_data;
>> +       u32 msi_irq_count = msi->settings->nr_msi_vec;
>> +       int msi_irq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       msi_irq = find_first_zero_bit(msi->bitmap, msi_irq_count);
>> +       if (msi_irq < msi_irq_count)
>> +               set_bit(msi_irq, msi->bitmap);
>> +       else
>> +               msi_irq = -ENOSPC;
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       if (msi_irq < 0)
>> +               return msi_irq;
>> +
>> +       irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
>> +                           domain->host_data, handle_simple_irq, NULL, NULL);
>> +       set_irq_flags(virq, IRQF_VALID);
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>> +                                 unsigned int virq, unsigned int nr_irqs)
>> +{
>> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       if (!test_bit(d->hwirq, msi->bitmap))
>> +               pr_err("trying to free unused MSI#%lu\n", d->hwirq);
>> +       else
>> +               clear_bit(d->hwirq, msi->bitmap);
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops msi_domain_ops = {
>> +       .alloc  = xgene_irq_domain_alloc,
>> +       .free   = xgene_irq_domain_free,
>> +};
>> +
>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>> +{
>> +       msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
>> +                                           &msi_domain_ops, msi);
>> +
>> +       if (!msi->domain) {
>> +               pr_err("failed to create parent MSI domain\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       msi->mchip.of_node = msi->node;
>> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>> +                                                     &xgene_msi_domain_info,
>> +                                                     msi->domain);
>> +
>> +       if (!msi->mchip.domain) {
>> +               pr_err("failed to create MSI domain\n");
>> +               irq_domain_remove(msi->domain);
>> +               return -ENOMEM;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_free_domains(struct xgene_msi *msi)
>> +{
>> +       if (msi->mchip.domain)
>> +               irq_domain_remove(msi->mchip.domain);
>> +       if (msi->domain)
>> +               irq_domain_remove(msi->domain);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +       u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>> +       u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
>> +       int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
>> +
>> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +       if (!xgene_msi->bitmap)
>> +               return -ENOMEM;
>> +
>> +       mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +       xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
>> +       if (!xgene_msi->msi_virqs)
>> +               return -ENOMEM;
>> +
>> +       return 0;
>> +}
>> +
>> +static irqreturn_t xgene_msi_isr(int irq, void *data)
>
> This is not a "normal" interrupt handler. This is really a chained
> interrupt controller. Please use the proper infrastructure for this
> (irq_set_chained_handler, chained_irq_enter, chained_irq_exit).
> Otherwise, your usage of irq_find_mapping is illegal wrt RCU.
>
Yes, I will change this function to protect it with chained_irq_enter,
chained_irq_exit

>> +{
>> +       struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
>> +       unsigned int virq;
>> +       int msir_index, msir_reg, msir_val, hw_irq;
>> +       u32 intr_index, grp_select, msi_grp, processed = 0;
>> +       u32 nr_hw_irqs, irqs_per_index, index_per_group;
>> +
>> +       msi_grp = irq - xgene_msi->msi_virqs[0];
>> +       if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
>> +               pr_err("invalid msi received\n");
>> +               return IRQ_NONE;
>> +       }
>> +
>> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +       irqs_per_index = xgene_msi->settings->irqs_per_index;
>> +       index_per_group = xgene_msi->settings->index_per_group;
>> +
>> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +       while (grp_select) {
>> +               msir_index = ffs(grp_select) - 1;
>> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
>> +               msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
>> +               while (msir_val) {
>> +                       intr_index = ffs(msir_val) - 1;
>> +                       hw_irq = (((msir_index * irqs_per_index) + intr_index) *
>> +                                nr_hw_irqs) + msi_grp;
>
> Same thing here. This requires some explaination on how the HW is organised.

I will have description for this.

>
>> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> +                       if (virq != 0)
>> +                               generic_handle_irq(virq);
>> +                       msir_val &= ~(1 << intr_index);
>> +                       processed++;
>> +               }
>> +               grp_select &= ~(1 << msir_index);
>> +       }
>> +
>> +       return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
>> +}
>> +
>> +static int xgene_msi_remove(struct platform_device *pdev)
>> +{
>> +       int virq, i;
>> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
>> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
>> +
>> +       for (i = 0; i < nr_hw_irqs; i++) {
>> +               virq = msi->msi_virqs[i];
>> +               if (virq != 0)
>> +                       free_irq(virq, msi);
>> +       }
>> +
>> +       kfree(msi->bitmap);
>> +       msi->bitmap = NULL;
>> +
>> +       xgene_free_domains(msi);
>> +
>> +       return 0;
>> +}
>> +
>> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
>> +                                struct platform_device *pdev,
>> +                                int irq_index)
>> +{
>> +       int virt_msir;
>> +       cpumask_var_t mask;
>> +       int err;
>> +
>> +       virt_msir = platform_get_irq(pdev, irq_index);
>> +       if (virt_msir < 0) {
>> +               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
>> +                       irq_index);
>> +               return -EINVAL;
>> +       }
>> +
>> +       err = request_irq(virt_msir, xgene_msi_isr, 0,
>> +                         xgene_msi_top_irq_chip.name, msi);
>
> This is where you should use irq_set_chained_handler.

I will replace request_irq with irq_set_chained_handler and irq_set_handler_data
>
>> +       if (err) {
>> +               dev_err(&pdev->dev, "request irq failed\n");
>> +               return err;
>> +       }
>> +
>> +       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
>> +               cpumask_setall(mask);
>> +               irq_set_affinity(virt_msir, mask);
>> +               free_cpumask_var(mask);
>> +       }
>> +
>> +       msi->msi_virqs[irq_index] = virt_msir;
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id xgene_msi_match_table[] = {
>> +       {.compatible = "apm,xgene1-msi",
>> +        .data = xgene_msi_init_storm_settings},
>> +       {},
>> +};
>> +
>> +static int xgene_msi_probe(struct platform_device *pdev)
>> +{
>> +       struct resource *res;
>> +       int rc, irq_index;
>> +       struct device_node *np;
>> +       const struct of_device_id *matched_np;
>> +       struct xgene_msi *xgene_msi;
>> +       xgene_msi_initcall_t init_fn;
>> +       u32 nr_hw_irqs, nr_msi_vecs;
>> +
>> +       np = of_find_matching_node_and_match(NULL,
>> +                            xgene_msi_match_table, &matched_np);
>> +       if (!np)
>> +               return -ENODEV;
>> +
>> +       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
>> +       if (!xgene_msi) {
>> +               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       xgene_msi->node = np;
>> +
>> +       init_fn = (xgene_msi_initcall_t) matched_np->data;
>> +       rc = init_fn(xgene_msi);
>> +       if (rc)
>> +               return rc;
>> +
>> +       platform_set_drvdata(pdev, xgene_msi);
>> +
>> +       nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(xgene_msi->msi_regs)) {
>> +               dev_err(&pdev->dev, "no reg space\n");
>> +               rc = -EINVAL;
>> +               goto error;
>> +       }
>> +
>> +       xgene_msi->msi_addr_hi = upper_32_bits(res->start);
>> +       xgene_msi->msi_addr_lo = lower_32_bits(res->start);
>> +
>> +       rc = xgene_msi_init_allocator(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
>> +               goto error;
>> +       }
>> +
>> +       rc = xgene_allocate_domains(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
>> +               goto error;
>> +       }
>> +
>> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +       for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
>> +               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
>> +               if (rc)
>> +                       goto error;
>> +       }
>> +
>> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
>> +               goto error;
>> +       }
>> +
>> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
>> +
>> +       return 0;
>> +error:
>> +       xgene_msi_remove(pdev);
>> +       return rc;
>> +}
>> +
>> +static struct platform_driver xgene_msi_driver = {
>> +       .driver = {
>> +               .name = "xgene-msi",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = xgene_msi_match_table,
>> +       },
>> +       .probe = xgene_msi_probe,
>> +       .remove = xgene_msi_remove,
>> +};
>> +
>> +static int __init xgene_pcie_msi_init(void)
>> +{
>> +       return platform_driver_register(&xgene_msi_driver);
>> +}
>> +subsys_initcall(xgene_pcie_msi_init);
>> +
>> +MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
>> +MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
>> index 22751ed..3e6faa1 100644
>> --- a/drivers/pci/host/pci-xgene.c
>> +++ b/drivers/pci/host/pci-xgene.c
>> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>>         return 0;
>>  }
>>
>> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
>> +{
>> +       struct device_node *msi_node;
>> +
>> +       msi_node = of_parse_phandle(bus->dev.of_node,
>> +                                       "msi-parent", 0);
>> +       if (!msi_node)
>> +               return -ENODEV;
>> +
>> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
>> +       if (bus->msi)
>> +               bus->msi->dev = &bus->dev;
>> +       else
>> +               return -ENODEV;
>> +       return 0;
>> +}
>> +
>>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>  {
>>         struct device_node *dn = pdev->dev.of_node;
>> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>         if (!bus)
>>                 return -ENOMEM;
>>
>> +       if (IS_ENABLED(CONFIG_PCI_MSI))
>> +               if (xgene_pcie_msi_enable(bus))
>> +                       dev_info(port->dev, "failed to enable MSI\n");
>> +
>>         pci_scan_child_bus(bus);
>>         pci_assign_unassigned_bus_resources(bus);
>>         pci_bus_add_devices(bus);
>> --
>> 1.9.1
>>
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...
Regards,
Duc Dang.

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-10 23:42                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-10 23:42 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 09/04/15 18:05, Duc Dang wrote:
>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>> 16 HW IRQ lines.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  drivers/pci/host/Kconfig         |   6 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 435 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 7b892a9..c9b61fa 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>> +       select PCI_XGENE_MSI if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index e61d91c..f39bde3 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..4f0ff42
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,407 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_INDEX0             0x000000
>> +#define MSI_INT0               0x800000
>> +
>> +struct xgene_msi_settings {
>> +       u32     index_per_group;
>> +       u32     irqs_per_index;
>> +       u32     nr_msi_vec;
>> +       u32     nr_hw_irqs;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       struct xgene_msi_settings       *settings;
>> +       u32                             msi_addr_lo;
>> +       u32                             msi_addr_hi;
>
> I'd rather see the mailbox address directly, and only do the split when
> assigning it to the message (you seem to play all kind of tricks on the
> address anyway).

msi_addr_lo and msi_addr_hi store the physical base address of MSI
controller registers. I will add comment to clarify this.
>
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       int                             *msi_virqs;
>> +};
>> +
>> +struct xgene_msi_settings storm_msi_settings = {
>> +       .index_per_group        = 8,
>> +       .irqs_per_index         = 21,
>> +       .nr_msi_vec             = 2688,
>> +       .nr_hw_irqs             = 16,
>> +};
>
> It would really help understanding how index, group and hw irq lines are
> structured. nr_msi_vec is obviously the product of these three numbers,
> so maybe you can loose it (we have computers, remember... ;-).
>
> Do you expect more than this single "storm" implementation? If so, maybe
> they should be described in the DT. If not, why do we need a separate
> structure with an indirection if we know these are constants?
>
Yes, I will add description about index, group, and hw irq lines
structure and also replace the fields in this storm_msi_settings
structure with constant dedinitions.

>> +
>> +typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
>> +
>> +static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
>> +{
>> +       xgene_msi->settings = &storm_msi_settings;
>> +       return 0;
>> +}
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
>> +       u32 irqs_per_index = msi->settings->irqs_per_index;
>> +       u32 reg_set = data->hwirq / (nr_hw_irqs * irqs_per_index);
>> +       u32 group = data->hwirq % nr_hw_irqs;
>> +
>> +       msg->address_hi = msi->msi_addr_hi;
>> +       msg->address_lo = msi->msi_addr_lo +
>> +                         (((8 * group) + reg_set) << 16);
>> +       msg->data = (data->hwirq / nr_hw_irqs) % irqs_per_index;
>
> ... (sound of head exploding...). I hate it when I have to reverse
> engineer the hardware by looking at the code...
>
> Please give us some clue on how interrupts are spread across the various
> pages, how the various indexes are combined to give an actual address.

I will add description about this.
>
>> +}
>> +
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
>> +       unsigned int gic_irq;
>> +       int ret;
>> +
>> +       gic_irq = msi->msi_virqs[irq_data->hwirq % msi->settings->nr_hw_irqs];
>> +       ret = irq_set_affinity(gic_irq, mask);
>
> Erm... This as the effect of moving *all* the MSIs hanging off this
> interrupt to another CPU. I'm not sure that's an acceptable effect...
> What if another MSI requires a different affinity?

We have 16 'real' hardware IRQs. Each of these has multiple MSIs attached to it.
So this will move all MSIs handing off this interrupt to another CPU;
and we don't support different affinity settings for different MSIs
that are attached to the same hardware IRQ.

>
>> +       if (ret == IRQ_SET_MASK_OK)
>> +               ret = IRQ_SET_MASK_OK_DONE;
>> +
>> +       return ret;
>> +}
>> +
>> +static struct irq_chip xgene_msi_bottom_irq_chip = {
>> +       .name                   = "MSI",
>> +       .irq_set_affinity       = xgene_msi_set_affinity,
>> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
>> +};
>> +
>> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
>> +                                 unsigned int nr_irqs, void *args)
>> +{
>> +
>> +       struct xgene_msi *msi = domain->host_data;
>> +       u32 msi_irq_count = msi->settings->nr_msi_vec;
>> +       int msi_irq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       msi_irq = find_first_zero_bit(msi->bitmap, msi_irq_count);
>> +       if (msi_irq < msi_irq_count)
>> +               set_bit(msi_irq, msi->bitmap);
>> +       else
>> +               msi_irq = -ENOSPC;
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       if (msi_irq < 0)
>> +               return msi_irq;
>> +
>> +       irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
>> +                           domain->host_data, handle_simple_irq, NULL, NULL);
>> +       set_irq_flags(virq, IRQF_VALID);
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>> +                                 unsigned int virq, unsigned int nr_irqs)
>> +{
>> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       if (!test_bit(d->hwirq, msi->bitmap))
>> +               pr_err("trying to free unused MSI#%lu\n", d->hwirq);
>> +       else
>> +               clear_bit(d->hwirq, msi->bitmap);
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops msi_domain_ops = {
>> +       .alloc  = xgene_irq_domain_alloc,
>> +       .free   = xgene_irq_domain_free,
>> +};
>> +
>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>> +{
>> +       msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
>> +                                           &msi_domain_ops, msi);
>> +
>> +       if (!msi->domain) {
>> +               pr_err("failed to create parent MSI domain\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       msi->mchip.of_node = msi->node;
>> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>> +                                                     &xgene_msi_domain_info,
>> +                                                     msi->domain);
>> +
>> +       if (!msi->mchip.domain) {
>> +               pr_err("failed to create MSI domain\n");
>> +               irq_domain_remove(msi->domain);
>> +               return -ENOMEM;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_free_domains(struct xgene_msi *msi)
>> +{
>> +       if (msi->mchip.domain)
>> +               irq_domain_remove(msi->mchip.domain);
>> +       if (msi->domain)
>> +               irq_domain_remove(msi->domain);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +       u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>> +       u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
>> +       int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
>> +
>> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +       if (!xgene_msi->bitmap)
>> +               return -ENOMEM;
>> +
>> +       mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +       xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
>> +       if (!xgene_msi->msi_virqs)
>> +               return -ENOMEM;
>> +
>> +       return 0;
>> +}
>> +
>> +static irqreturn_t xgene_msi_isr(int irq, void *data)
>
> This is not a "normal" interrupt handler. This is really a chained
> interrupt controller. Please use the proper infrastructure for this
> (irq_set_chained_handler, chained_irq_enter, chained_irq_exit).
> Otherwise, your usage of irq_find_mapping is illegal wrt RCU.
>
Yes, I will change this function to protect it with chained_irq_enter,
chained_irq_exit

>> +{
>> +       struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
>> +       unsigned int virq;
>> +       int msir_index, msir_reg, msir_val, hw_irq;
>> +       u32 intr_index, grp_select, msi_grp, processed = 0;
>> +       u32 nr_hw_irqs, irqs_per_index, index_per_group;
>> +
>> +       msi_grp = irq - xgene_msi->msi_virqs[0];
>> +       if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
>> +               pr_err("invalid msi received\n");
>> +               return IRQ_NONE;
>> +       }
>> +
>> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +       irqs_per_index = xgene_msi->settings->irqs_per_index;
>> +       index_per_group = xgene_msi->settings->index_per_group;
>> +
>> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +       while (grp_select) {
>> +               msir_index = ffs(grp_select) - 1;
>> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
>> +               msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
>> +               while (msir_val) {
>> +                       intr_index = ffs(msir_val) - 1;
>> +                       hw_irq = (((msir_index * irqs_per_index) + intr_index) *
>> +                                nr_hw_irqs) + msi_grp;
>
> Same thing here. This requires some explaination on how the HW is organised.

I will have description for this.

>
>> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> +                       if (virq != 0)
>> +                               generic_handle_irq(virq);
>> +                       msir_val &= ~(1 << intr_index);
>> +                       processed++;
>> +               }
>> +               grp_select &= ~(1 << msir_index);
>> +       }
>> +
>> +       return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
>> +}
>> +
>> +static int xgene_msi_remove(struct platform_device *pdev)
>> +{
>> +       int virq, i;
>> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
>> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
>> +
>> +       for (i = 0; i < nr_hw_irqs; i++) {
>> +               virq = msi->msi_virqs[i];
>> +               if (virq != 0)
>> +                       free_irq(virq, msi);
>> +       }
>> +
>> +       kfree(msi->bitmap);
>> +       msi->bitmap = NULL;
>> +
>> +       xgene_free_domains(msi);
>> +
>> +       return 0;
>> +}
>> +
>> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
>> +                                struct platform_device *pdev,
>> +                                int irq_index)
>> +{
>> +       int virt_msir;
>> +       cpumask_var_t mask;
>> +       int err;
>> +
>> +       virt_msir = platform_get_irq(pdev, irq_index);
>> +       if (virt_msir < 0) {
>> +               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
>> +                       irq_index);
>> +               return -EINVAL;
>> +       }
>> +
>> +       err = request_irq(virt_msir, xgene_msi_isr, 0,
>> +                         xgene_msi_top_irq_chip.name, msi);
>
> This is where you should use irq_set_chained_handler.

I will replace request_irq with irq_set_chained_handler and irq_set_handler_data
>
>> +       if (err) {
>> +               dev_err(&pdev->dev, "request irq failed\n");
>> +               return err;
>> +       }
>> +
>> +       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
>> +               cpumask_setall(mask);
>> +               irq_set_affinity(virt_msir, mask);
>> +               free_cpumask_var(mask);
>> +       }
>> +
>> +       msi->msi_virqs[irq_index] = virt_msir;
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id xgene_msi_match_table[] = {
>> +       {.compatible = "apm,xgene1-msi",
>> +        .data = xgene_msi_init_storm_settings},
>> +       {},
>> +};
>> +
>> +static int xgene_msi_probe(struct platform_device *pdev)
>> +{
>> +       struct resource *res;
>> +       int rc, irq_index;
>> +       struct device_node *np;
>> +       const struct of_device_id *matched_np;
>> +       struct xgene_msi *xgene_msi;
>> +       xgene_msi_initcall_t init_fn;
>> +       u32 nr_hw_irqs, nr_msi_vecs;
>> +
>> +       np = of_find_matching_node_and_match(NULL,
>> +                            xgene_msi_match_table, &matched_np);
>> +       if (!np)
>> +               return -ENODEV;
>> +
>> +       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
>> +       if (!xgene_msi) {
>> +               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       xgene_msi->node = np;
>> +
>> +       init_fn = (xgene_msi_initcall_t) matched_np->data;
>> +       rc = init_fn(xgene_msi);
>> +       if (rc)
>> +               return rc;
>> +
>> +       platform_set_drvdata(pdev, xgene_msi);
>> +
>> +       nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(xgene_msi->msi_regs)) {
>> +               dev_err(&pdev->dev, "no reg space\n");
>> +               rc = -EINVAL;
>> +               goto error;
>> +       }
>> +
>> +       xgene_msi->msi_addr_hi = upper_32_bits(res->start);
>> +       xgene_msi->msi_addr_lo = lower_32_bits(res->start);
>> +
>> +       rc = xgene_msi_init_allocator(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
>> +               goto error;
>> +       }
>> +
>> +       rc = xgene_allocate_domains(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
>> +               goto error;
>> +       }
>> +
>> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +       for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
>> +               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
>> +               if (rc)
>> +                       goto error;
>> +       }
>> +
>> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
>> +               goto error;
>> +       }
>> +
>> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
>> +
>> +       return 0;
>> +error:
>> +       xgene_msi_remove(pdev);
>> +       return rc;
>> +}
>> +
>> +static struct platform_driver xgene_msi_driver = {
>> +       .driver = {
>> +               .name = "xgene-msi",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = xgene_msi_match_table,
>> +       },
>> +       .probe = xgene_msi_probe,
>> +       .remove = xgene_msi_remove,
>> +};
>> +
>> +static int __init xgene_pcie_msi_init(void)
>> +{
>> +       return platform_driver_register(&xgene_msi_driver);
>> +}
>> +subsys_initcall(xgene_pcie_msi_init);
>> +
>> +MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
>> +MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
>> index 22751ed..3e6faa1 100644
>> --- a/drivers/pci/host/pci-xgene.c
>> +++ b/drivers/pci/host/pci-xgene.c
>> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>>         return 0;
>>  }
>>
>> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
>> +{
>> +       struct device_node *msi_node;
>> +
>> +       msi_node = of_parse_phandle(bus->dev.of_node,
>> +                                       "msi-parent", 0);
>> +       if (!msi_node)
>> +               return -ENODEV;
>> +
>> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
>> +       if (bus->msi)
>> +               bus->msi->dev = &bus->dev;
>> +       else
>> +               return -ENODEV;
>> +       return 0;
>> +}
>> +
>>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>  {
>>         struct device_node *dn = pdev->dev.of_node;
>> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>         if (!bus)
>>                 return -ENOMEM;
>>
>> +       if (IS_ENABLED(CONFIG_PCI_MSI))
>> +               if (xgene_pcie_msi_enable(bus))
>> +                       dev_info(port->dev, "failed to enable MSI\n");
>> +
>>         pci_scan_child_bus(bus);
>>         pci_assign_unassigned_bus_resources(bus);
>>         pci_bus_add_devices(bus);
>> --
>> 1.9.1
>>
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...
Regards,
Duc Dang.

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-10 23:42                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-10 23:42 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 09/04/15 18:05, Duc Dang wrote:
>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>> 16 HW IRQ lines.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  drivers/pci/host/Kconfig         |   6 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 435 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 7b892a9..c9b61fa 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>> +       select PCI_XGENE_MSI if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index e61d91c..f39bde3 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..4f0ff42
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,407 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_INDEX0             0x000000
>> +#define MSI_INT0               0x800000
>> +
>> +struct xgene_msi_settings {
>> +       u32     index_per_group;
>> +       u32     irqs_per_index;
>> +       u32     nr_msi_vec;
>> +       u32     nr_hw_irqs;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       struct xgene_msi_settings       *settings;
>> +       u32                             msi_addr_lo;
>> +       u32                             msi_addr_hi;
>
> I'd rather see the mailbox address directly, and only do the split when
> assigning it to the message (you seem to play all kind of tricks on the
> address anyway).

msi_addr_lo and msi_addr_hi store the physical base address of MSI
controller registers. I will add comment to clarify this.
>
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       int                             *msi_virqs;
>> +};
>> +
>> +struct xgene_msi_settings storm_msi_settings = {
>> +       .index_per_group        = 8,
>> +       .irqs_per_index         = 21,
>> +       .nr_msi_vec             = 2688,
>> +       .nr_hw_irqs             = 16,
>> +};
>
> It would really help understanding how index, group and hw irq lines are
> structured. nr_msi_vec is obviously the product of these three numbers,
> so maybe you can loose it (we have computers, remember... ;-).
>
> Do you expect more than this single "storm" implementation? If so, maybe
> they should be described in the DT. If not, why do we need a separate
> structure with an indirection if we know these are constants?
>
Yes, I will add description about index, group, and hw irq lines
structure and also replace the fields in this storm_msi_settings
structure with constant dedinitions.

>> +
>> +typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
>> +
>> +static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
>> +{
>> +       xgene_msi->settings = &storm_msi_settings;
>> +       return 0;
>> +}
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
>> +       u32 irqs_per_index = msi->settings->irqs_per_index;
>> +       u32 reg_set = data->hwirq / (nr_hw_irqs * irqs_per_index);
>> +       u32 group = data->hwirq % nr_hw_irqs;
>> +
>> +       msg->address_hi = msi->msi_addr_hi;
>> +       msg->address_lo = msi->msi_addr_lo +
>> +                         (((8 * group) + reg_set) << 16);
>> +       msg->data = (data->hwirq / nr_hw_irqs) % irqs_per_index;
>
> ... (sound of head exploding...). I hate it when I have to reverse
> engineer the hardware by looking at the code...
>
> Please give us some clue on how interrupts are spread across the various
> pages, how the various indexes are combined to give an actual address.

I will add description about this.
>
>> +}
>> +
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
>> +       unsigned int gic_irq;
>> +       int ret;
>> +
>> +       gic_irq = msi->msi_virqs[irq_data->hwirq % msi->settings->nr_hw_irqs];
>> +       ret = irq_set_affinity(gic_irq, mask);
>
> Erm... This as the effect of moving *all* the MSIs hanging off this
> interrupt to another CPU. I'm not sure that's an acceptable effect...
> What if another MSI requires a different affinity?

We have 16 'real' hardware IRQs. Each of these has multiple MSIs attached to it.
So this will move all MSIs handing off this interrupt to another CPU;
and we don't support different affinity settings for different MSIs
that are attached to the same hardware IRQ.

>
>> +       if (ret == IRQ_SET_MASK_OK)
>> +               ret = IRQ_SET_MASK_OK_DONE;
>> +
>> +       return ret;
>> +}
>> +
>> +static struct irq_chip xgene_msi_bottom_irq_chip = {
>> +       .name                   = "MSI",
>> +       .irq_set_affinity       = xgene_msi_set_affinity,
>> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
>> +};
>> +
>> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
>> +                                 unsigned int nr_irqs, void *args)
>> +{
>> +
>> +       struct xgene_msi *msi = domain->host_data;
>> +       u32 msi_irq_count = msi->settings->nr_msi_vec;
>> +       int msi_irq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       msi_irq = find_first_zero_bit(msi->bitmap, msi_irq_count);
>> +       if (msi_irq < msi_irq_count)
>> +               set_bit(msi_irq, msi->bitmap);
>> +       else
>> +               msi_irq = -ENOSPC;
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       if (msi_irq < 0)
>> +               return msi_irq;
>> +
>> +       irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
>> +                           domain->host_data, handle_simple_irq, NULL, NULL);
>> +       set_irq_flags(virq, IRQF_VALID);
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>> +                                 unsigned int virq, unsigned int nr_irqs)
>> +{
>> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       if (!test_bit(d->hwirq, msi->bitmap))
>> +               pr_err("trying to free unused MSI#%lu\n", d->hwirq);
>> +       else
>> +               clear_bit(d->hwirq, msi->bitmap);
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops msi_domain_ops = {
>> +       .alloc  = xgene_irq_domain_alloc,
>> +       .free   = xgene_irq_domain_free,
>> +};
>> +
>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>> +{
>> +       msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
>> +                                           &msi_domain_ops, msi);
>> +
>> +       if (!msi->domain) {
>> +               pr_err("failed to create parent MSI domain\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       msi->mchip.of_node = msi->node;
>> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>> +                                                     &xgene_msi_domain_info,
>> +                                                     msi->domain);
>> +
>> +       if (!msi->mchip.domain) {
>> +               pr_err("failed to create MSI domain\n");
>> +               irq_domain_remove(msi->domain);
>> +               return -ENOMEM;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_free_domains(struct xgene_msi *msi)
>> +{
>> +       if (msi->mchip.domain)
>> +               irq_domain_remove(msi->mchip.domain);
>> +       if (msi->domain)
>> +               irq_domain_remove(msi->domain);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +       u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>> +       u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
>> +       int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
>> +
>> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +       if (!xgene_msi->bitmap)
>> +               return -ENOMEM;
>> +
>> +       mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +       xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
>> +       if (!xgene_msi->msi_virqs)
>> +               return -ENOMEM;
>> +
>> +       return 0;
>> +}
>> +
>> +static irqreturn_t xgene_msi_isr(int irq, void *data)
>
> This is not a "normal" interrupt handler. This is really a chained
> interrupt controller. Please use the proper infrastructure for this
> (irq_set_chained_handler, chained_irq_enter, chained_irq_exit).
> Otherwise, your usage of irq_find_mapping is illegal wrt RCU.
>
Yes, I will change this function to protect it with chained_irq_enter,
chained_irq_exit

>> +{
>> +       struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
>> +       unsigned int virq;
>> +       int msir_index, msir_reg, msir_val, hw_irq;
>> +       u32 intr_index, grp_select, msi_grp, processed = 0;
>> +       u32 nr_hw_irqs, irqs_per_index, index_per_group;
>> +
>> +       msi_grp = irq - xgene_msi->msi_virqs[0];
>> +       if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
>> +               pr_err("invalid msi received\n");
>> +               return IRQ_NONE;
>> +       }
>> +
>> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +       irqs_per_index = xgene_msi->settings->irqs_per_index;
>> +       index_per_group = xgene_msi->settings->index_per_group;
>> +
>> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +       while (grp_select) {
>> +               msir_index = ffs(grp_select) - 1;
>> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
>> +               msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
>> +               while (msir_val) {
>> +                       intr_index = ffs(msir_val) - 1;
>> +                       hw_irq = (((msir_index * irqs_per_index) + intr_index) *
>> +                                nr_hw_irqs) + msi_grp;
>
> Same thing here. This requires some explaination on how the HW is organised.

I will have description for this.

>
>> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> +                       if (virq != 0)
>> +                               generic_handle_irq(virq);
>> +                       msir_val &= ~(1 << intr_index);
>> +                       processed++;
>> +               }
>> +               grp_select &= ~(1 << msir_index);
>> +       }
>> +
>> +       return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
>> +}
>> +
>> +static int xgene_msi_remove(struct platform_device *pdev)
>> +{
>> +       int virq, i;
>> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
>> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
>> +
>> +       for (i = 0; i < nr_hw_irqs; i++) {
>> +               virq = msi->msi_virqs[i];
>> +               if (virq != 0)
>> +                       free_irq(virq, msi);
>> +       }
>> +
>> +       kfree(msi->bitmap);
>> +       msi->bitmap = NULL;
>> +
>> +       xgene_free_domains(msi);
>> +
>> +       return 0;
>> +}
>> +
>> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
>> +                                struct platform_device *pdev,
>> +                                int irq_index)
>> +{
>> +       int virt_msir;
>> +       cpumask_var_t mask;
>> +       int err;
>> +
>> +       virt_msir = platform_get_irq(pdev, irq_index);
>> +       if (virt_msir < 0) {
>> +               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
>> +                       irq_index);
>> +               return -EINVAL;
>> +       }
>> +
>> +       err = request_irq(virt_msir, xgene_msi_isr, 0,
>> +                         xgene_msi_top_irq_chip.name, msi);
>
> This is where you should use irq_set_chained_handler.

I will replace request_irq with irq_set_chained_handler and irq_set_handler_data
>
>> +       if (err) {
>> +               dev_err(&pdev->dev, "request irq failed\n");
>> +               return err;
>> +       }
>> +
>> +       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
>> +               cpumask_setall(mask);
>> +               irq_set_affinity(virt_msir, mask);
>> +               free_cpumask_var(mask);
>> +       }
>> +
>> +       msi->msi_virqs[irq_index] = virt_msir;
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id xgene_msi_match_table[] = {
>> +       {.compatible = "apm,xgene1-msi",
>> +        .data = xgene_msi_init_storm_settings},
>> +       {},
>> +};
>> +
>> +static int xgene_msi_probe(struct platform_device *pdev)
>> +{
>> +       struct resource *res;
>> +       int rc, irq_index;
>> +       struct device_node *np;
>> +       const struct of_device_id *matched_np;
>> +       struct xgene_msi *xgene_msi;
>> +       xgene_msi_initcall_t init_fn;
>> +       u32 nr_hw_irqs, nr_msi_vecs;
>> +
>> +       np = of_find_matching_node_and_match(NULL,
>> +                            xgene_msi_match_table, &matched_np);
>> +       if (!np)
>> +               return -ENODEV;
>> +
>> +       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
>> +       if (!xgene_msi) {
>> +               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       xgene_msi->node = np;
>> +
>> +       init_fn = (xgene_msi_initcall_t) matched_np->data;
>> +       rc = init_fn(xgene_msi);
>> +       if (rc)
>> +               return rc;
>> +
>> +       platform_set_drvdata(pdev, xgene_msi);
>> +
>> +       nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(xgene_msi->msi_regs)) {
>> +               dev_err(&pdev->dev, "no reg space\n");
>> +               rc = -EINVAL;
>> +               goto error;
>> +       }
>> +
>> +       xgene_msi->msi_addr_hi = upper_32_bits(res->start);
>> +       xgene_msi->msi_addr_lo = lower_32_bits(res->start);
>> +
>> +       rc = xgene_msi_init_allocator(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
>> +               goto error;
>> +       }
>> +
>> +       rc = xgene_allocate_domains(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
>> +               goto error;
>> +       }
>> +
>> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +       for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
>> +               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
>> +               if (rc)
>> +                       goto error;
>> +       }
>> +
>> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
>> +               goto error;
>> +       }
>> +
>> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
>> +
>> +       return 0;
>> +error:
>> +       xgene_msi_remove(pdev);
>> +       return rc;
>> +}
>> +
>> +static struct platform_driver xgene_msi_driver = {
>> +       .driver = {
>> +               .name = "xgene-msi",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = xgene_msi_match_table,
>> +       },
>> +       .probe = xgene_msi_probe,
>> +       .remove = xgene_msi_remove,
>> +};
>> +
>> +static int __init xgene_pcie_msi_init(void)
>> +{
>> +       return platform_driver_register(&xgene_msi_driver);
>> +}
>> +subsys_initcall(xgene_pcie_msi_init);
>> +
>> +MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
>> +MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
>> index 22751ed..3e6faa1 100644
>> --- a/drivers/pci/host/pci-xgene.c
>> +++ b/drivers/pci/host/pci-xgene.c
>> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>>         return 0;
>>  }
>>
>> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
>> +{
>> +       struct device_node *msi_node;
>> +
>> +       msi_node = of_parse_phandle(bus->dev.of_node,
>> +                                       "msi-parent", 0);
>> +       if (!msi_node)
>> +               return -ENODEV;
>> +
>> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
>> +       if (bus->msi)
>> +               bus->msi->dev = &bus->dev;
>> +       else
>> +               return -ENODEV;
>> +       return 0;
>> +}
>> +
>>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>  {
>>         struct device_node *dn = pdev->dev.of_node;
>> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>         if (!bus)
>>                 return -ENOMEM;
>>
>> +       if (IS_ENABLED(CONFIG_PCI_MSI))
>> +               if (xgene_pcie_msi_enable(bus))
>> +                       dev_info(port->dev, "failed to enable MSI\n");
>> +
>>         pci_scan_child_bus(bus);
>>         pci_assign_unassigned_bus_resources(bus);
>>         pci_bus_add_devices(bus);
>> --
>> 1.9.1
>>
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...
Regards,
Duc Dang.

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-10 18:13                         ` Paul Bolle
@ 2015-04-10 23:55                           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-10 23:55 UTC (permalink / raw)
  To: Paul Bolle
  Cc: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau,
	Marc Zyngier, linux-pci, linux-arm, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Fri, Apr 10, 2015 at 11:13 AM, Paul Bolle <pebolle@tiscali.nl> wrote:
> Just a nit about a license mismatch.
>
> On Thu, 2015-04-09 at 10:05 -0700, Duc Dang wrote:
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>
>> +config PCI_XGENE_MSI
>> +     bool "X-Gene v1 PCIe MSI feature"
>> +     depends on PCI_XGENE && PCI_MSI
>
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>
>> + * This program is free software; you can redistribute  it and/or modify it
>> + * under  the terms of  the GNU General  Public License as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>
> This states the license of this file is GPL v2 or later.
>
>> +MODULE_LICENSE("GPL v2");
>
> And, according to include/linux/module.h, this states the license of
> this driver is GPL v2. So either the comment at the top of this file or
> the license ident used in this macro needs to change.
>
Thanks, I will fix this.

> (Now, as far as I can tell, this driver is built-in only. So I think
> this macro - and all other module specific code in this file - is
> actually unneeded. But I already know Bjorn's position here, so I won't
> bother you about that.)
>
> Thanks,
>
>
> Paul Bolle
>

Regards,
Duc Dang.

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-10 23:55                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-10 23:55 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 10, 2015 at 11:13 AM, Paul Bolle <pebolle@tiscali.nl> wrote:
> Just a nit about a license mismatch.
>
> On Thu, 2015-04-09 at 10:05 -0700, Duc Dang wrote:
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>
>> +config PCI_XGENE_MSI
>> +     bool "X-Gene v1 PCIe MSI feature"
>> +     depends on PCI_XGENE && PCI_MSI
>
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>
>> + * This program is free software; you can redistribute  it and/or modify it
>> + * under  the terms of  the GNU General  Public License as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>
> This states the license of this file is GPL v2 or later.
>
>> +MODULE_LICENSE("GPL v2");
>
> And, according to include/linux/module.h, this states the license of
> this driver is GPL v2. So either the comment at the top of this file or
> the license ident used in this macro needs to change.
>
Thanks, I will fix this.

> (Now, as far as I can tell, this driver is built-in only. So I think
> this macro - and all other module specific code in this file - is
> actually unneeded. But I already know Bjorn's position here, so I won't
> bother you about that.)
>
> Thanks,
>
>
> Paul Bolle
>

Regards,
Duc Dang.

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-10 17:20                         ` Marc Zyngier
  (?)
@ 2015-04-11  0:16                           ` Feng Kan
  -1 siblings, 0 replies; 230+ messages in thread
From: Feng Kan @ 2015-04-11  0:16 UTC (permalink / raw)
  To: Marc Zyngier, Rafael J. Wysocki
  Cc: Duc Dang, Bjorn Helgaas, Arnd Bergmann, grant.likely,
	Liviu Dudau, linux-pci, linux-arm-kernel, linux-kernel,
	Tanmay Inamdar, Loc Ho

Hi Marc:

Is there any plans to support ACPI for GICv2m MSI? Both this driver and the
GICv2m seems to support OF model of discovery for msi controller. X-Gene1
uses this driver and X-Gene2 uses GICv2m, there needs to be a way to
associate msi controller with the PCIe bus. I haven't
found a standard way of doing finding "msi-parent" for ACPI. Do you have
any suggestion.

Sorry for top posting.


On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 09/04/15 18:05, Duc Dang wrote:
>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>> 16 HW IRQ lines.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  drivers/pci/host/Kconfig         |   6 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 435 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 7b892a9..c9b61fa 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>> +       select PCI_XGENE_MSI if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index e61d91c..f39bde3 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..4f0ff42
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,407 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_INDEX0             0x000000
>> +#define MSI_INT0               0x800000
>> +
>> +struct xgene_msi_settings {
>> +       u32     index_per_group;
>> +       u32     irqs_per_index;
>> +       u32     nr_msi_vec;
>> +       u32     nr_hw_irqs;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       struct xgene_msi_settings       *settings;
>> +       u32                             msi_addr_lo;
>> +       u32                             msi_addr_hi;
>
> I'd rather see the mailbox address directly, and only do the split when
> assigning it to the message (you seem to play all kind of tricks on the
> address anyway).
>
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       int                             *msi_virqs;
>> +};
>> +
>> +struct xgene_msi_settings storm_msi_settings = {
>> +       .index_per_group        = 8,
>> +       .irqs_per_index         = 21,
>> +       .nr_msi_vec             = 2688,
>> +       .nr_hw_irqs             = 16,
>> +};
>
> It would really help understanding how index, group and hw irq lines are
> structured. nr_msi_vec is obviously the product of these three numbers,
> so maybe you can loose it (we have computers, remember... ;-).
>
> Do you expect more than this single "storm" implementation? If so, maybe
> they should be described in the DT. If not, why do we need a separate
> structure with an indirection if we know these are constants?
>
>> +
>> +typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
>> +
>> +static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
>> +{
>> +       xgene_msi->settings = &storm_msi_settings;
>> +       return 0;
>> +}
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
>> +       u32 irqs_per_index = msi->settings->irqs_per_index;
>> +       u32 reg_set = data->hwirq / (nr_hw_irqs * irqs_per_index);
>> +       u32 group = data->hwirq % nr_hw_irqs;
>> +
>> +       msg->address_hi = msi->msi_addr_hi;
>> +       msg->address_lo = msi->msi_addr_lo +
>> +                         (((8 * group) + reg_set) << 16);
>> +       msg->data = (data->hwirq / nr_hw_irqs) % irqs_per_index;
>
> ... (sound of head exploding...). I hate it when I have to reverse
> engineer the hardware by looking at the code...
>
> Please give us some clue on how interrupts are spread across the various
> pages, how the various indexes are combined to give an actual address.
>
>> +}
>> +
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
>> +       unsigned int gic_irq;
>> +       int ret;
>> +
>> +       gic_irq = msi->msi_virqs[irq_data->hwirq % msi->settings->nr_hw_irqs];
>> +       ret = irq_set_affinity(gic_irq, mask);
>
> Erm... This as the effect of moving *all* the MSIs hanging off this
> interrupt to another CPU. I'm not sure that's an acceptable effect...
> What if another MSI requires a different affinity?
>
>> +       if (ret == IRQ_SET_MASK_OK)
>> +               ret = IRQ_SET_MASK_OK_DONE;
>> +
>> +       return ret;
>> +}
>> +
>> +static struct irq_chip xgene_msi_bottom_irq_chip = {
>> +       .name                   = "MSI",
>> +       .irq_set_affinity       = xgene_msi_set_affinity,
>> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
>> +};
>> +
>> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
>> +                                 unsigned int nr_irqs, void *args)
>> +{
>> +
>> +       struct xgene_msi *msi = domain->host_data;
>> +       u32 msi_irq_count = msi->settings->nr_msi_vec;
>> +       int msi_irq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       msi_irq = find_first_zero_bit(msi->bitmap, msi_irq_count);
>> +       if (msi_irq < msi_irq_count)
>> +               set_bit(msi_irq, msi->bitmap);
>> +       else
>> +               msi_irq = -ENOSPC;
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       if (msi_irq < 0)
>> +               return msi_irq;
>> +
>> +       irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
>> +                           domain->host_data, handle_simple_irq, NULL, NULL);
>> +       set_irq_flags(virq, IRQF_VALID);
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>> +                                 unsigned int virq, unsigned int nr_irqs)
>> +{
>> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       if (!test_bit(d->hwirq, msi->bitmap))
>> +               pr_err("trying to free unused MSI#%lu\n", d->hwirq);
>> +       else
>> +               clear_bit(d->hwirq, msi->bitmap);
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops msi_domain_ops = {
>> +       .alloc  = xgene_irq_domain_alloc,
>> +       .free   = xgene_irq_domain_free,
>> +};
>> +
>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>> +{
>> +       msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
>> +                                           &msi_domain_ops, msi);
>> +
>> +       if (!msi->domain) {
>> +               pr_err("failed to create parent MSI domain\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       msi->mchip.of_node = msi->node;
>> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>> +                                                     &xgene_msi_domain_info,
>> +                                                     msi->domain);
>> +
>> +       if (!msi->mchip.domain) {
>> +               pr_err("failed to create MSI domain\n");
>> +               irq_domain_remove(msi->domain);
>> +               return -ENOMEM;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_free_domains(struct xgene_msi *msi)
>> +{
>> +       if (msi->mchip.domain)
>> +               irq_domain_remove(msi->mchip.domain);
>> +       if (msi->domain)
>> +               irq_domain_remove(msi->domain);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +       u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>> +       u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
>> +       int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
>> +
>> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +       if (!xgene_msi->bitmap)
>> +               return -ENOMEM;
>> +
>> +       mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +       xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
>> +       if (!xgene_msi->msi_virqs)
>> +               return -ENOMEM;
>> +
>> +       return 0;
>> +}
>> +
>> +static irqreturn_t xgene_msi_isr(int irq, void *data)
>
> This is not a "normal" interrupt handler. This is really a chained
> interrupt controller. Please use the proper infrastructure for this
> (irq_set_chained_handler, chained_irq_enter, chained_irq_exit).
> Otherwise, your usage of irq_find_mapping is illegal wrt RCU.
>
>> +{
>> +       struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
>> +       unsigned int virq;
>> +       int msir_index, msir_reg, msir_val, hw_irq;
>> +       u32 intr_index, grp_select, msi_grp, processed = 0;
>> +       u32 nr_hw_irqs, irqs_per_index, index_per_group;
>> +
>> +       msi_grp = irq - xgene_msi->msi_virqs[0];
>> +       if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
>> +               pr_err("invalid msi received\n");
>> +               return IRQ_NONE;
>> +       }
>> +
>> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +       irqs_per_index = xgene_msi->settings->irqs_per_index;
>> +       index_per_group = xgene_msi->settings->index_per_group;
>> +
>> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +       while (grp_select) {
>> +               msir_index = ffs(grp_select) - 1;
>> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
>> +               msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
>> +               while (msir_val) {
>> +                       intr_index = ffs(msir_val) - 1;
>> +                       hw_irq = (((msir_index * irqs_per_index) + intr_index) *
>> +                                nr_hw_irqs) + msi_grp;
>
> Same thing here. This requires some explaination on how the HW is organised.
>
>> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> +                       if (virq != 0)
>> +                               generic_handle_irq(virq);
>> +                       msir_val &= ~(1 << intr_index);
>> +                       processed++;
>> +               }
>> +               grp_select &= ~(1 << msir_index);
>> +       }
>> +
>> +       return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
>> +}
>> +
>> +static int xgene_msi_remove(struct platform_device *pdev)
>> +{
>> +       int virq, i;
>> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
>> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
>> +
>> +       for (i = 0; i < nr_hw_irqs; i++) {
>> +               virq = msi->msi_virqs[i];
>> +               if (virq != 0)
>> +                       free_irq(virq, msi);
>> +       }
>> +
>> +       kfree(msi->bitmap);
>> +       msi->bitmap = NULL;
>> +
>> +       xgene_free_domains(msi);
>> +
>> +       return 0;
>> +}
>> +
>> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
>> +                                struct platform_device *pdev,
>> +                                int irq_index)
>> +{
>> +       int virt_msir;
>> +       cpumask_var_t mask;
>> +       int err;
>> +
>> +       virt_msir = platform_get_irq(pdev, irq_index);
>> +       if (virt_msir < 0) {
>> +               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
>> +                       irq_index);
>> +               return -EINVAL;
>> +       }
>> +
>> +       err = request_irq(virt_msir, xgene_msi_isr, 0,
>> +                         xgene_msi_top_irq_chip.name, msi);
>
> This is where you should use irq_set_chained_handler.
>
>> +       if (err) {
>> +               dev_err(&pdev->dev, "request irq failed\n");
>> +               return err;
>> +       }
>> +
>> +       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
>> +               cpumask_setall(mask);
>> +               irq_set_affinity(virt_msir, mask);
>> +               free_cpumask_var(mask);
>> +       }
>> +
>> +       msi->msi_virqs[irq_index] = virt_msir;
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id xgene_msi_match_table[] = {
>> +       {.compatible = "apm,xgene1-msi",
>> +        .data = xgene_msi_init_storm_settings},
>> +       {},
>> +};
>> +
>> +static int xgene_msi_probe(struct platform_device *pdev)
>> +{
>> +       struct resource *res;
>> +       int rc, irq_index;
>> +       struct device_node *np;
>> +       const struct of_device_id *matched_np;
>> +       struct xgene_msi *xgene_msi;
>> +       xgene_msi_initcall_t init_fn;
>> +       u32 nr_hw_irqs, nr_msi_vecs;
>> +
>> +       np = of_find_matching_node_and_match(NULL,
>> +                            xgene_msi_match_table, &matched_np);
>> +       if (!np)
>> +               return -ENODEV;
>> +
>> +       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
>> +       if (!xgene_msi) {
>> +               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       xgene_msi->node = np;
>> +
>> +       init_fn = (xgene_msi_initcall_t) matched_np->data;
>> +       rc = init_fn(xgene_msi);
>> +       if (rc)
>> +               return rc;
>> +
>> +       platform_set_drvdata(pdev, xgene_msi);
>> +
>> +       nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(xgene_msi->msi_regs)) {
>> +               dev_err(&pdev->dev, "no reg space\n");
>> +               rc = -EINVAL;
>> +               goto error;
>> +       }
>> +
>> +       xgene_msi->msi_addr_hi = upper_32_bits(res->start);
>> +       xgene_msi->msi_addr_lo = lower_32_bits(res->start);
>> +
>> +       rc = xgene_msi_init_allocator(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
>> +               goto error;
>> +       }
>> +
>> +       rc = xgene_allocate_domains(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
>> +               goto error;
>> +       }
>> +
>> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +       for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
>> +               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
>> +               if (rc)
>> +                       goto error;
>> +       }
>> +
>> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
>> +               goto error;
>> +       }
>> +
>> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
>> +
>> +       return 0;
>> +error:
>> +       xgene_msi_remove(pdev);
>> +       return rc;
>> +}
>> +
>> +static struct platform_driver xgene_msi_driver = {
>> +       .driver = {
>> +               .name = "xgene-msi",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = xgene_msi_match_table,
>> +       },
>> +       .probe = xgene_msi_probe,
>> +       .remove = xgene_msi_remove,
>> +};
>> +
>> +static int __init xgene_pcie_msi_init(void)
>> +{
>> +       return platform_driver_register(&xgene_msi_driver);
>> +}
>> +subsys_initcall(xgene_pcie_msi_init);
>> +
>> +MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
>> +MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
>> index 22751ed..3e6faa1 100644
>> --- a/drivers/pci/host/pci-xgene.c
>> +++ b/drivers/pci/host/pci-xgene.c
>> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>>         return 0;
>>  }
>>
>> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
>> +{
>> +       struct device_node *msi_node;
>> +
>> +       msi_node = of_parse_phandle(bus->dev.of_node,
>> +                                       "msi-parent", 0);
>> +       if (!msi_node)
>> +               return -ENODEV;
>> +
>> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
>> +       if (bus->msi)
>> +               bus->msi->dev = &bus->dev;
>> +       else
>> +               return -ENODEV;
>> +       return 0;
>> +}
>> +
>>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>  {
>>         struct device_node *dn = pdev->dev.of_node;
>> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>         if (!bus)
>>                 return -ENOMEM;
>>
>> +       if (IS_ENABLED(CONFIG_PCI_MSI))
>> +               if (xgene_pcie_msi_enable(bus))
>> +                       dev_info(port->dev, "failed to enable MSI\n");
>> +
>>         pci_scan_child_bus(bus);
>>         pci_assign_unassigned_bus_resources(bus);
>>         pci_bus_add_devices(bus);
>> --
>> 1.9.1
>>
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-11  0:16                           ` Feng Kan
  0 siblings, 0 replies; 230+ messages in thread
From: Feng Kan @ 2015-04-11  0:16 UTC (permalink / raw)
  To: Marc Zyngier, Rafael J. Wysocki
  Cc: Duc Dang, Bjorn Helgaas, Arnd Bergmann, grant.likely,
	Liviu Dudau, linux-pci, linux-arm-kernel, linux-kernel,
	Tanmay Inamdar, Loc Ho

Hi Marc:

Is there any plans to support ACPI for GICv2m MSI? Both this driver and the
GICv2m seems to support OF model of discovery for msi controller. X-Gene1
uses this driver and X-Gene2 uses GICv2m, there needs to be a way to
associate msi controller with the PCIe bus. I haven't
found a standard way of doing finding "msi-parent" for ACPI. Do you have
any suggestion.

Sorry for top posting.


On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 09/04/15 18:05, Duc Dang wrote:
>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>> 16 HW IRQ lines.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  drivers/pci/host/Kconfig         |   6 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 435 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 7b892a9..c9b61fa 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>> +       select PCI_XGENE_MSI if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index e61d91c..f39bde3 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..4f0ff42
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,407 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_INDEX0             0x000000
>> +#define MSI_INT0               0x800000
>> +
>> +struct xgene_msi_settings {
>> +       u32     index_per_group;
>> +       u32     irqs_per_index;
>> +       u32     nr_msi_vec;
>> +       u32     nr_hw_irqs;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       struct xgene_msi_settings       *settings;
>> +       u32                             msi_addr_lo;
>> +       u32                             msi_addr_hi;
>
> I'd rather see the mailbox address directly, and only do the split when
> assigning it to the message (you seem to play all kind of tricks on the
> address anyway).
>
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       int                             *msi_virqs;
>> +};
>> +
>> +struct xgene_msi_settings storm_msi_settings = {
>> +       .index_per_group        = 8,
>> +       .irqs_per_index         = 21,
>> +       .nr_msi_vec             = 2688,
>> +       .nr_hw_irqs             = 16,
>> +};
>
> It would really help understanding how index, group and hw irq lines are
> structured. nr_msi_vec is obviously the product of these three numbers,
> so maybe you can loose it (we have computers, remember... ;-).
>
> Do you expect more than this single "storm" implementation? If so, maybe
> they should be described in the DT. If not, why do we need a separate
> structure with an indirection if we know these are constants?
>
>> +
>> +typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
>> +
>> +static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
>> +{
>> +       xgene_msi->settings = &storm_msi_settings;
>> +       return 0;
>> +}
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
>> +       u32 irqs_per_index = msi->settings->irqs_per_index;
>> +       u32 reg_set = data->hwirq / (nr_hw_irqs * irqs_per_index);
>> +       u32 group = data->hwirq % nr_hw_irqs;
>> +
>> +       msg->address_hi = msi->msi_addr_hi;
>> +       msg->address_lo = msi->msi_addr_lo +
>> +                         (((8 * group) + reg_set) << 16);
>> +       msg->data = (data->hwirq / nr_hw_irqs) % irqs_per_index;
>
> ... (sound of head exploding...). I hate it when I have to reverse
> engineer the hardware by looking at the code...
>
> Please give us some clue on how interrupts are spread across the various
> pages, how the various indexes are combined to give an actual address.
>
>> +}
>> +
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
>> +       unsigned int gic_irq;
>> +       int ret;
>> +
>> +       gic_irq = msi->msi_virqs[irq_data->hwirq % msi->settings->nr_hw_irqs];
>> +       ret = irq_set_affinity(gic_irq, mask);
>
> Erm... This as the effect of moving *all* the MSIs hanging off this
> interrupt to another CPU. I'm not sure that's an acceptable effect...
> What if another MSI requires a different affinity?
>
>> +       if (ret == IRQ_SET_MASK_OK)
>> +               ret = IRQ_SET_MASK_OK_DONE;
>> +
>> +       return ret;
>> +}
>> +
>> +static struct irq_chip xgene_msi_bottom_irq_chip = {
>> +       .name                   = "MSI",
>> +       .irq_set_affinity       = xgene_msi_set_affinity,
>> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
>> +};
>> +
>> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
>> +                                 unsigned int nr_irqs, void *args)
>> +{
>> +
>> +       struct xgene_msi *msi = domain->host_data;
>> +       u32 msi_irq_count = msi->settings->nr_msi_vec;
>> +       int msi_irq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       msi_irq = find_first_zero_bit(msi->bitmap, msi_irq_count);
>> +       if (msi_irq < msi_irq_count)
>> +               set_bit(msi_irq, msi->bitmap);
>> +       else
>> +               msi_irq = -ENOSPC;
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       if (msi_irq < 0)
>> +               return msi_irq;
>> +
>> +       irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
>> +                           domain->host_data, handle_simple_irq, NULL, NULL);
>> +       set_irq_flags(virq, IRQF_VALID);
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>> +                                 unsigned int virq, unsigned int nr_irqs)
>> +{
>> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       if (!test_bit(d->hwirq, msi->bitmap))
>> +               pr_err("trying to free unused MSI#%lu\n", d->hwirq);
>> +       else
>> +               clear_bit(d->hwirq, msi->bitmap);
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops msi_domain_ops = {
>> +       .alloc  = xgene_irq_domain_alloc,
>> +       .free   = xgene_irq_domain_free,
>> +};
>> +
>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>> +{
>> +       msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
>> +                                           &msi_domain_ops, msi);
>> +
>> +       if (!msi->domain) {
>> +               pr_err("failed to create parent MSI domain\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       msi->mchip.of_node = msi->node;
>> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>> +                                                     &xgene_msi_domain_info,
>> +                                                     msi->domain);
>> +
>> +       if (!msi->mchip.domain) {
>> +               pr_err("failed to create MSI domain\n");
>> +               irq_domain_remove(msi->domain);
>> +               return -ENOMEM;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_free_domains(struct xgene_msi *msi)
>> +{
>> +       if (msi->mchip.domain)
>> +               irq_domain_remove(msi->mchip.domain);
>> +       if (msi->domain)
>> +               irq_domain_remove(msi->domain);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +       u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>> +       u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
>> +       int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
>> +
>> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +       if (!xgene_msi->bitmap)
>> +               return -ENOMEM;
>> +
>> +       mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +       xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
>> +       if (!xgene_msi->msi_virqs)
>> +               return -ENOMEM;
>> +
>> +       return 0;
>> +}
>> +
>> +static irqreturn_t xgene_msi_isr(int irq, void *data)
>
> This is not a "normal" interrupt handler. This is really a chained
> interrupt controller. Please use the proper infrastructure for this
> (irq_set_chained_handler, chained_irq_enter, chained_irq_exit).
> Otherwise, your usage of irq_find_mapping is illegal wrt RCU.
>
>> +{
>> +       struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
>> +       unsigned int virq;
>> +       int msir_index, msir_reg, msir_val, hw_irq;
>> +       u32 intr_index, grp_select, msi_grp, processed = 0;
>> +       u32 nr_hw_irqs, irqs_per_index, index_per_group;
>> +
>> +       msi_grp = irq - xgene_msi->msi_virqs[0];
>> +       if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
>> +               pr_err("invalid msi received\n");
>> +               return IRQ_NONE;
>> +       }
>> +
>> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +       irqs_per_index = xgene_msi->settings->irqs_per_index;
>> +       index_per_group = xgene_msi->settings->index_per_group;
>> +
>> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +       while (grp_select) {
>> +               msir_index = ffs(grp_select) - 1;
>> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
>> +               msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
>> +               while (msir_val) {
>> +                       intr_index = ffs(msir_val) - 1;
>> +                       hw_irq = (((msir_index * irqs_per_index) + intr_index) *
>> +                                nr_hw_irqs) + msi_grp;
>
> Same thing here. This requires some explaination on how the HW is organised.
>
>> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> +                       if (virq != 0)
>> +                               generic_handle_irq(virq);
>> +                       msir_val &= ~(1 << intr_index);
>> +                       processed++;
>> +               }
>> +               grp_select &= ~(1 << msir_index);
>> +       }
>> +
>> +       return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
>> +}
>> +
>> +static int xgene_msi_remove(struct platform_device *pdev)
>> +{
>> +       int virq, i;
>> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
>> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
>> +
>> +       for (i = 0; i < nr_hw_irqs; i++) {
>> +               virq = msi->msi_virqs[i];
>> +               if (virq != 0)
>> +                       free_irq(virq, msi);
>> +       }
>> +
>> +       kfree(msi->bitmap);
>> +       msi->bitmap = NULL;
>> +
>> +       xgene_free_domains(msi);
>> +
>> +       return 0;
>> +}
>> +
>> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
>> +                                struct platform_device *pdev,
>> +                                int irq_index)
>> +{
>> +       int virt_msir;
>> +       cpumask_var_t mask;
>> +       int err;
>> +
>> +       virt_msir = platform_get_irq(pdev, irq_index);
>> +       if (virt_msir < 0) {
>> +               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
>> +                       irq_index);
>> +               return -EINVAL;
>> +       }
>> +
>> +       err = request_irq(virt_msir, xgene_msi_isr, 0,
>> +                         xgene_msi_top_irq_chip.name, msi);
>
> This is where you should use irq_set_chained_handler.
>
>> +       if (err) {
>> +               dev_err(&pdev->dev, "request irq failed\n");
>> +               return err;
>> +       }
>> +
>> +       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
>> +               cpumask_setall(mask);
>> +               irq_set_affinity(virt_msir, mask);
>> +               free_cpumask_var(mask);
>> +       }
>> +
>> +       msi->msi_virqs[irq_index] = virt_msir;
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id xgene_msi_match_table[] = {
>> +       {.compatible = "apm,xgene1-msi",
>> +        .data = xgene_msi_init_storm_settings},
>> +       {},
>> +};
>> +
>> +static int xgene_msi_probe(struct platform_device *pdev)
>> +{
>> +       struct resource *res;
>> +       int rc, irq_index;
>> +       struct device_node *np;
>> +       const struct of_device_id *matched_np;
>> +       struct xgene_msi *xgene_msi;
>> +       xgene_msi_initcall_t init_fn;
>> +       u32 nr_hw_irqs, nr_msi_vecs;
>> +
>> +       np = of_find_matching_node_and_match(NULL,
>> +                            xgene_msi_match_table, &matched_np);
>> +       if (!np)
>> +               return -ENODEV;
>> +
>> +       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
>> +       if (!xgene_msi) {
>> +               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       xgene_msi->node = np;
>> +
>> +       init_fn = (xgene_msi_initcall_t) matched_np->data;
>> +       rc = init_fn(xgene_msi);
>> +       if (rc)
>> +               return rc;
>> +
>> +       platform_set_drvdata(pdev, xgene_msi);
>> +
>> +       nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(xgene_msi->msi_regs)) {
>> +               dev_err(&pdev->dev, "no reg space\n");
>> +               rc = -EINVAL;
>> +               goto error;
>> +       }
>> +
>> +       xgene_msi->msi_addr_hi = upper_32_bits(res->start);
>> +       xgene_msi->msi_addr_lo = lower_32_bits(res->start);
>> +
>> +       rc = xgene_msi_init_allocator(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
>> +               goto error;
>> +       }
>> +
>> +       rc = xgene_allocate_domains(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
>> +               goto error;
>> +       }
>> +
>> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +       for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
>> +               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
>> +               if (rc)
>> +                       goto error;
>> +       }
>> +
>> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
>> +               goto error;
>> +       }
>> +
>> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
>> +
>> +       return 0;
>> +error:
>> +       xgene_msi_remove(pdev);
>> +       return rc;
>> +}
>> +
>> +static struct platform_driver xgene_msi_driver = {
>> +       .driver = {
>> +               .name = "xgene-msi",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = xgene_msi_match_table,
>> +       },
>> +       .probe = xgene_msi_probe,
>> +       .remove = xgene_msi_remove,
>> +};
>> +
>> +static int __init xgene_pcie_msi_init(void)
>> +{
>> +       return platform_driver_register(&xgene_msi_driver);
>> +}
>> +subsys_initcall(xgene_pcie_msi_init);
>> +
>> +MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
>> +MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
>> index 22751ed..3e6faa1 100644
>> --- a/drivers/pci/host/pci-xgene.c
>> +++ b/drivers/pci/host/pci-xgene.c
>> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>>         return 0;
>>  }
>>
>> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
>> +{
>> +       struct device_node *msi_node;
>> +
>> +       msi_node = of_parse_phandle(bus->dev.of_node,
>> +                                       "msi-parent", 0);
>> +       if (!msi_node)
>> +               return -ENODEV;
>> +
>> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
>> +       if (bus->msi)
>> +               bus->msi->dev = &bus->dev;
>> +       else
>> +               return -ENODEV;
>> +       return 0;
>> +}
>> +
>>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>  {
>>         struct device_node *dn = pdev->dev.of_node;
>> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>         if (!bus)
>>                 return -ENOMEM;
>>
>> +       if (IS_ENABLED(CONFIG_PCI_MSI))
>> +               if (xgene_pcie_msi_enable(bus))
>> +                       dev_info(port->dev, "failed to enable MSI\n");
>> +
>>         pci_scan_child_bus(bus);
>>         pci_assign_unassigned_bus_resources(bus);
>>         pci_bus_add_devices(bus);
>> --
>> 1.9.1
>>
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-11  0:16                           ` Feng Kan
  0 siblings, 0 replies; 230+ messages in thread
From: Feng Kan @ 2015-04-11  0:16 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Marc:

Is there any plans to support ACPI for GICv2m MSI? Both this driver and the
GICv2m seems to support OF model of discovery for msi controller. X-Gene1
uses this driver and X-Gene2 uses GICv2m, there needs to be a way to
associate msi controller with the PCIe bus. I haven't
found a standard way of doing finding "msi-parent" for ACPI. Do you have
any suggestion.

Sorry for top posting.


On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 09/04/15 18:05, Duc Dang wrote:
>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>> 16 HW IRQ lines.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  drivers/pci/host/Kconfig         |   6 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 435 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 7b892a9..c9b61fa 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>> +       select PCI_XGENE_MSI if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index e61d91c..f39bde3 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..4f0ff42
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,407 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_INDEX0             0x000000
>> +#define MSI_INT0               0x800000
>> +
>> +struct xgene_msi_settings {
>> +       u32     index_per_group;
>> +       u32     irqs_per_index;
>> +       u32     nr_msi_vec;
>> +       u32     nr_hw_irqs;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       struct xgene_msi_settings       *settings;
>> +       u32                             msi_addr_lo;
>> +       u32                             msi_addr_hi;
>
> I'd rather see the mailbox address directly, and only do the split when
> assigning it to the message (you seem to play all kind of tricks on the
> address anyway).
>
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       int                             *msi_virqs;
>> +};
>> +
>> +struct xgene_msi_settings storm_msi_settings = {
>> +       .index_per_group        = 8,
>> +       .irqs_per_index         = 21,
>> +       .nr_msi_vec             = 2688,
>> +       .nr_hw_irqs             = 16,
>> +};
>
> It would really help understanding how index, group and hw irq lines are
> structured. nr_msi_vec is obviously the product of these three numbers,
> so maybe you can loose it (we have computers, remember... ;-).
>
> Do you expect more than this single "storm" implementation? If so, maybe
> they should be described in the DT. If not, why do we need a separate
> structure with an indirection if we know these are constants?
>
>> +
>> +typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
>> +
>> +static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
>> +{
>> +       xgene_msi->settings = &storm_msi_settings;
>> +       return 0;
>> +}
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
>> +       u32 irqs_per_index = msi->settings->irqs_per_index;
>> +       u32 reg_set = data->hwirq / (nr_hw_irqs * irqs_per_index);
>> +       u32 group = data->hwirq % nr_hw_irqs;
>> +
>> +       msg->address_hi = msi->msi_addr_hi;
>> +       msg->address_lo = msi->msi_addr_lo +
>> +                         (((8 * group) + reg_set) << 16);
>> +       msg->data = (data->hwirq / nr_hw_irqs) % irqs_per_index;
>
> ... (sound of head exploding...). I hate it when I have to reverse
> engineer the hardware by looking at the code...
>
> Please give us some clue on how interrupts are spread across the various
> pages, how the various indexes are combined to give an actual address.
>
>> +}
>> +
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
>> +       unsigned int gic_irq;
>> +       int ret;
>> +
>> +       gic_irq = msi->msi_virqs[irq_data->hwirq % msi->settings->nr_hw_irqs];
>> +       ret = irq_set_affinity(gic_irq, mask);
>
> Erm... This as the effect of moving *all* the MSIs hanging off this
> interrupt to another CPU. I'm not sure that's an acceptable effect...
> What if another MSI requires a different affinity?
>
>> +       if (ret == IRQ_SET_MASK_OK)
>> +               ret = IRQ_SET_MASK_OK_DONE;
>> +
>> +       return ret;
>> +}
>> +
>> +static struct irq_chip xgene_msi_bottom_irq_chip = {
>> +       .name                   = "MSI",
>> +       .irq_set_affinity       = xgene_msi_set_affinity,
>> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
>> +};
>> +
>> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
>> +                                 unsigned int nr_irqs, void *args)
>> +{
>> +
>> +       struct xgene_msi *msi = domain->host_data;
>> +       u32 msi_irq_count = msi->settings->nr_msi_vec;
>> +       int msi_irq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       msi_irq = find_first_zero_bit(msi->bitmap, msi_irq_count);
>> +       if (msi_irq < msi_irq_count)
>> +               set_bit(msi_irq, msi->bitmap);
>> +       else
>> +               msi_irq = -ENOSPC;
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       if (msi_irq < 0)
>> +               return msi_irq;
>> +
>> +       irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
>> +                           domain->host_data, handle_simple_irq, NULL, NULL);
>> +       set_irq_flags(virq, IRQF_VALID);
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>> +                                 unsigned int virq, unsigned int nr_irqs)
>> +{
>> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       if (!test_bit(d->hwirq, msi->bitmap))
>> +               pr_err("trying to free unused MSI#%lu\n", d->hwirq);
>> +       else
>> +               clear_bit(d->hwirq, msi->bitmap);
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops msi_domain_ops = {
>> +       .alloc  = xgene_irq_domain_alloc,
>> +       .free   = xgene_irq_domain_free,
>> +};
>> +
>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>> +{
>> +       msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
>> +                                           &msi_domain_ops, msi);
>> +
>> +       if (!msi->domain) {
>> +               pr_err("failed to create parent MSI domain\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       msi->mchip.of_node = msi->node;
>> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>> +                                                     &xgene_msi_domain_info,
>> +                                                     msi->domain);
>> +
>> +       if (!msi->mchip.domain) {
>> +               pr_err("failed to create MSI domain\n");
>> +               irq_domain_remove(msi->domain);
>> +               return -ENOMEM;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_free_domains(struct xgene_msi *msi)
>> +{
>> +       if (msi->mchip.domain)
>> +               irq_domain_remove(msi->mchip.domain);
>> +       if (msi->domain)
>> +               irq_domain_remove(msi->domain);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +       u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
>> +       u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
>> +       int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
>> +
>> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +       if (!xgene_msi->bitmap)
>> +               return -ENOMEM;
>> +
>> +       mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +       xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
>> +       if (!xgene_msi->msi_virqs)
>> +               return -ENOMEM;
>> +
>> +       return 0;
>> +}
>> +
>> +static irqreturn_t xgene_msi_isr(int irq, void *data)
>
> This is not a "normal" interrupt handler. This is really a chained
> interrupt controller. Please use the proper infrastructure for this
> (irq_set_chained_handler, chained_irq_enter, chained_irq_exit).
> Otherwise, your usage of irq_find_mapping is illegal wrt RCU.
>
>> +{
>> +       struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
>> +       unsigned int virq;
>> +       int msir_index, msir_reg, msir_val, hw_irq;
>> +       u32 intr_index, grp_select, msi_grp, processed = 0;
>> +       u32 nr_hw_irqs, irqs_per_index, index_per_group;
>> +
>> +       msi_grp = irq - xgene_msi->msi_virqs[0];
>> +       if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
>> +               pr_err("invalid msi received\n");
>> +               return IRQ_NONE;
>> +       }
>> +
>> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +       irqs_per_index = xgene_msi->settings->irqs_per_index;
>> +       index_per_group = xgene_msi->settings->index_per_group;
>> +
>> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +       while (grp_select) {
>> +               msir_index = ffs(grp_select) - 1;
>> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
>> +               msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
>> +               while (msir_val) {
>> +                       intr_index = ffs(msir_val) - 1;
>> +                       hw_irq = (((msir_index * irqs_per_index) + intr_index) *
>> +                                nr_hw_irqs) + msi_grp;
>
> Same thing here. This requires some explaination on how the HW is organised.
>
>> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> +                       if (virq != 0)
>> +                               generic_handle_irq(virq);
>> +                       msir_val &= ~(1 << intr_index);
>> +                       processed++;
>> +               }
>> +               grp_select &= ~(1 << msir_index);
>> +       }
>> +
>> +       return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
>> +}
>> +
>> +static int xgene_msi_remove(struct platform_device *pdev)
>> +{
>> +       int virq, i;
>> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
>> +       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
>> +
>> +       for (i = 0; i < nr_hw_irqs; i++) {
>> +               virq = msi->msi_virqs[i];
>> +               if (virq != 0)
>> +                       free_irq(virq, msi);
>> +       }
>> +
>> +       kfree(msi->bitmap);
>> +       msi->bitmap = NULL;
>> +
>> +       xgene_free_domains(msi);
>> +
>> +       return 0;
>> +}
>> +
>> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
>> +                                struct platform_device *pdev,
>> +                                int irq_index)
>> +{
>> +       int virt_msir;
>> +       cpumask_var_t mask;
>> +       int err;
>> +
>> +       virt_msir = platform_get_irq(pdev, irq_index);
>> +       if (virt_msir < 0) {
>> +               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
>> +                       irq_index);
>> +               return -EINVAL;
>> +       }
>> +
>> +       err = request_irq(virt_msir, xgene_msi_isr, 0,
>> +                         xgene_msi_top_irq_chip.name, msi);
>
> This is where you should use irq_set_chained_handler.
>
>> +       if (err) {
>> +               dev_err(&pdev->dev, "request irq failed\n");
>> +               return err;
>> +       }
>> +
>> +       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
>> +               cpumask_setall(mask);
>> +               irq_set_affinity(virt_msir, mask);
>> +               free_cpumask_var(mask);
>> +       }
>> +
>> +       msi->msi_virqs[irq_index] = virt_msir;
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id xgene_msi_match_table[] = {
>> +       {.compatible = "apm,xgene1-msi",
>> +        .data = xgene_msi_init_storm_settings},
>> +       {},
>> +};
>> +
>> +static int xgene_msi_probe(struct platform_device *pdev)
>> +{
>> +       struct resource *res;
>> +       int rc, irq_index;
>> +       struct device_node *np;
>> +       const struct of_device_id *matched_np;
>> +       struct xgene_msi *xgene_msi;
>> +       xgene_msi_initcall_t init_fn;
>> +       u32 nr_hw_irqs, nr_msi_vecs;
>> +
>> +       np = of_find_matching_node_and_match(NULL,
>> +                            xgene_msi_match_table, &matched_np);
>> +       if (!np)
>> +               return -ENODEV;
>> +
>> +       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
>> +       if (!xgene_msi) {
>> +               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       xgene_msi->node = np;
>> +
>> +       init_fn = (xgene_msi_initcall_t) matched_np->data;
>> +       rc = init_fn(xgene_msi);
>> +       if (rc)
>> +               return rc;
>> +
>> +       platform_set_drvdata(pdev, xgene_msi);
>> +
>> +       nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(xgene_msi->msi_regs)) {
>> +               dev_err(&pdev->dev, "no reg space\n");
>> +               rc = -EINVAL;
>> +               goto error;
>> +       }
>> +
>> +       xgene_msi->msi_addr_hi = upper_32_bits(res->start);
>> +       xgene_msi->msi_addr_lo = lower_32_bits(res->start);
>> +
>> +       rc = xgene_msi_init_allocator(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
>> +               goto error;
>> +       }
>> +
>> +       rc = xgene_allocate_domains(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
>> +               goto error;
>> +       }
>> +
>> +       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
>> +       for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
>> +               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
>> +               if (rc)
>> +                       goto error;
>> +       }
>> +
>> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
>> +               goto error;
>> +       }
>> +
>> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
>> +
>> +       return 0;
>> +error:
>> +       xgene_msi_remove(pdev);
>> +       return rc;
>> +}
>> +
>> +static struct platform_driver xgene_msi_driver = {
>> +       .driver = {
>> +               .name = "xgene-msi",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = xgene_msi_match_table,
>> +       },
>> +       .probe = xgene_msi_probe,
>> +       .remove = xgene_msi_remove,
>> +};
>> +
>> +static int __init xgene_pcie_msi_init(void)
>> +{
>> +       return platform_driver_register(&xgene_msi_driver);
>> +}
>> +subsys_initcall(xgene_pcie_msi_init);
>> +
>> +MODULE_AUTHOR("Duc Dang <dhdang@apm.com>");
>> +MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
>> index 22751ed..3e6faa1 100644
>> --- a/drivers/pci/host/pci-xgene.c
>> +++ b/drivers/pci/host/pci-xgene.c
>> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>>         return 0;
>>  }
>>
>> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
>> +{
>> +       struct device_node *msi_node;
>> +
>> +       msi_node = of_parse_phandle(bus->dev.of_node,
>> +                                       "msi-parent", 0);
>> +       if (!msi_node)
>> +               return -ENODEV;
>> +
>> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
>> +       if (bus->msi)
>> +               bus->msi->dev = &bus->dev;
>> +       else
>> +               return -ENODEV;
>> +       return 0;
>> +}
>> +
>>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>  {
>>         struct device_node *dn = pdev->dev.of_node;
>> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>         if (!bus)
>>                 return -ENOMEM;
>>
>> +       if (IS_ENABLED(CONFIG_PCI_MSI))
>> +               if (xgene_pcie_msi_enable(bus))
>> +                       dev_info(port->dev, "failed to enable MSI\n");
>> +
>>         pci_scan_child_bus(bus);
>>         pci_assign_unassigned_bus_resources(bus);
>>         pci_bus_add_devices(bus);
>> --
>> 1.9.1
>>
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX  termination driver
  2015-04-10 23:42                           ` Duc Dang
@ 2015-04-11 12:06                             ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-11 12:06 UTC (permalink / raw)
  To: Duc Dang
  Cc: Feng Kan, Arnd Bergmann, linux-pci, Liviu Dudau, linux-kernel,
	grant.likely, Tanmay Inamdar, Bjorn Helgaas, linux-arm-kernel,
	Loc Ho

On 2015-04-11 00:42, Duc Dang wrote:
> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier <marc.zyngier@arm.com> 
> wrote:
>> On 09/04/15 18:05, Duc Dang wrote:
>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>> 16 HW IRQ lines.
>>>
>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>> ---
>>>  drivers/pci/host/Kconfig         |   6 +
>>>  drivers/pci/host/Makefile        |   1 +
>>>  drivers/pci/host/pci-xgene-msi.c | 407 
>>> +++++++++++++++++++++++++++++++++++++++
>>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>>  4 files changed, 435 insertions(+)
>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>>
>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>>> index 7b892a9..c9b61fa 100644
>>> --- a/drivers/pci/host/Kconfig
>>> +++ b/drivers/pci/host/Kconfig
>>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>>         depends on ARCH_XGENE
>>>         depends on OF
>>>         select PCIEPORTBUS
>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>> +       select PCI_XGENE_MSI if PCI_MSI
>>>         help
>>>           Say Y here if you want internal PCI support on APM X-Gene 
>>> SoC.
>>>           There are 5 internal PCIe ports available. Each port is 
>>> GEN3 capable
>>>           and have varied lanes from x1 to x8.
>>>
>>> +config PCI_XGENE_MSI
>>> +       bool "X-Gene v1 PCIe MSI feature"
>>> +       depends on PCI_XGENE && PCI_MSI
>>> +
>>>  config PCI_LAYERSCAPE
>>>         bool "Freescale Layerscape PCIe controller"
>>>         depends on OF && ARM
>>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>>> index e61d91c..f39bde3 100644
>>> --- a/drivers/pci/host/Makefile
>>> +++ b/drivers/pci/host/Makefile
>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>> diff --git a/drivers/pci/host/pci-xgene-msi.c 
>>> b/drivers/pci/host/pci-xgene-msi.c
>>> new file mode 100644
>>> index 0000000..4f0ff42
>>> --- /dev/null
>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>> @@ -0,0 +1,407 @@
>>> +/*
>>> + * APM X-Gene MSI Driver
>>> + *
>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>>> + *        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 as 
>>> published by the
>>> + * Free Software Foundation;  either version 2 of the  License, or 
>>> (at your
>>> + * option) any later version.
>>> + *
>>> + * 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.
>>> + */
>>> +#include <linux/interrupt.h>
>>> +#include <linux/module.h>
>>> +#include <linux/msi.h>
>>> +#include <linux/of_irq.h>
>>> +#include <linux/pci.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/of_pci.h>
>>> +
>>> +#define MSI_INDEX0             0x000000
>>> +#define MSI_INT0               0x800000
>>> +
>>> +struct xgene_msi_settings {
>>> +       u32     index_per_group;
>>> +       u32     irqs_per_index;
>>> +       u32     nr_msi_vec;
>>> +       u32     nr_hw_irqs;
>>> +};
>>> +
>>> +struct xgene_msi {
>>> +       struct device_node              *node;
>>> +       struct msi_controller           mchip;
>>> +       struct irq_domain               *domain;
>>> +       struct xgene_msi_settings       *settings;
>>> +       u32                             msi_addr_lo;
>>> +       u32                             msi_addr_hi;
>>
>> I'd rather see the mailbox address directly, and only do the split 
>> when
>> assigning it to the message (you seem to play all kind of tricks on 
>> the
>> address anyway).
>
> msi_addr_lo and msi_addr_hi store the physical base address of MSI
> controller registers. I will add comment to clarify this.

What I mean is that there is no point in keeping this around as a pair
of 32bit variables. You'd better keep it as a single 64bit, and do the
split when assigning it the the MSI message.

[...]

>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>>> +                                 const struct cpumask *mask, bool 
>>> force)
>>> +{
>>> +       struct xgene_msi *msi = 
>>> irq_data_get_irq_chip_data(irq_data);
>>> +       unsigned int gic_irq;
>>> +       int ret;
>>> +
>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq % 
>>> msi->settings->nr_hw_irqs];
>>> +       ret = irq_set_affinity(gic_irq, mask);
>>
>> Erm... This as the effect of moving *all* the MSIs hanging off this
>> interrupt to another CPU. I'm not sure that's an acceptable 
>> effect...
>> What if another MSI requires a different affinity?
>
> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
> attached to it.
> So this will move all MSIs handing off this interrupt to another CPU;
> and we don't support different affinity settings for different MSIs
> that are attached to the same hardware IRQ.

Well, that's a significant departure from the expected behaviour. In 
other
words, I wonder how useful this is. Could you instead reconfigure the 
MSI
itself to hit the right CPU (assuming you don't have more than 16 CPUs 
and if
that's only for XGene-1, this will only be 8 at most)? This would 
reduce
your number of possible MSIs, but at least the semantics of set_afinity
would be preserved.

Thanks,

         M.
-- 
Fast, cheap, reliable. Pick two.

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-11 12:06                             ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-11 12:06 UTC (permalink / raw)
  To: linux-arm-kernel

On 2015-04-11 00:42, Duc Dang wrote:
> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier <marc.zyngier@arm.com> 
> wrote:
>> On 09/04/15 18:05, Duc Dang wrote:
>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>> 16 HW IRQ lines.
>>>
>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>> ---
>>>  drivers/pci/host/Kconfig         |   6 +
>>>  drivers/pci/host/Makefile        |   1 +
>>>  drivers/pci/host/pci-xgene-msi.c | 407 
>>> +++++++++++++++++++++++++++++++++++++++
>>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>>  4 files changed, 435 insertions(+)
>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>>
>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>>> index 7b892a9..c9b61fa 100644
>>> --- a/drivers/pci/host/Kconfig
>>> +++ b/drivers/pci/host/Kconfig
>>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>>         depends on ARCH_XGENE
>>>         depends on OF
>>>         select PCIEPORTBUS
>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>> +       select PCI_XGENE_MSI if PCI_MSI
>>>         help
>>>           Say Y here if you want internal PCI support on APM X-Gene 
>>> SoC.
>>>           There are 5 internal PCIe ports available. Each port is 
>>> GEN3 capable
>>>           and have varied lanes from x1 to x8.
>>>
>>> +config PCI_XGENE_MSI
>>> +       bool "X-Gene v1 PCIe MSI feature"
>>> +       depends on PCI_XGENE && PCI_MSI
>>> +
>>>  config PCI_LAYERSCAPE
>>>         bool "Freescale Layerscape PCIe controller"
>>>         depends on OF && ARM
>>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>>> index e61d91c..f39bde3 100644
>>> --- a/drivers/pci/host/Makefile
>>> +++ b/drivers/pci/host/Makefile
>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>> diff --git a/drivers/pci/host/pci-xgene-msi.c 
>>> b/drivers/pci/host/pci-xgene-msi.c
>>> new file mode 100644
>>> index 0000000..4f0ff42
>>> --- /dev/null
>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>> @@ -0,0 +1,407 @@
>>> +/*
>>> + * APM X-Gene MSI Driver
>>> + *
>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>>> + *        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 as 
>>> published by the
>>> + * Free Software Foundation;  either version 2 of the  License, or 
>>> (at your
>>> + * option) any later version.
>>> + *
>>> + * 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.
>>> + */
>>> +#include <linux/interrupt.h>
>>> +#include <linux/module.h>
>>> +#include <linux/msi.h>
>>> +#include <linux/of_irq.h>
>>> +#include <linux/pci.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/of_pci.h>
>>> +
>>> +#define MSI_INDEX0             0x000000
>>> +#define MSI_INT0               0x800000
>>> +
>>> +struct xgene_msi_settings {
>>> +       u32     index_per_group;
>>> +       u32     irqs_per_index;
>>> +       u32     nr_msi_vec;
>>> +       u32     nr_hw_irqs;
>>> +};
>>> +
>>> +struct xgene_msi {
>>> +       struct device_node              *node;
>>> +       struct msi_controller           mchip;
>>> +       struct irq_domain               *domain;
>>> +       struct xgene_msi_settings       *settings;
>>> +       u32                             msi_addr_lo;
>>> +       u32                             msi_addr_hi;
>>
>> I'd rather see the mailbox address directly, and only do the split 
>> when
>> assigning it to the message (you seem to play all kind of tricks on 
>> the
>> address anyway).
>
> msi_addr_lo and msi_addr_hi store the physical base address of MSI
> controller registers. I will add comment to clarify this.

What I mean is that there is no point in keeping this around as a pair
of 32bit variables. You'd better keep it as a single 64bit, and do the
split when assigning it the the MSI message.

[...]

>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>>> +                                 const struct cpumask *mask, bool 
>>> force)
>>> +{
>>> +       struct xgene_msi *msi = 
>>> irq_data_get_irq_chip_data(irq_data);
>>> +       unsigned int gic_irq;
>>> +       int ret;
>>> +
>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq % 
>>> msi->settings->nr_hw_irqs];
>>> +       ret = irq_set_affinity(gic_irq, mask);
>>
>> Erm... This as the effect of moving *all* the MSIs hanging off this
>> interrupt to another CPU. I'm not sure that's an acceptable 
>> effect...
>> What if another MSI requires a different affinity?
>
> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
> attached to it.
> So this will move all MSIs handing off this interrupt to another CPU;
> and we don't support different affinity settings for different MSIs
> that are attached to the same hardware IRQ.

Well, that's a significant departure from the expected behaviour. In 
other
words, I wonder how useful this is. Could you instead reconfigure the 
MSI
itself to hit the right CPU (assuming you don't have more than 16 CPUs 
and if
that's only for XGene-1, this will only be 8 at most)? This would 
reduce
your number of possible MSIs, but at least the semantics of set_afinity
would be preserved.

Thanks,

         M.
-- 
Fast, cheap, reliable. Pick two.

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX  termination driver
  2015-04-11  0:16                           ` Feng Kan
@ 2015-04-11 12:18                             ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-11 12:18 UTC (permalink / raw)
  To: Feng Kan
  Cc: Rafael J. Wysocki, Arnd Bergmann, linux-pci, Duc Dang,
	Liviu Dudau, linux-kernel, grant.likely, Tanmay Inamdar,
	Bjorn Helgaas, linux-arm-kernel, Loc Ho

Hi Feng,

On 2015-04-11 01:16, Feng Kan wrote:

> Is there any plans to support ACPI for GICv2m MSI? Both this driver 
> and the
> GICv2m seems to support OF model of discovery for msi controller. 
> X-Gene1
> uses this driver and X-Gene2 uses GICv2m, there needs to be a way to
> associate msi controller with the PCIe bus. I haven't
> found a standard way of doing finding "msi-parent" for ACPI. Do you 
> have
> any suggestion.

This is remarkably off topic! ;-)

ACPI only provides information for GICv2m and GICv3. No other interrupt 
controller,
MSI widget or fairy dust provider will be supported on arm64 with ACPI. 
So this driver
will be DT only.

GICv2m, being described in the ACPI spec, is growing some support. My 
understanding is
that this is done by AMD (Suravee Suthikulpanit) using my 
irq/msi-domain branch as a
base (I know that Suravee is looking at expanding the irqdomain 
matching code to
accept a pointer to an ACPI table instead of a device node to match the 
target
domain).

Hope this helps,

         M.
-- 
Fast, cheap, reliable. Pick two.

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-11 12:18                             ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-11 12:18 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Feng,

On 2015-04-11 01:16, Feng Kan wrote:

> Is there any plans to support ACPI for GICv2m MSI? Both this driver 
> and the
> GICv2m seems to support OF model of discovery for msi controller. 
> X-Gene1
> uses this driver and X-Gene2 uses GICv2m, there needs to be a way to
> associate msi controller with the PCIe bus. I haven't
> found a standard way of doing finding "msi-parent" for ACPI. Do you 
> have
> any suggestion.

This is remarkably off topic! ;-)

ACPI only provides information for GICv2m and GICv3. No other interrupt 
controller,
MSI widget or fairy dust provider will be supported on arm64 with ACPI. 
So this driver
will be DT only.

GICv2m, being described in the ACPI spec, is growing some support. My 
understanding is
that this is done by AMD (Suravee Suthikulpanit) using my 
irq/msi-domain branch as a
base (I know that Suravee is looking at expanding the irqdomain 
matching code to
accept a pointer to an ACPI table instead of a device node to match the 
target
domain).

Hope this helps,

         M.
-- 
Fast, cheap, reliable. Pick two.

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-11  0:16                           ` Feng Kan
  (?)
@ 2015-04-11 14:50                             ` Arnd Bergmann
  -1 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-04-11 14:50 UTC (permalink / raw)
  To: Feng Kan
  Cc: Marc Zyngier, Rafael J. Wysocki, Duc Dang, Bjorn Helgaas,
	grant.likely, Liviu Dudau, linux-pci, linux-arm-kernel,
	linux-kernel, Tanmay Inamdar, Loc Ho

On Friday 10 April 2015 17:16:32 Feng Kan wrote:
> Hi Marc:
> 
> Is there any plans to support ACPI for GICv2m MSI? Both this driver and the
> GICv2m seems to support OF model of discovery for msi controller. X-Gene1
> uses this driver and X-Gene2 uses GICv2m, there needs to be a way to
> associate msi controller with the PCIe bus. I haven't
> found a standard way of doing finding "msi-parent" for ACPI. Do you have
> any suggestion.
> 
> Sorry for top posting.

Doing gicv2m in ACPI should be straightforward, and the ACPI code can simply
assume that there is only one GIC instance (whichever version) in the system
and use that for all standard PCI hosts.

	Arnd

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-11 14:50                             ` Arnd Bergmann
  0 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-04-11 14:50 UTC (permalink / raw)
  To: Feng Kan
  Cc: Marc Zyngier, Rafael J. Wysocki, Duc Dang, Bjorn Helgaas,
	grant.likely, Liviu Dudau, linux-pci, linux-arm-kernel,
	linux-kernel, Tanmay Inamdar, Loc Ho

On Friday 10 April 2015 17:16:32 Feng Kan wrote:
> Hi Marc:
> 
> Is there any plans to support ACPI for GICv2m MSI? Both this driver and the
> GICv2m seems to support OF model of discovery for msi controller. X-Gene1
> uses this driver and X-Gene2 uses GICv2m, there needs to be a way to
> associate msi controller with the PCIe bus. I haven't
> found a standard way of doing finding "msi-parent" for ACPI. Do you have
> any suggestion.
> 
> Sorry for top posting.

Doing gicv2m in ACPI should be straightforward, and the ACPI code can simply
assume that there is only one GIC instance (whichever version) in the system
and use that for all standard PCI hosts.

	Arnd

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-11 14:50                             ` Arnd Bergmann
  0 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-04-11 14:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Friday 10 April 2015 17:16:32 Feng Kan wrote:
> Hi Marc:
> 
> Is there any plans to support ACPI for GICv2m MSI? Both this driver and the
> GICv2m seems to support OF model of discovery for msi controller. X-Gene1
> uses this driver and X-Gene2 uses GICv2m, there needs to be a way to
> associate msi controller with the PCIe bus. I haven't
> found a standard way of doing finding "msi-parent" for ACPI. Do you have
> any suggestion.
> 
> Sorry for top posting.

Doing gicv2m in ACPI should be straightforward, and the ACPI code can simply
assume that there is only one GIC instance (whichever version) in the system
and use that for all standard PCI hosts.

	Arnd

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-11 12:06                             ` Marc Zyngier
@ 2015-04-14 18:20                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-14 18:20 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Feng Kan, Arnd Bergmann, linux-pci, Liviu Dudau, linux-kernel,
	Grant Likely, Tanmay Inamdar, Bjorn Helgaas, linux-arm, Loc Ho

On Sat, Apr 11, 2015 at 5:06 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 2015-04-11 00:42, Duc Dang wrote:
>>
>> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier <marc.zyngier@arm.com>
>> wrote:
>>>
>>> On 09/04/15 18:05, Duc Dang wrote:
>>>>
>>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>>> 16 HW IRQ lines.
>>>>
>>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>>> ---
>>>>  drivers/pci/host/Kconfig         |   6 +
>>>>  drivers/pci/host/Makefile        |   1 +
>>>>  drivers/pci/host/pci-xgene-msi.c | 407
>>>> +++++++++++++++++++++++++++++++++++++++
>>>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>>>  4 files changed, 435 insertions(+)
>>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>>>
>>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>>>> index 7b892a9..c9b61fa 100644
>>>> --- a/drivers/pci/host/Kconfig
>>>> +++ b/drivers/pci/host/Kconfig
>>>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>>>         depends on ARCH_XGENE
>>>>         depends on OF
>>>>         select PCIEPORTBUS
>>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>>> +       select PCI_XGENE_MSI if PCI_MSI
>>>>         help
>>>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>>>           There are 5 internal PCIe ports available. Each port is GEN3
>>>> capable
>>>>           and have varied lanes from x1 to x8.
>>>>
>>>> +config PCI_XGENE_MSI
>>>> +       bool "X-Gene v1 PCIe MSI feature"
>>>> +       depends on PCI_XGENE && PCI_MSI
>>>> +
>>>>  config PCI_LAYERSCAPE
>>>>         bool "Freescale Layerscape PCIe controller"
>>>>         depends on OF && ARM
>>>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>>>> index e61d91c..f39bde3 100644
>>>> --- a/drivers/pci/host/Makefile
>>>> +++ b/drivers/pci/host/Makefile
>>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>>> diff --git a/drivers/pci/host/pci-xgene-msi.c
>>>> b/drivers/pci/host/pci-xgene-msi.c
>>>> new file mode 100644
>>>> index 0000000..4f0ff42
>>>> --- /dev/null
>>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>>> @@ -0,0 +1,407 @@
>>>> +/*
>>>> + * APM X-Gene MSI Driver
>>>> + *
>>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>>>> + *        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 as published by
>>>> the
>>>> + * Free Software Foundation;  either version 2 of the  License, or (at
>>>> your
>>>> + * option) any later version.
>>>> + *
>>>> + * 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.
>>>> + */
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/msi.h>
>>>> +#include <linux/of_irq.h>
>>>> +#include <linux/pci.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/of_pci.h>
>>>> +
>>>> +#define MSI_INDEX0             0x000000
>>>> +#define MSI_INT0               0x800000
>>>> +
>>>> +struct xgene_msi_settings {
>>>> +       u32     index_per_group;
>>>> +       u32     irqs_per_index;
>>>> +       u32     nr_msi_vec;
>>>> +       u32     nr_hw_irqs;
>>>> +};
>>>> +
>>>> +struct xgene_msi {
>>>> +       struct device_node              *node;
>>>> +       struct msi_controller           mchip;
>>>> +       struct irq_domain               *domain;
>>>> +       struct xgene_msi_settings       *settings;
>>>> +       u32                             msi_addr_lo;
>>>> +       u32                             msi_addr_hi;
>>>
>>>
>>> I'd rather see the mailbox address directly, and only do the split when
>>> assigning it to the message (you seem to play all kind of tricks on the
>>> address anyway).
>>
>>
>> msi_addr_lo and msi_addr_hi store the physical base address of MSI
>> controller registers. I will add comment to clarify this.
>
>
> What I mean is that there is no point in keeping this around as a pair
> of 32bit variables. You'd better keep it as a single 64bit, and do the
> split when assigning it the the MSI message.

Hi Marc,

These came from device-tree (which describes 64-bit address number as
2 32-bit words).
If I store them this way, I don't need CPU cycles to do the split
every time assigning them to the MSI message. Please let me know what
do you think about it.

>
> [...]
>
>>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>>>> +                                 const struct cpumask *mask, bool
>>>> force)
>>>> +{
>>>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
>>>> +       unsigned int gic_irq;
>>>> +       int ret;
>>>> +
>>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq %
>>>> msi->settings->nr_hw_irqs];
>>>> +       ret = irq_set_affinity(gic_irq, mask);
>>>
>>>
>>> Erm... This as the effect of moving *all* the MSIs hanging off this
>>> interrupt to another CPU. I'm not sure that's an acceptable effect...
>>> What if another MSI requires a different affinity?
>>
>>
>> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
>> attached to it.
>> So this will move all MSIs handing off this interrupt to another CPU;
>> and we don't support different affinity settings for different MSIs
>> that are attached to the same hardware IRQ.
>
>
> Well, that's a significant departure from the expected behaviour. In other
> words, I wonder how useful this is. Could you instead reconfigure the MSI
> itself to hit the right CPU (assuming you don't have more than 16 CPUs and
> if
> that's only for XGene-1, this will only be 8 at most)? This would reduce
> your number of possible MSIs, but at least the semantics of set_afinity
> would be preserved.

X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each
group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have
16 hardware GIC IRQs for 2688 MSIs).

Setting affinity of single MSI to deliver it to a target CPU will move
all the MSIs mapped to the same GIC IRQ to that CPU as well. This is
not a standard behavior, but limiting the total number of MSIs will
cause a lot of devices to fall back to INTx (with huge performance
penalty) or even fail to load their driver as these devices request
more than 16 MSIs during driver initialization.

I can document the limitation in affinity setting of X-Gene-1 MSI in
the driver to hopefully not make people surprise and hope to keep the
total number of supported MSI as 2688 so that we can support as many
cards that require MSI/MSI-X as possible.

Please let me know your opinion.

>
> Thanks,
>
>         M.
> --
> Fast, cheap, reliable. Pick two.

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-14 18:20                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-14 18:20 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Apr 11, 2015 at 5:06 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 2015-04-11 00:42, Duc Dang wrote:
>>
>> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier <marc.zyngier@arm.com>
>> wrote:
>>>
>>> On 09/04/15 18:05, Duc Dang wrote:
>>>>
>>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>>> 16 HW IRQ lines.
>>>>
>>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>>> ---
>>>>  drivers/pci/host/Kconfig         |   6 +
>>>>  drivers/pci/host/Makefile        |   1 +
>>>>  drivers/pci/host/pci-xgene-msi.c | 407
>>>> +++++++++++++++++++++++++++++++++++++++
>>>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>>>  4 files changed, 435 insertions(+)
>>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>>>
>>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>>>> index 7b892a9..c9b61fa 100644
>>>> --- a/drivers/pci/host/Kconfig
>>>> +++ b/drivers/pci/host/Kconfig
>>>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>>>         depends on ARCH_XGENE
>>>>         depends on OF
>>>>         select PCIEPORTBUS
>>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>>> +       select PCI_XGENE_MSI if PCI_MSI
>>>>         help
>>>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>>>           There are 5 internal PCIe ports available. Each port is GEN3
>>>> capable
>>>>           and have varied lanes from x1 to x8.
>>>>
>>>> +config PCI_XGENE_MSI
>>>> +       bool "X-Gene v1 PCIe MSI feature"
>>>> +       depends on PCI_XGENE && PCI_MSI
>>>> +
>>>>  config PCI_LAYERSCAPE
>>>>         bool "Freescale Layerscape PCIe controller"
>>>>         depends on OF && ARM
>>>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>>>> index e61d91c..f39bde3 100644
>>>> --- a/drivers/pci/host/Makefile
>>>> +++ b/drivers/pci/host/Makefile
>>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>>> diff --git a/drivers/pci/host/pci-xgene-msi.c
>>>> b/drivers/pci/host/pci-xgene-msi.c
>>>> new file mode 100644
>>>> index 0000000..4f0ff42
>>>> --- /dev/null
>>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>>> @@ -0,0 +1,407 @@
>>>> +/*
>>>> + * APM X-Gene MSI Driver
>>>> + *
>>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>>>> + *        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 as published by
>>>> the
>>>> + * Free Software Foundation;  either version 2 of the  License, or (at
>>>> your
>>>> + * option) any later version.
>>>> + *
>>>> + * 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.
>>>> + */
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/msi.h>
>>>> +#include <linux/of_irq.h>
>>>> +#include <linux/pci.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/of_pci.h>
>>>> +
>>>> +#define MSI_INDEX0             0x000000
>>>> +#define MSI_INT0               0x800000
>>>> +
>>>> +struct xgene_msi_settings {
>>>> +       u32     index_per_group;
>>>> +       u32     irqs_per_index;
>>>> +       u32     nr_msi_vec;
>>>> +       u32     nr_hw_irqs;
>>>> +};
>>>> +
>>>> +struct xgene_msi {
>>>> +       struct device_node              *node;
>>>> +       struct msi_controller           mchip;
>>>> +       struct irq_domain               *domain;
>>>> +       struct xgene_msi_settings       *settings;
>>>> +       u32                             msi_addr_lo;
>>>> +       u32                             msi_addr_hi;
>>>
>>>
>>> I'd rather see the mailbox address directly, and only do the split when
>>> assigning it to the message (you seem to play all kind of tricks on the
>>> address anyway).
>>
>>
>> msi_addr_lo and msi_addr_hi store the physical base address of MSI
>> controller registers. I will add comment to clarify this.
>
>
> What I mean is that there is no point in keeping this around as a pair
> of 32bit variables. You'd better keep it as a single 64bit, and do the
> split when assigning it the the MSI message.

Hi Marc,

These came from device-tree (which describes 64-bit address number as
2 32-bit words).
If I store them this way, I don't need CPU cycles to do the split
every time assigning them to the MSI message. Please let me know what
do you think about it.

>
> [...]
>
>>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>>>> +                                 const struct cpumask *mask, bool
>>>> force)
>>>> +{
>>>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
>>>> +       unsigned int gic_irq;
>>>> +       int ret;
>>>> +
>>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq %
>>>> msi->settings->nr_hw_irqs];
>>>> +       ret = irq_set_affinity(gic_irq, mask);
>>>
>>>
>>> Erm... This as the effect of moving *all* the MSIs hanging off this
>>> interrupt to another CPU. I'm not sure that's an acceptable effect...
>>> What if another MSI requires a different affinity?
>>
>>
>> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
>> attached to it.
>> So this will move all MSIs handing off this interrupt to another CPU;
>> and we don't support different affinity settings for different MSIs
>> that are attached to the same hardware IRQ.
>
>
> Well, that's a significant departure from the expected behaviour. In other
> words, I wonder how useful this is. Could you instead reconfigure the MSI
> itself to hit the right CPU (assuming you don't have more than 16 CPUs and
> if
> that's only for XGene-1, this will only be 8 at most)? This would reduce
> your number of possible MSIs, but at least the semantics of set_afinity
> would be preserved.

X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each
group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have
16 hardware GIC IRQs for 2688 MSIs).

Setting affinity of single MSI to deliver it to a target CPU will move
all the MSIs mapped to the same GIC IRQ to that CPU as well. This is
not a standard behavior, but limiting the total number of MSIs will
cause a lot of devices to fall back to INTx (with huge performance
penalty) or even fail to load their driver as these devices request
more than 16 MSIs during driver initialization.

I can document the limitation in affinity setting of X-Gene-1 MSI in
the driver to hopefully not make people surprise and hope to keep the
total number of supported MSI as 2688 so that we can support as many
cards that require MSI/MSI-X as possible.

Please let me know your opinion.

>
> Thanks,
>
>         M.
> --
> Fast, cheap, reliable. Pick two.

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-14 18:20                               ` Duc Dang
@ 2015-04-15  8:16                                 ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-15  8:16 UTC (permalink / raw)
  To: Duc Dang
  Cc: Feng Kan, Arnd Bergmann, linux-pci, Liviu Dudau, linux-kernel,
	grant.likely, Tanmay Inamdar, Bjorn Helgaas, linux-arm, Loc Ho

On Tue, 14 Apr 2015 19:20:19 +0100
Duc Dang <dhdang@apm.com> wrote:

> On Sat, Apr 11, 2015 at 5:06 AM, Marc Zyngier <marc.zyngier@arm.com>
> wrote:
> > On 2015-04-11 00:42, Duc Dang wrote:
> >>
> >> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier
> >> <marc.zyngier@arm.com> wrote:
> >>>
> >>> On 09/04/15 18:05, Duc Dang wrote:
> >>>>
> >>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
> >>>> 16 HW IRQ lines.
> >>>>
> >>>> Signed-off-by: Duc Dang <dhdang@apm.com>
> >>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> >>>> ---
> >>>>  drivers/pci/host/Kconfig         |   6 +
> >>>>  drivers/pci/host/Makefile        |   1 +
> >>>>  drivers/pci/host/pci-xgene-msi.c | 407
> >>>> +++++++++++++++++++++++++++++++++++++++
> >>>>  drivers/pci/host/pci-xgene.c     |  21 ++
> >>>>  4 files changed, 435 insertions(+)
> >>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> >>>>
> >>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> >>>> index 7b892a9..c9b61fa 100644
> >>>> --- a/drivers/pci/host/Kconfig
> >>>> +++ b/drivers/pci/host/Kconfig
> >>>> @@ -89,11 +89,17 @@ config PCI_XGENE
> >>>>         depends on ARCH_XGENE
> >>>>         depends on OF
> >>>>         select PCIEPORTBUS
> >>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> >>>> +       select PCI_XGENE_MSI if PCI_MSI
> >>>>         help
> >>>>           Say Y here if you want internal PCI support on APM
> >>>> X-Gene SoC. There are 5 internal PCIe ports available. Each port
> >>>> is GEN3 capable
> >>>>           and have varied lanes from x1 to x8.
> >>>>
> >>>> +config PCI_XGENE_MSI
> >>>> +       bool "X-Gene v1 PCIe MSI feature"
> >>>> +       depends on PCI_XGENE && PCI_MSI
> >>>> +
> >>>>  config PCI_LAYERSCAPE
> >>>>         bool "Freescale Layerscape PCIe controller"
> >>>>         depends on OF && ARM
> >>>> diff --git a/drivers/pci/host/Makefile
> >>>> b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644
> >>>> --- a/drivers/pci/host/Makefile
> >>>> +++ b/drivers/pci/host/Makefile
> >>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> >>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
> >>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
> >>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> >>>> diff --git a/drivers/pci/host/pci-xgene-msi.c
> >>>> b/drivers/pci/host/pci-xgene-msi.c
> >>>> new file mode 100644
> >>>> index 0000000..4f0ff42
> >>>> --- /dev/null
> >>>> +++ b/drivers/pci/host/pci-xgene-msi.c
> >>>> @@ -0,0 +1,407 @@
> >>>> +/*
> >>>> + * APM X-Gene MSI Driver
> >>>> + *
> >>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> >>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> >>>> + *        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 as
> >>>> published by the
> >>>> + * Free Software Foundation;  either version 2 of the  License,
> >>>> or (at your
> >>>> + * option) any later version.
> >>>> + *
> >>>> + * 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.
> >>>> + */
> >>>> +#include <linux/interrupt.h>
> >>>> +#include <linux/module.h>
> >>>> +#include <linux/msi.h>
> >>>> +#include <linux/of_irq.h>
> >>>> +#include <linux/pci.h>
> >>>> +#include <linux/platform_device.h>
> >>>> +#include <linux/of_pci.h>
> >>>> +
> >>>> +#define MSI_INDEX0             0x000000
> >>>> +#define MSI_INT0               0x800000
> >>>> +
> >>>> +struct xgene_msi_settings {
> >>>> +       u32     index_per_group;
> >>>> +       u32     irqs_per_index;
> >>>> +       u32     nr_msi_vec;
> >>>> +       u32     nr_hw_irqs;
> >>>> +};
> >>>> +
> >>>> +struct xgene_msi {
> >>>> +       struct device_node              *node;
> >>>> +       struct msi_controller           mchip;
> >>>> +       struct irq_domain               *domain;
> >>>> +       struct xgene_msi_settings       *settings;
> >>>> +       u32                             msi_addr_lo;
> >>>> +       u32                             msi_addr_hi;
> >>>
> >>>
> >>> I'd rather see the mailbox address directly, and only do the
> >>> split when assigning it to the message (you seem to play all kind
> >>> of tricks on the address anyway).
> >>
> >>
> >> msi_addr_lo and msi_addr_hi store the physical base address of MSI
> >> controller registers. I will add comment to clarify this.
> >
> >
> > What I mean is that there is no point in keeping this around as a
> > pair of 32bit variables. You'd better keep it as a single 64bit,
> > and do the split when assigning it the the MSI message.
> 
> Hi Marc,
> 
> These came from device-tree (which describes 64-bit address number as
> 2 32-bit words).

... and converted to a resource as a 64bit word, on which you apply
{upper,lower}_32_bit(). So much for DT...

> If I store them this way, I don't need CPU cycles to do the split
> every time assigning them to the MSI message. Please let me know what
> do you think about it.

This is getting absolutely silly.

How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If
it takes so long that it is considered to be a bottleneck, I suggest
you go and design a better CPU (hint: the answer is probably 1 cycle
absolutely everywhere).

How often are you configuring MSIs in the face of what is happening in
the rest of the kernel? Almost never!

So, given that "never" times 1 is still never,  I'll consider that
readability of the code trumps it anytime (I can't believe we're having
that kind of conversation...).

> >
> > [...]
> >
> >>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> >>>> +                                 const struct cpumask *mask,
> >>>> bool force)
> >>>> +{
> >>>> +       struct xgene_msi *msi =
> >>>> irq_data_get_irq_chip_data(irq_data);
> >>>> +       unsigned int gic_irq;
> >>>> +       int ret;
> >>>> +
> >>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq %
> >>>> msi->settings->nr_hw_irqs];
> >>>> +       ret = irq_set_affinity(gic_irq, mask);
> >>>
> >>>
> >>> Erm... This as the effect of moving *all* the MSIs hanging off
> >>> this interrupt to another CPU. I'm not sure that's an acceptable
> >>> effect... What if another MSI requires a different affinity?
> >>
> >>
> >> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
> >> attached to it.
> >> So this will move all MSIs handing off this interrupt to another
> >> CPU; and we don't support different affinity settings for
> >> different MSIs that are attached to the same hardware IRQ.
> >
> >
> > Well, that's a significant departure from the expected behaviour.
> > In other words, I wonder how useful this is. Could you instead
> > reconfigure the MSI itself to hit the right CPU (assuming you don't
> > have more than 16 CPUs and if
> > that's only for XGene-1, this will only be 8 at most)? This would
> > reduce your number of possible MSIs, but at least the semantics of
> > set_afinity would be preserved.
> 
> X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each
> group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have
> 16 hardware GIC IRQs for 2688 MSIs).

We've already established that.

> Setting affinity of single MSI to deliver it to a target CPU will move
> all the MSIs mapped to the same GIC IRQ to that CPU as well. This is
> not a standard behavior, but limiting the total number of MSIs will
> cause a lot of devices to fall back to INTx (with huge performance
> penalty) or even fail to load their driver as these devices request
> more than 16 MSIs during driver initialization.

No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and
provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per
CPU (having statically assigned 2 IRQs per CPU).

Assuming you adopt my scheme, you still have a grand total of 336 MSIs
that can be freely moved around without breaking any userspace
expectations.

I think that 336 MSIs is a fair number (nowhere near the 16 you claim).
Most platforms are doing quite well with that kind of numbers. Also,
you don't have to allocate all the MSIs a device can possibly claim (up
to 2048 MSI-X per device), as they are all perfectly capable of using
less MSI without having to fallback to INTx).

> I can document the limitation in affinity setting of X-Gene-1 MSI in
> the driver to hopefully not make people surprise and hope to keep the
> total number of supported MSI as 2688 so that we can support as many
> cards that require MSI/MSI-X as possible.

I don't think this is a valid approach. This breaks userspace (think of
things like irqbalance), and breaks the SMP affinity model that Linux
uses. No amount of documentation is going to solve it, so I think you
just have to admit that the HW is mis-designed and do the best you can
to make it work like Linux expect it to work.

The alternative would to disable affinity setting altogether instead of
introducing these horrible side effects.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny.

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-15  8:16                                 ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-15  8:16 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 14 Apr 2015 19:20:19 +0100
Duc Dang <dhdang@apm.com> wrote:

> On Sat, Apr 11, 2015 at 5:06 AM, Marc Zyngier <marc.zyngier@arm.com>
> wrote:
> > On 2015-04-11 00:42, Duc Dang wrote:
> >>
> >> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier
> >> <marc.zyngier@arm.com> wrote:
> >>>
> >>> On 09/04/15 18:05, Duc Dang wrote:
> >>>>
> >>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
> >>>> 16 HW IRQ lines.
> >>>>
> >>>> Signed-off-by: Duc Dang <dhdang@apm.com>
> >>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> >>>> ---
> >>>>  drivers/pci/host/Kconfig         |   6 +
> >>>>  drivers/pci/host/Makefile        |   1 +
> >>>>  drivers/pci/host/pci-xgene-msi.c | 407
> >>>> +++++++++++++++++++++++++++++++++++++++
> >>>>  drivers/pci/host/pci-xgene.c     |  21 ++
> >>>>  4 files changed, 435 insertions(+)
> >>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> >>>>
> >>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> >>>> index 7b892a9..c9b61fa 100644
> >>>> --- a/drivers/pci/host/Kconfig
> >>>> +++ b/drivers/pci/host/Kconfig
> >>>> @@ -89,11 +89,17 @@ config PCI_XGENE
> >>>>         depends on ARCH_XGENE
> >>>>         depends on OF
> >>>>         select PCIEPORTBUS
> >>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> >>>> +       select PCI_XGENE_MSI if PCI_MSI
> >>>>         help
> >>>>           Say Y here if you want internal PCI support on APM
> >>>> X-Gene SoC. There are 5 internal PCIe ports available. Each port
> >>>> is GEN3 capable
> >>>>           and have varied lanes from x1 to x8.
> >>>>
> >>>> +config PCI_XGENE_MSI
> >>>> +       bool "X-Gene v1 PCIe MSI feature"
> >>>> +       depends on PCI_XGENE && PCI_MSI
> >>>> +
> >>>>  config PCI_LAYERSCAPE
> >>>>         bool "Freescale Layerscape PCIe controller"
> >>>>         depends on OF && ARM
> >>>> diff --git a/drivers/pci/host/Makefile
> >>>> b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644
> >>>> --- a/drivers/pci/host/Makefile
> >>>> +++ b/drivers/pci/host/Makefile
> >>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> >>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
> >>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
> >>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> >>>> diff --git a/drivers/pci/host/pci-xgene-msi.c
> >>>> b/drivers/pci/host/pci-xgene-msi.c
> >>>> new file mode 100644
> >>>> index 0000000..4f0ff42
> >>>> --- /dev/null
> >>>> +++ b/drivers/pci/host/pci-xgene-msi.c
> >>>> @@ -0,0 +1,407 @@
> >>>> +/*
> >>>> + * APM X-Gene MSI Driver
> >>>> + *
> >>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> >>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> >>>> + *        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 as
> >>>> published by the
> >>>> + * Free Software Foundation;  either version 2 of the  License,
> >>>> or (at your
> >>>> + * option) any later version.
> >>>> + *
> >>>> + * 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.
> >>>> + */
> >>>> +#include <linux/interrupt.h>
> >>>> +#include <linux/module.h>
> >>>> +#include <linux/msi.h>
> >>>> +#include <linux/of_irq.h>
> >>>> +#include <linux/pci.h>
> >>>> +#include <linux/platform_device.h>
> >>>> +#include <linux/of_pci.h>
> >>>> +
> >>>> +#define MSI_INDEX0             0x000000
> >>>> +#define MSI_INT0               0x800000
> >>>> +
> >>>> +struct xgene_msi_settings {
> >>>> +       u32     index_per_group;
> >>>> +       u32     irqs_per_index;
> >>>> +       u32     nr_msi_vec;
> >>>> +       u32     nr_hw_irqs;
> >>>> +};
> >>>> +
> >>>> +struct xgene_msi {
> >>>> +       struct device_node              *node;
> >>>> +       struct msi_controller           mchip;
> >>>> +       struct irq_domain               *domain;
> >>>> +       struct xgene_msi_settings       *settings;
> >>>> +       u32                             msi_addr_lo;
> >>>> +       u32                             msi_addr_hi;
> >>>
> >>>
> >>> I'd rather see the mailbox address directly, and only do the
> >>> split when assigning it to the message (you seem to play all kind
> >>> of tricks on the address anyway).
> >>
> >>
> >> msi_addr_lo and msi_addr_hi store the physical base address of MSI
> >> controller registers. I will add comment to clarify this.
> >
> >
> > What I mean is that there is no point in keeping this around as a
> > pair of 32bit variables. You'd better keep it as a single 64bit,
> > and do the split when assigning it the the MSI message.
> 
> Hi Marc,
> 
> These came from device-tree (which describes 64-bit address number as
> 2 32-bit words).

... and converted to a resource as a 64bit word, on which you apply
{upper,lower}_32_bit(). So much for DT...

> If I store them this way, I don't need CPU cycles to do the split
> every time assigning them to the MSI message. Please let me know what
> do you think about it.

This is getting absolutely silly.

How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If
it takes so long that it is considered to be a bottleneck, I suggest
you go and design a better CPU (hint: the answer is probably 1 cycle
absolutely everywhere).

How often are you configuring MSIs in the face of what is happening in
the rest of the kernel? Almost never!

So, given that "never" times 1 is still never,  I'll consider that
readability of the code trumps it anytime (I can't believe we're having
that kind of conversation...).

> >
> > [...]
> >
> >>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> >>>> +                                 const struct cpumask *mask,
> >>>> bool force)
> >>>> +{
> >>>> +       struct xgene_msi *msi =
> >>>> irq_data_get_irq_chip_data(irq_data);
> >>>> +       unsigned int gic_irq;
> >>>> +       int ret;
> >>>> +
> >>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq %
> >>>> msi->settings->nr_hw_irqs];
> >>>> +       ret = irq_set_affinity(gic_irq, mask);
> >>>
> >>>
> >>> Erm... This as the effect of moving *all* the MSIs hanging off
> >>> this interrupt to another CPU. I'm not sure that's an acceptable
> >>> effect... What if another MSI requires a different affinity?
> >>
> >>
> >> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
> >> attached to it.
> >> So this will move all MSIs handing off this interrupt to another
> >> CPU; and we don't support different affinity settings for
> >> different MSIs that are attached to the same hardware IRQ.
> >
> >
> > Well, that's a significant departure from the expected behaviour.
> > In other words, I wonder how useful this is. Could you instead
> > reconfigure the MSI itself to hit the right CPU (assuming you don't
> > have more than 16 CPUs and if
> > that's only for XGene-1, this will only be 8 at most)? This would
> > reduce your number of possible MSIs, but at least the semantics of
> > set_afinity would be preserved.
> 
> X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each
> group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have
> 16 hardware GIC IRQs for 2688 MSIs).

We've already established that.

> Setting affinity of single MSI to deliver it to a target CPU will move
> all the MSIs mapped to the same GIC IRQ to that CPU as well. This is
> not a standard behavior, but limiting the total number of MSIs will
> cause a lot of devices to fall back to INTx (with huge performance
> penalty) or even fail to load their driver as these devices request
> more than 16 MSIs during driver initialization.

No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and
provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per
CPU (having statically assigned 2 IRQs per CPU).

Assuming you adopt my scheme, you still have a grand total of 336 MSIs
that can be freely moved around without breaking any userspace
expectations.

I think that 336 MSIs is a fair number (nowhere near the 16 you claim).
Most platforms are doing quite well with that kind of numbers. Also,
you don't have to allocate all the MSIs a device can possibly claim (up
to 2048 MSI-X per device), as they are all perfectly capable of using
less MSI without having to fallback to INTx).

> I can document the limitation in affinity setting of X-Gene-1 MSI in
> the driver to hopefully not make people surprise and hope to keep the
> total number of supported MSI as 2688 so that we can support as many
> cards that require MSI/MSI-X as possible.

I don't think this is a valid approach. This breaks userspace (think of
things like irqbalance), and breaks the SMP affinity model that Linux
uses. No amount of documentation is going to solve it, so I think you
just have to admit that the HW is mis-designed and do the best you can
to make it work like Linux expect it to work.

The alternative would to disable affinity setting altogether instead of
introducing these horrible side effects.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny.

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

* [PATCH v4 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-04-15  8:16                                 ` Marc Zyngier
@ 2015-04-17  9:50                                   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-17  9:50 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports.

v4 changes:
	1. Remove affinity setting for each MSI
	2. Add description about register layout, MSI termination address and data
	3. Correct total number of MSI vectors to 2048
	4. Clean up error messages
	5. Remove unused module code

v3 changes:
        1. Implement MSI support using PCI MSI IRQ domain
        2. Only use msi_controller to store IRQ domain
v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
 


 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  63 ++++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   6 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 410 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 ++
 7 files changed, 536 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1


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

* [PATCH v4 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-04-17  9:50                                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-17  9:50 UTC (permalink / raw)
  To: linux-arm-kernel

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports.

v4 changes:
	1. Remove affinity setting for each MSI
	2. Add description about register layout, MSI termination address and data
	3. Correct total number of MSI vectors to 2048
	4. Clean up error messages
	5. Remove unused module code

v3 changes:
        1. Implement MSI support using PCI MSI IRQ domain
        2. Only use msi_controller to store IRQ domain
v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
 


 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  63 ++++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   6 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 410 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 ++
 7 files changed, 536 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1

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

* [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-17  9:50                                   ` Duc Dang
@ 2015-04-17  9:50                                   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-17  9:50 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

X-Gene v1 SoC supports total 2048 MSI/MSIX vectors coalesced into
16 HW IRQ lines.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 410 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 438 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9..c9b61fa 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,17 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c..f39bde3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..f53b581e
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,410 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0			0x000000
+#define MSI_INT0		0x800000
+
+#define IDX_PER_GROUP		8
+#define IRQS_PER_IDX		16
+#define NR_HW_IRQS		16
+#define NR_MSI_VEC		(IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	u64				msi_addr;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	int				*msi_virqs;
+};
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0			base_addr
+ * MSI0IR1			base_addr +  0x10000
+ * ...				...
+ * MSI0IR6			base_addr +  0x60000
+ * MSI0IR7			base_addr +  0x70000
+ * MSI1IR0			base_addr +  0x80000
+ * MSI1IR1			base_addr +  0x90000
+ * ...				...
+ * MSI1IR7			base_addr +  0xF0000
+ * MSI2IR0			base_addr + 0x100000
+ * ...				...
+ * MSIFIR0			base_addr + 0x780000
+ * MSIFIR1			base_addr + 0x790000
+ * ...				...
+ * MSIFIR7			base_addr + 0x7F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *		Group 0: 0-255
+ *		Group 1: 256-511
+ *		Group 2: 512-767
+ *		...
+ *		Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *	As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *		Group 0: 0-15
+ *		Group 1: 16-32
+ *		...
+ *		Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+	u32 group = data->hwirq % NR_HW_IRQS;
+
+	msg->address_hi = upper_32_bits(msi->msi_addr);
+	msg->address_lo = lower_32_bits(msi->msi_addr) +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+				  const struct cpumask *mask, bool force)
+{
+	return IRQ_SET_MASK_OK_DONE;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+
+	struct xgene_msi *msi = domain->host_data;
+	int msi_irq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = find_first_zero_bit(msi->bitmap, NR_MSI_VEC);
+	if (msi_irq < NR_MSI_VEC)
+		set_bit(msi_irq, msi->bitmap);
+	else
+		msi_irq = -ENOSPC;
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
+			    domain->host_data, handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+
+	mutex_lock(&msi->bitmap_lock);
+
+	if (test_bit(d->hwirq, msi->bitmap))
+		clear_bit(d->hwirq, msi->bitmap);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+					    &msi_domain_ops, msi);
+	if (!msi->domain)
+		return -ENOMEM;
+
+	msi->mchip.of_node = msi->node;
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
+	if (!xgene_msi->msi_virqs)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct xgene_msi *xgene_msi;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp, processed = 0;
+
+	chained_irq_enter(chip, desc);
+
+	xgene_msi = irq_desc_get_handler_data(desc);
+
+	msi_grp = irq - xgene_msi->msi_virqs[0];
+	if (msi_grp >= NR_HW_IRQS) {
+		chained_irq_exit(chip, desc);
+		return;
+	}
+
+	/*
+	 * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+	 * If bit x of this register is set (x is 0..7), one or more interupts
+	 * corresponding to MSInIRx is set.
+	 */
+	grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		/*
+		 * Calculate MSInIRx address to read to check for interrupts
+		 * (refer to termination address and data assignment
+		 * described in xgene_compose_msi_msg function)
+		 */
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			/*
+			 * Calculate MSI vector number (refer to the termination
+			 * address and data assignment described in
+			 * xgene_compose_msi_msg function)
+			 */
+			hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+				 NR_HW_IRQS) + msi_grp;
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+			processed++;
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		virq = msi->msi_virqs[i];
+		if (virq != 0)
+			free_irq(virq, msi);
+	}
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+				 struct platform_device *pdev,
+				 int irq_index)
+{
+	int virt_msir;
+	cpumask_var_t mask;
+	int err;
+
+	virt_msir = platform_get_irq(pdev, irq_index);
+	if (virt_msir < 0) {
+		dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+			irq_index);
+		return -EINVAL;
+	}
+
+	irq_set_chained_handler(virt_msir, xgene_msi_isr);
+	err = irq_set_handler_data(virt_msir, msi);
+	if (err) {
+		dev_err(&pdev->dev, "request irq failed\n");
+		return err;
+	}
+
+	if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+		cpumask_clear(mask);
+		cpumask_set_cpu(irq_index % num_online_cpus(), mask);
+		irq_set_affinity(virt_msir, mask);
+		free_cpumask_var(mask);
+	}
+
+	msi->msi_virqs[irq_index] = virt_msir;
+
+	return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi"},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct device_node *np;
+	const struct of_device_id *matched_np;
+	struct xgene_msi *xgene_msi;
+
+	np = of_find_matching_node_and_match(NULL,
+			     xgene_msi_match_table, &matched_np);
+	if (!np)
+		return -ENODEV;
+
+	xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
+	if (!xgene_msi) {
+		dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
+		return -ENOMEM;
+	}
+
+	xgene_msi->node = np;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	xgene_msi->msi_addr = res->start;
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+		if (rc)
+			goto error;
+	}
+
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 22751ed..3e6faa1 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1


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

* [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-17  9:50                                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-17  9:50 UTC (permalink / raw)
  To: linux-arm-kernel

X-Gene v1 SoC supports total 2048 MSI/MSIX vectors coalesced into
16 HW IRQ lines.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 410 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 438 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9..c9b61fa 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,17 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c..f39bde3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..f53b581e
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,410 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0			0x000000
+#define MSI_INT0		0x800000
+
+#define IDX_PER_GROUP		8
+#define IRQS_PER_IDX		16
+#define NR_HW_IRQS		16
+#define NR_MSI_VEC		(IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	u64				msi_addr;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	int				*msi_virqs;
+};
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0			base_addr
+ * MSI0IR1			base_addr +  0x10000
+ * ...				...
+ * MSI0IR6			base_addr +  0x60000
+ * MSI0IR7			base_addr +  0x70000
+ * MSI1IR0			base_addr +  0x80000
+ * MSI1IR1			base_addr +  0x90000
+ * ...				...
+ * MSI1IR7			base_addr +  0xF0000
+ * MSI2IR0			base_addr + 0x100000
+ * ...				...
+ * MSIFIR0			base_addr + 0x780000
+ * MSIFIR1			base_addr + 0x790000
+ * ...				...
+ * MSIFIR7			base_addr + 0x7F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *		Group 0: 0-255
+ *		Group 1: 256-511
+ *		Group 2: 512-767
+ *		...
+ *		Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *	As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *		Group 0: 0-15
+ *		Group 1: 16-32
+ *		...
+ *		Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+	u32 group = data->hwirq % NR_HW_IRQS;
+
+	msg->address_hi = upper_32_bits(msi->msi_addr);
+	msg->address_lo = lower_32_bits(msi->msi_addr) +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+				  const struct cpumask *mask, bool force)
+{
+	return IRQ_SET_MASK_OK_DONE;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+
+	struct xgene_msi *msi = domain->host_data;
+	int msi_irq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = find_first_zero_bit(msi->bitmap, NR_MSI_VEC);
+	if (msi_irq < NR_MSI_VEC)
+		set_bit(msi_irq, msi->bitmap);
+	else
+		msi_irq = -ENOSPC;
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
+			    domain->host_data, handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+
+	mutex_lock(&msi->bitmap_lock);
+
+	if (test_bit(d->hwirq, msi->bitmap))
+		clear_bit(d->hwirq, msi->bitmap);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+					    &msi_domain_ops, msi);
+	if (!msi->domain)
+		return -ENOMEM;
+
+	msi->mchip.of_node = msi->node;
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
+	if (!xgene_msi->msi_virqs)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct xgene_msi *xgene_msi;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp, processed = 0;
+
+	chained_irq_enter(chip, desc);
+
+	xgene_msi = irq_desc_get_handler_data(desc);
+
+	msi_grp = irq - xgene_msi->msi_virqs[0];
+	if (msi_grp >= NR_HW_IRQS) {
+		chained_irq_exit(chip, desc);
+		return;
+	}
+
+	/*
+	 * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+	 * If bit x of this register is set (x is 0..7), one or more interupts
+	 * corresponding to MSInIRx is set.
+	 */
+	grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		/*
+		 * Calculate MSInIRx address to read to check for interrupts
+		 * (refer to termination address and data assignment
+		 * described in xgene_compose_msi_msg function)
+		 */
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			/*
+			 * Calculate MSI vector number (refer to the termination
+			 * address and data assignment described in
+			 * xgene_compose_msi_msg function)
+			 */
+			hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+				 NR_HW_IRQS) + msi_grp;
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+			processed++;
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		virq = msi->msi_virqs[i];
+		if (virq != 0)
+			free_irq(virq, msi);
+	}
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+				 struct platform_device *pdev,
+				 int irq_index)
+{
+	int virt_msir;
+	cpumask_var_t mask;
+	int err;
+
+	virt_msir = platform_get_irq(pdev, irq_index);
+	if (virt_msir < 0) {
+		dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+			irq_index);
+		return -EINVAL;
+	}
+
+	irq_set_chained_handler(virt_msir, xgene_msi_isr);
+	err = irq_set_handler_data(virt_msir, msi);
+	if (err) {
+		dev_err(&pdev->dev, "request irq failed\n");
+		return err;
+	}
+
+	if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+		cpumask_clear(mask);
+		cpumask_set_cpu(irq_index % num_online_cpus(), mask);
+		irq_set_affinity(virt_msir, mask);
+		free_cpumask_var(mask);
+	}
+
+	msi->msi_virqs[irq_index] = virt_msir;
+
+	return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi"},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct device_node *np;
+	const struct of_device_id *matched_np;
+	struct xgene_msi *xgene_msi;
+
+	np = of_find_matching_node_and_match(NULL,
+			     xgene_msi_match_table, &matched_np);
+	if (!np)
+		return -ENODEV;
+
+	xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
+	if (!xgene_msi) {
+		dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
+		return -ENOMEM;
+	}
+
+	xgene_msi->node = np;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	xgene_msi->msi_addr = res->start;
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+		if (rc)
+			goto error;
+	}
+
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 22751ed..3e6faa1 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1

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

* [PATCH v4 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
  2015-04-17  9:50                                   ` Duc Dang
@ 2015-04-17  9:50                                   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-17  9:50 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..4b719c9 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi@79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie@1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie@1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie@1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie@1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie@1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial@1c020000 {
-- 
1.9.1


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

* [PATCH v4 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
@ 2015-04-17  9:50                                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-17  9:50 UTC (permalink / raw)
  To: linux-arm-kernel

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..4b719c9 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
-- 
1.9.1

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

* [PATCH v4 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
  2015-04-17  9:50                                   ` Duc Dang
@ 2015-04-17  9:50                                   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-17  9:50 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..0ffdcb3
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,63 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene1-msi" to identify the core.
+- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi@79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = <  0x0 0x10 0x4
+				0x0 0x11 0x4
+				0x0 0x12 0x4
+				0x0 0x13 0x4
+				0x0 0x14 0x4
+				0x0 0x15 0x4
+				0x0 0x16 0x4
+				0x0 0x17 0x4
+				0x0 0x18 0x4
+				0x0 0x19 0x4
+				0x0 0x1a 0x4
+				0x0 0x1b 0x4
+				0x0 0x1c 0x4
+				0x0 0x1d 0x4
+				0x0 0x1e 0x4
+				0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie@1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1


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

* [PATCH v4 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
@ 2015-04-17  9:50                                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-17  9:50 UTC (permalink / raw)
  To: linux-arm-kernel

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..0ffdcb3
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,63 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene1-msi" to identify the core.
+- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = <  0x0 0x10 0x4
+				0x0 0x11 0x4
+				0x0 0x12 0x4
+				0x0 0x13 0x4
+				0x0 0x14 0x4
+				0x0 0x15 0x4
+				0x0 0x16 0x4
+				0x0 0x17 0x4
+				0x0 0x18 0x4
+				0x0 0x19 0x4
+				0x0 0x1a 0x4
+				0x0 0x1b 0x4
+				0x0 0x1c 0x4
+				0x0 0x1d 0x4
+				0x0 0x1e 0x4
+				0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1

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

* [PATCH v4 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
  2015-04-17  9:50                                   ` Duc Dang
@ 2015-04-17  9:50                                   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-17  9:50 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddc5a8c..a1b119b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7490,6 +7490,14 @@ L:	linux-pci@vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci@vger.kernel.org
+L:	linux-arm-kernel@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia@lists.infradead.org
-- 
1.9.1


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

* [PATCH v4 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
@ 2015-04-17  9:50                                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-17  9:50 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddc5a8c..a1b119b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7490,6 +7490,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
-- 
1.9.1

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-15  8:16                                 ` Marc Zyngier
@ 2015-04-17 10:00                                   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-17 10:00 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Feng Kan, Arnd Bergmann, linux-pci, Liviu Dudau, linux-kernel,
	grant.likely, Tanmay Inamdar, Bjorn Helgaas, linux-arm, Loc Ho

On Wed, Apr 15, 2015 at 1:16 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On Tue, 14 Apr 2015 19:20:19 +0100
> Duc Dang <dhdang@apm.com> wrote:
>
>> On Sat, Apr 11, 2015 at 5:06 AM, Marc Zyngier <marc.zyngier@arm.com>
>> wrote:
>> > On 2015-04-11 00:42, Duc Dang wrote:
>> >>
>> >> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier
>> >> <marc.zyngier@arm.com> wrote:
>> >>>
>> >>> On 09/04/15 18:05, Duc Dang wrote:
>> >>>>
>> >>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>> >>>> 16 HW IRQ lines.
>> >>>>
>> >>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> >>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> >>>> ---
>> >>>>  drivers/pci/host/Kconfig         |   6 +
>> >>>>  drivers/pci/host/Makefile        |   1 +
>> >>>>  drivers/pci/host/pci-xgene-msi.c | 407
>> >>>> +++++++++++++++++++++++++++++++++++++++
>> >>>>  drivers/pci/host/pci-xgene.c     |  21 ++
>> >>>>  4 files changed, 435 insertions(+)
>> >>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>> >>>>
>> >>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> >>>> index 7b892a9..c9b61fa 100644
>> >>>> --- a/drivers/pci/host/Kconfig
>> >>>> +++ b/drivers/pci/host/Kconfig
>> >>>> @@ -89,11 +89,17 @@ config PCI_XGENE
>> >>>>         depends on ARCH_XGENE
>> >>>>         depends on OF
>> >>>>         select PCIEPORTBUS
>> >>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>> >>>> +       select PCI_XGENE_MSI if PCI_MSI
>> >>>>         help
>> >>>>           Say Y here if you want internal PCI support on APM
>> >>>> X-Gene SoC. There are 5 internal PCIe ports available. Each port
>> >>>> is GEN3 capable
>> >>>>           and have varied lanes from x1 to x8.
>> >>>>
>> >>>> +config PCI_XGENE_MSI
>> >>>> +       bool "X-Gene v1 PCIe MSI feature"
>> >>>> +       depends on PCI_XGENE && PCI_MSI
>> >>>> +
>> >>>>  config PCI_LAYERSCAPE
>> >>>>         bool "Freescale Layerscape PCIe controller"
>> >>>>         depends on OF && ARM
>> >>>> diff --git a/drivers/pci/host/Makefile
>> >>>> b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644
>> >>>> --- a/drivers/pci/host/Makefile
>> >>>> +++ b/drivers/pci/host/Makefile
>> >>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> >>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>> >>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>> >>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> >>>> diff --git a/drivers/pci/host/pci-xgene-msi.c
>> >>>> b/drivers/pci/host/pci-xgene-msi.c
>> >>>> new file mode 100644
>> >>>> index 0000000..4f0ff42
>> >>>> --- /dev/null
>> >>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> >>>> @@ -0,0 +1,407 @@
>> >>>> +/*
>> >>>> + * APM X-Gene MSI Driver
>> >>>> + *
>> >>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> >>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> >>>> + *        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 as
>> >>>> published by the
>> >>>> + * Free Software Foundation;  either version 2 of the  License,
>> >>>> or (at your
>> >>>> + * option) any later version.
>> >>>> + *
>> >>>> + * 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.
>> >>>> + */
>> >>>> +#include <linux/interrupt.h>
>> >>>> +#include <linux/module.h>
>> >>>> +#include <linux/msi.h>
>> >>>> +#include <linux/of_irq.h>
>> >>>> +#include <linux/pci.h>
>> >>>> +#include <linux/platform_device.h>
>> >>>> +#include <linux/of_pci.h>
>> >>>> +
>> >>>> +#define MSI_INDEX0             0x000000
>> >>>> +#define MSI_INT0               0x800000
>> >>>> +
>> >>>> +struct xgene_msi_settings {
>> >>>> +       u32     index_per_group;
>> >>>> +       u32     irqs_per_index;
>> >>>> +       u32     nr_msi_vec;
>> >>>> +       u32     nr_hw_irqs;
>> >>>> +};
>> >>>> +
>> >>>> +struct xgene_msi {
>> >>>> +       struct device_node              *node;
>> >>>> +       struct msi_controller           mchip;
>> >>>> +       struct irq_domain               *domain;
>> >>>> +       struct xgene_msi_settings       *settings;
>> >>>> +       u32                             msi_addr_lo;
>> >>>> +       u32                             msi_addr_hi;
>> >>>
>> >>>
>> >>> I'd rather see the mailbox address directly, and only do the
>> >>> split when assigning it to the message (you seem to play all kind
>> >>> of tricks on the address anyway).
>> >>
>> >>
>> >> msi_addr_lo and msi_addr_hi store the physical base address of MSI
>> >> controller registers. I will add comment to clarify this.
>> >
>> >
>> > What I mean is that there is no point in keeping this around as a
>> > pair of 32bit variables. You'd better keep it as a single 64bit,
>> > and do the split when assigning it the the MSI message.
>>
>> Hi Marc,
>>
>> These came from device-tree (which describes 64-bit address number as
>> 2 32-bit words).
>
> ... and converted to a resource as a 64bit word, on which you apply
> {upper,lower}_32_bit(). So much for DT...
>
>> If I store them this way, I don't need CPU cycles to do the split
>> every time assigning them to the MSI message. Please let me know what
>> do you think about it.
>
> This is getting absolutely silly.
>
> How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If
> it takes so long that it is considered to be a bottleneck, I suggest
> you go and design a better CPU (hint: the answer is probably 1 cycle
> absolutely everywhere).
>
> How often are you configuring MSIs in the face of what is happening in
> the rest of the kernel? Almost never!
>
> So, given that "never" times 1 is still never,  I'll consider that
> readability of the code trumps it anytime (I can't believe we're having
> that kind of conversation...).
>
I changed to use u64 for msi_addr and split it when composing MSI messages.
The change is in v4 of the patch set that I just posted.
>> >
>> > [...]
>> >
>> >>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> >>>> +                                 const struct cpumask *mask,
>> >>>> bool force)
>> >>>> +{
>> >>>> +       struct xgene_msi *msi =
>> >>>> irq_data_get_irq_chip_data(irq_data);
>> >>>> +       unsigned int gic_irq;
>> >>>> +       int ret;
>> >>>> +
>> >>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq %
>> >>>> msi->settings->nr_hw_irqs];
>> >>>> +       ret = irq_set_affinity(gic_irq, mask);
>> >>>
>> >>>
>> >>> Erm... This as the effect of moving *all* the MSIs hanging off
>> >>> this interrupt to another CPU. I'm not sure that's an acceptable
>> >>> effect... What if another MSI requires a different affinity?
>> >>
>> >>
>> >> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
>> >> attached to it.
>> >> So this will move all MSIs handing off this interrupt to another
>> >> CPU; and we don't support different affinity settings for
>> >> different MSIs that are attached to the same hardware IRQ.
>> >
>> >
>> > Well, that's a significant departure from the expected behaviour.
>> > In other words, I wonder how useful this is. Could you instead
>> > reconfigure the MSI itself to hit the right CPU (assuming you don't
>> > have more than 16 CPUs and if
>> > that's only for XGene-1, this will only be 8 at most)? This would
>> > reduce your number of possible MSIs, but at least the semantics of
>> > set_afinity would be preserved.
>>
>> X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each
>> group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have
>> 16 hardware GIC IRQs for 2688 MSIs).
>
> We've already established that.
>
>> Setting affinity of single MSI to deliver it to a target CPU will move
>> all the MSIs mapped to the same GIC IRQ to that CPU as well. This is
>> not a standard behavior, but limiting the total number of MSIs will
>> cause a lot of devices to fall back to INTx (with huge performance
>> penalty) or even fail to load their driver as these devices request
>> more than 16 MSIs during driver initialization.
>
> No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and
> provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per
> CPU (having statically assigned 2 IRQs per CPU).
>
> Assuming you adopt my scheme, you still have a grand total of 336 MSIs
> that can be freely moved around without breaking any userspace
> expectations.
>
Thanks Marc. This is a very good idea.

But to  move MSIs around, I need to change MSI termination address and data
and write them to device configuration space. This may cause problems
if the device
fires an interrupt at the same time when I do the config write?

What is your opinion here?

> I think that 336 MSIs is a fair number (nowhere near the 16 you claim).
> Most platforms are doing quite well with that kind of numbers. Also,
> you don't have to allocate all the MSIs a device can possibly claim (up
> to 2048 MSI-X per device), as they are all perfectly capable of using
> less MSI without having to fallback to INTx).
>
>> I can document the limitation in affinity setting of X-Gene-1 MSI in
>> the driver to hopefully not make people surprise and hope to keep the
>> total number of supported MSI as 2688 so that we can support as many
>> cards that require MSI/MSI-X as possible.
>
> I don't think this is a valid approach. This breaks userspace (think of
> things like irqbalance), and breaks the SMP affinity model that Linux
> uses. No amount of documentation is going to solve it, so I think you
> just have to admit that the HW is mis-designed and do the best you can
> to make it work like Linux expect it to work.
>
> The alternative would to disable affinity setting altogether instead of
> introducing these horrible side effects.
>
I have it disabled (set_affinity does nothing) in my v4 patch.

> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny.

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-17 10:00                                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-17 10:00 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 15, 2015 at 1:16 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On Tue, 14 Apr 2015 19:20:19 +0100
> Duc Dang <dhdang@apm.com> wrote:
>
>> On Sat, Apr 11, 2015 at 5:06 AM, Marc Zyngier <marc.zyngier@arm.com>
>> wrote:
>> > On 2015-04-11 00:42, Duc Dang wrote:
>> >>
>> >> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier
>> >> <marc.zyngier@arm.com> wrote:
>> >>>
>> >>> On 09/04/15 18:05, Duc Dang wrote:
>> >>>>
>> >>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>> >>>> 16 HW IRQ lines.
>> >>>>
>> >>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> >>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> >>>> ---
>> >>>>  drivers/pci/host/Kconfig         |   6 +
>> >>>>  drivers/pci/host/Makefile        |   1 +
>> >>>>  drivers/pci/host/pci-xgene-msi.c | 407
>> >>>> +++++++++++++++++++++++++++++++++++++++
>> >>>>  drivers/pci/host/pci-xgene.c     |  21 ++
>> >>>>  4 files changed, 435 insertions(+)
>> >>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>> >>>>
>> >>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> >>>> index 7b892a9..c9b61fa 100644
>> >>>> --- a/drivers/pci/host/Kconfig
>> >>>> +++ b/drivers/pci/host/Kconfig
>> >>>> @@ -89,11 +89,17 @@ config PCI_XGENE
>> >>>>         depends on ARCH_XGENE
>> >>>>         depends on OF
>> >>>>         select PCIEPORTBUS
>> >>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>> >>>> +       select PCI_XGENE_MSI if PCI_MSI
>> >>>>         help
>> >>>>           Say Y here if you want internal PCI support on APM
>> >>>> X-Gene SoC. There are 5 internal PCIe ports available. Each port
>> >>>> is GEN3 capable
>> >>>>           and have varied lanes from x1 to x8.
>> >>>>
>> >>>> +config PCI_XGENE_MSI
>> >>>> +       bool "X-Gene v1 PCIe MSI feature"
>> >>>> +       depends on PCI_XGENE && PCI_MSI
>> >>>> +
>> >>>>  config PCI_LAYERSCAPE
>> >>>>         bool "Freescale Layerscape PCIe controller"
>> >>>>         depends on OF && ARM
>> >>>> diff --git a/drivers/pci/host/Makefile
>> >>>> b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644
>> >>>> --- a/drivers/pci/host/Makefile
>> >>>> +++ b/drivers/pci/host/Makefile
>> >>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> >>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>> >>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>> >>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> >>>> diff --git a/drivers/pci/host/pci-xgene-msi.c
>> >>>> b/drivers/pci/host/pci-xgene-msi.c
>> >>>> new file mode 100644
>> >>>> index 0000000..4f0ff42
>> >>>> --- /dev/null
>> >>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> >>>> @@ -0,0 +1,407 @@
>> >>>> +/*
>> >>>> + * APM X-Gene MSI Driver
>> >>>> + *
>> >>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> >>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> >>>> + *        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 as
>> >>>> published by the
>> >>>> + * Free Software Foundation;  either version 2 of the  License,
>> >>>> or (at your
>> >>>> + * option) any later version.
>> >>>> + *
>> >>>> + * 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.
>> >>>> + */
>> >>>> +#include <linux/interrupt.h>
>> >>>> +#include <linux/module.h>
>> >>>> +#include <linux/msi.h>
>> >>>> +#include <linux/of_irq.h>
>> >>>> +#include <linux/pci.h>
>> >>>> +#include <linux/platform_device.h>
>> >>>> +#include <linux/of_pci.h>
>> >>>> +
>> >>>> +#define MSI_INDEX0             0x000000
>> >>>> +#define MSI_INT0               0x800000
>> >>>> +
>> >>>> +struct xgene_msi_settings {
>> >>>> +       u32     index_per_group;
>> >>>> +       u32     irqs_per_index;
>> >>>> +       u32     nr_msi_vec;
>> >>>> +       u32     nr_hw_irqs;
>> >>>> +};
>> >>>> +
>> >>>> +struct xgene_msi {
>> >>>> +       struct device_node              *node;
>> >>>> +       struct msi_controller           mchip;
>> >>>> +       struct irq_domain               *domain;
>> >>>> +       struct xgene_msi_settings       *settings;
>> >>>> +       u32                             msi_addr_lo;
>> >>>> +       u32                             msi_addr_hi;
>> >>>
>> >>>
>> >>> I'd rather see the mailbox address directly, and only do the
>> >>> split when assigning it to the message (you seem to play all kind
>> >>> of tricks on the address anyway).
>> >>
>> >>
>> >> msi_addr_lo and msi_addr_hi store the physical base address of MSI
>> >> controller registers. I will add comment to clarify this.
>> >
>> >
>> > What I mean is that there is no point in keeping this around as a
>> > pair of 32bit variables. You'd better keep it as a single 64bit,
>> > and do the split when assigning it the the MSI message.
>>
>> Hi Marc,
>>
>> These came from device-tree (which describes 64-bit address number as
>> 2 32-bit words).
>
> ... and converted to a resource as a 64bit word, on which you apply
> {upper,lower}_32_bit(). So much for DT...
>
>> If I store them this way, I don't need CPU cycles to do the split
>> every time assigning them to the MSI message. Please let me know what
>> do you think about it.
>
> This is getting absolutely silly.
>
> How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If
> it takes so long that it is considered to be a bottleneck, I suggest
> you go and design a better CPU (hint: the answer is probably 1 cycle
> absolutely everywhere).
>
> How often are you configuring MSIs in the face of what is happening in
> the rest of the kernel? Almost never!
>
> So, given that "never" times 1 is still never,  I'll consider that
> readability of the code trumps it anytime (I can't believe we're having
> that kind of conversation...).
>
I changed to use u64 for msi_addr and split it when composing MSI messages.
The change is in v4 of the patch set that I just posted.
>> >
>> > [...]
>> >
>> >>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> >>>> +                                 const struct cpumask *mask,
>> >>>> bool force)
>> >>>> +{
>> >>>> +       struct xgene_msi *msi =
>> >>>> irq_data_get_irq_chip_data(irq_data);
>> >>>> +       unsigned int gic_irq;
>> >>>> +       int ret;
>> >>>> +
>> >>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq %
>> >>>> msi->settings->nr_hw_irqs];
>> >>>> +       ret = irq_set_affinity(gic_irq, mask);
>> >>>
>> >>>
>> >>> Erm... This as the effect of moving *all* the MSIs hanging off
>> >>> this interrupt to another CPU. I'm not sure that's an acceptable
>> >>> effect... What if another MSI requires a different affinity?
>> >>
>> >>
>> >> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
>> >> attached to it.
>> >> So this will move all MSIs handing off this interrupt to another
>> >> CPU; and we don't support different affinity settings for
>> >> different MSIs that are attached to the same hardware IRQ.
>> >
>> >
>> > Well, that's a significant departure from the expected behaviour.
>> > In other words, I wonder how useful this is. Could you instead
>> > reconfigure the MSI itself to hit the right CPU (assuming you don't
>> > have more than 16 CPUs and if
>> > that's only for XGene-1, this will only be 8 at most)? This would
>> > reduce your number of possible MSIs, but at least the semantics of
>> > set_afinity would be preserved.
>>
>> X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each
>> group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have
>> 16 hardware GIC IRQs for 2688 MSIs).
>
> We've already established that.
>
>> Setting affinity of single MSI to deliver it to a target CPU will move
>> all the MSIs mapped to the same GIC IRQ to that CPU as well. This is
>> not a standard behavior, but limiting the total number of MSIs will
>> cause a lot of devices to fall back to INTx (with huge performance
>> penalty) or even fail to load their driver as these devices request
>> more than 16 MSIs during driver initialization.
>
> No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and
> provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per
> CPU (having statically assigned 2 IRQs per CPU).
>
> Assuming you adopt my scheme, you still have a grand total of 336 MSIs
> that can be freely moved around without breaking any userspace
> expectations.
>
Thanks Marc. This is a very good idea.

But to  move MSIs around, I need to change MSI termination address and data
and write them to device configuration space. This may cause problems
if the device
fires an interrupt at the same time when I do the config write?

What is your opinion here?

> I think that 336 MSIs is a fair number (nowhere near the 16 you claim).
> Most platforms are doing quite well with that kind of numbers. Also,
> you don't have to allocate all the MSIs a device can possibly claim (up
> to 2048 MSI-X per device), as they are all perfectly capable of using
> less MSI without having to fallback to INTx).
>
>> I can document the limitation in affinity setting of X-Gene-1 MSI in
>> the driver to hopefully not make people surprise and hope to keep the
>> total number of supported MSI as 2688 so that we can support as many
>> cards that require MSI/MSI-X as possible.
>
> I don't think this is a valid approach. This breaks userspace (think of
> things like irqbalance), and breaks the SMP affinity model that Linux
> uses. No amount of documentation is going to solve it, so I think you
> just have to admit that the HW is mis-designed and do the best you can
> to make it work like Linux expect it to work.
>
> The alternative would to disable affinity setting altogether instead of
> introducing these horrible side effects.
>
I have it disabled (set_affinity does nothing) in my v4 patch.

> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny.

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-17 10:00                                   ` Duc Dang
@ 2015-04-17 10:17                                     ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-17 10:17 UTC (permalink / raw)
  To: Duc Dang
  Cc: Feng Kan, Arnd Bergmann, linux-pci, Liviu Dudau, linux-kernel,
	grant.likely, Tanmay Inamdar, Bjorn Helgaas, linux-arm, Loc Ho

On 17/04/15 11:00, Duc Dang wrote:
> On Wed, Apr 15, 2015 at 1:16 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> On Tue, 14 Apr 2015 19:20:19 +0100
>> Duc Dang <dhdang@apm.com> wrote:
>>
>>> On Sat, Apr 11, 2015 at 5:06 AM, Marc Zyngier <marc.zyngier@arm.com>
>>> wrote:
>>>> On 2015-04-11 00:42, Duc Dang wrote:
>>>>>
>>>>> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier
>>>>> <marc.zyngier@arm.com> wrote:
>>>>>>
>>>>>> On 09/04/15 18:05, Duc Dang wrote:
>>>>>>>
>>>>>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>>>>>> 16 HW IRQ lines.
>>>>>>>
>>>>>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>>>>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>> ---
>>>>>>>  drivers/pci/host/Kconfig         |   6 +
>>>>>>>  drivers/pci/host/Makefile        |   1 +
>>>>>>>  drivers/pci/host/pci-xgene-msi.c | 407
>>>>>>> +++++++++++++++++++++++++++++++++++++++
>>>>>>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>>>>>>  4 files changed, 435 insertions(+)
>>>>>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>>>>>>
>>>>>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>>>>>>> index 7b892a9..c9b61fa 100644
>>>>>>> --- a/drivers/pci/host/Kconfig
>>>>>>> +++ b/drivers/pci/host/Kconfig
>>>>>>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>>>>>>         depends on ARCH_XGENE
>>>>>>>         depends on OF
>>>>>>>         select PCIEPORTBUS
>>>>>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>>>>>> +       select PCI_XGENE_MSI if PCI_MSI
>>>>>>>         help
>>>>>>>           Say Y here if you want internal PCI support on APM
>>>>>>> X-Gene SoC. There are 5 internal PCIe ports available. Each port
>>>>>>> is GEN3 capable
>>>>>>>           and have varied lanes from x1 to x8.
>>>>>>>
>>>>>>> +config PCI_XGENE_MSI
>>>>>>> +       bool "X-Gene v1 PCIe MSI feature"
>>>>>>> +       depends on PCI_XGENE && PCI_MSI
>>>>>>> +
>>>>>>>  config PCI_LAYERSCAPE
>>>>>>>         bool "Freescale Layerscape PCIe controller"
>>>>>>>         depends on OF && ARM
>>>>>>> diff --git a/drivers/pci/host/Makefile
>>>>>>> b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644
>>>>>>> --- a/drivers/pci/host/Makefile
>>>>>>> +++ b/drivers/pci/host/Makefile
>>>>>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>>>>>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>>>>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>>>>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>>>>>> diff --git a/drivers/pci/host/pci-xgene-msi.c
>>>>>>> b/drivers/pci/host/pci-xgene-msi.c
>>>>>>> new file mode 100644
>>>>>>> index 0000000..4f0ff42
>>>>>>> --- /dev/null
>>>>>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>>>>>> @@ -0,0 +1,407 @@
>>>>>>> +/*
>>>>>>> + * APM X-Gene MSI Driver
>>>>>>> + *
>>>>>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>>>>>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>> + *        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 as
>>>>>>> published by the
>>>>>>> + * Free Software Foundation;  either version 2 of the  License,
>>>>>>> or (at your
>>>>>>> + * option) any later version.
>>>>>>> + *
>>>>>>> + * 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.
>>>>>>> + */
>>>>>>> +#include <linux/interrupt.h>
>>>>>>> +#include <linux/module.h>
>>>>>>> +#include <linux/msi.h>
>>>>>>> +#include <linux/of_irq.h>
>>>>>>> +#include <linux/pci.h>
>>>>>>> +#include <linux/platform_device.h>
>>>>>>> +#include <linux/of_pci.h>
>>>>>>> +
>>>>>>> +#define MSI_INDEX0             0x000000
>>>>>>> +#define MSI_INT0               0x800000
>>>>>>> +
>>>>>>> +struct xgene_msi_settings {
>>>>>>> +       u32     index_per_group;
>>>>>>> +       u32     irqs_per_index;
>>>>>>> +       u32     nr_msi_vec;
>>>>>>> +       u32     nr_hw_irqs;
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct xgene_msi {
>>>>>>> +       struct device_node              *node;
>>>>>>> +       struct msi_controller           mchip;
>>>>>>> +       struct irq_domain               *domain;
>>>>>>> +       struct xgene_msi_settings       *settings;
>>>>>>> +       u32                             msi_addr_lo;
>>>>>>> +       u32                             msi_addr_hi;
>>>>>>
>>>>>>
>>>>>> I'd rather see the mailbox address directly, and only do the
>>>>>> split when assigning it to the message (you seem to play all kind
>>>>>> of tricks on the address anyway).
>>>>>
>>>>>
>>>>> msi_addr_lo and msi_addr_hi store the physical base address of MSI
>>>>> controller registers. I will add comment to clarify this.
>>>>
>>>>
>>>> What I mean is that there is no point in keeping this around as a
>>>> pair of 32bit variables. You'd better keep it as a single 64bit,
>>>> and do the split when assigning it the the MSI message.
>>>
>>> Hi Marc,
>>>
>>> These came from device-tree (which describes 64-bit address number as
>>> 2 32-bit words).
>>
>> ... and converted to a resource as a 64bit word, on which you apply
>> {upper,lower}_32_bit(). So much for DT...
>>
>>> If I store them this way, I don't need CPU cycles to do the split
>>> every time assigning them to the MSI message. Please let me know what
>>> do you think about it.
>>
>> This is getting absolutely silly.
>>
>> How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If
>> it takes so long that it is considered to be a bottleneck, I suggest
>> you go and design a better CPU (hint: the answer is probably 1 cycle
>> absolutely everywhere).
>>
>> How often are you configuring MSIs in the face of what is happening in
>> the rest of the kernel? Almost never!
>>
>> So, given that "never" times 1 is still never,  I'll consider that
>> readability of the code trumps it anytime (I can't believe we're having
>> that kind of conversation...).
>>
> I changed to use u64 for msi_addr and split it when composing MSI messages.
> The change is in v4 of the patch set that I just posted.
>>>>
>>>> [...]
>>>>
>>>>>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>>>>>>> +                                 const struct cpumask *mask,
>>>>>>> bool force)
>>>>>>> +{
>>>>>>> +       struct xgene_msi *msi =
>>>>>>> irq_data_get_irq_chip_data(irq_data);
>>>>>>> +       unsigned int gic_irq;
>>>>>>> +       int ret;
>>>>>>> +
>>>>>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq %
>>>>>>> msi->settings->nr_hw_irqs];
>>>>>>> +       ret = irq_set_affinity(gic_irq, mask);
>>>>>>
>>>>>>
>>>>>> Erm... This as the effect of moving *all* the MSIs hanging off
>>>>>> this interrupt to another CPU. I'm not sure that's an acceptable
>>>>>> effect... What if another MSI requires a different affinity?
>>>>>
>>>>>
>>>>> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
>>>>> attached to it.
>>>>> So this will move all MSIs handing off this interrupt to another
>>>>> CPU; and we don't support different affinity settings for
>>>>> different MSIs that are attached to the same hardware IRQ.
>>>>
>>>>
>>>> Well, that's a significant departure from the expected behaviour.
>>>> In other words, I wonder how useful this is. Could you instead
>>>> reconfigure the MSI itself to hit the right CPU (assuming you don't
>>>> have more than 16 CPUs and if
>>>> that's only for XGene-1, this will only be 8 at most)? This would
>>>> reduce your number of possible MSIs, but at least the semantics of
>>>> set_afinity would be preserved.
>>>
>>> X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each
>>> group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have
>>> 16 hardware GIC IRQs for 2688 MSIs).
>>
>> We've already established that.
>>
>>> Setting affinity of single MSI to deliver it to a target CPU will move
>>> all the MSIs mapped to the same GIC IRQ to that CPU as well. This is
>>> not a standard behavior, but limiting the total number of MSIs will
>>> cause a lot of devices to fall back to INTx (with huge performance
>>> penalty) or even fail to load their driver as these devices request
>>> more than 16 MSIs during driver initialization.
>>
>> No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and
>> provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per
>> CPU (having statically assigned 2 IRQs per CPU).
>>
>> Assuming you adopt my scheme, you still have a grand total of 336 MSIs
>> that can be freely moved around without breaking any userspace
>> expectations.
>>
> Thanks Marc. This is a very good idea.
> 
> But to  move MSIs around, I need to change MSI termination address and data
> and write them to device configuration space. This may cause problems
> if the device
> fires an interrupt at the same time when I do the config write?
> 
> What is your opinion here?

There is an inherent race when changing the affinity of any interrupt,
whether that's an MSI or not. The kernel is perfectly prepared to handle
such a situation (to be honest, the kernel doesn't really care).

The write to the config space shouldn't be a concern. You will either
hit the old *or* the new CPU, but that race is only during the write
itself (you can read back from the config space if you're really
paranoid).  By the time you return from this read/write, the device will
be reconfigured.

>> I think that 336 MSIs is a fair number (nowhere near the 16 you claim).
>> Most platforms are doing quite well with that kind of numbers. Also,
>> you don't have to allocate all the MSIs a device can possibly claim (up
>> to 2048 MSI-X per device), as they are all perfectly capable of using
>> less MSI without having to fallback to INTx).
>>
>>> I can document the limitation in affinity setting of X-Gene-1 MSI in
>>> the driver to hopefully not make people surprise and hope to keep the
>>> total number of supported MSI as 2688 so that we can support as many
>>> cards that require MSI/MSI-X as possible.
>>
>> I don't think this is a valid approach. This breaks userspace (think of
>> things like irqbalance), and breaks the SMP affinity model that Linux
>> uses. No amount of documentation is going to solve it, so I think you
>> just have to admit that the HW is mis-designed and do the best you can
>> to make it work like Linux expect it to work.
>>
>> The alternative would to disable affinity setting altogether instead of
>> introducing these horrible side effects.
>>
> I have it disabled (set_affinity does nothing) in my v4 patch.

It would be good if you could try the above approach. It shouldn't be
hard to write, and it would be a lot better than just ignoring the problem.

I'll try to review the patches soon(-ish)...

	M.
-- 
Jazz is not dead. It just smells funny...

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-17 10:17                                     ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-17 10:17 UTC (permalink / raw)
  To: linux-arm-kernel

On 17/04/15 11:00, Duc Dang wrote:
> On Wed, Apr 15, 2015 at 1:16 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> On Tue, 14 Apr 2015 19:20:19 +0100
>> Duc Dang <dhdang@apm.com> wrote:
>>
>>> On Sat, Apr 11, 2015 at 5:06 AM, Marc Zyngier <marc.zyngier@arm.com>
>>> wrote:
>>>> On 2015-04-11 00:42, Duc Dang wrote:
>>>>>
>>>>> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier
>>>>> <marc.zyngier@arm.com> wrote:
>>>>>>
>>>>>> On 09/04/15 18:05, Duc Dang wrote:
>>>>>>>
>>>>>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>>>>>> 16 HW IRQ lines.
>>>>>>>
>>>>>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>>>>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>> ---
>>>>>>>  drivers/pci/host/Kconfig         |   6 +
>>>>>>>  drivers/pci/host/Makefile        |   1 +
>>>>>>>  drivers/pci/host/pci-xgene-msi.c | 407
>>>>>>> +++++++++++++++++++++++++++++++++++++++
>>>>>>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>>>>>>  4 files changed, 435 insertions(+)
>>>>>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>>>>>>
>>>>>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>>>>>>> index 7b892a9..c9b61fa 100644
>>>>>>> --- a/drivers/pci/host/Kconfig
>>>>>>> +++ b/drivers/pci/host/Kconfig
>>>>>>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>>>>>>         depends on ARCH_XGENE
>>>>>>>         depends on OF
>>>>>>>         select PCIEPORTBUS
>>>>>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>>>>>> +       select PCI_XGENE_MSI if PCI_MSI
>>>>>>>         help
>>>>>>>           Say Y here if you want internal PCI support on APM
>>>>>>> X-Gene SoC. There are 5 internal PCIe ports available. Each port
>>>>>>> is GEN3 capable
>>>>>>>           and have varied lanes from x1 to x8.
>>>>>>>
>>>>>>> +config PCI_XGENE_MSI
>>>>>>> +       bool "X-Gene v1 PCIe MSI feature"
>>>>>>> +       depends on PCI_XGENE && PCI_MSI
>>>>>>> +
>>>>>>>  config PCI_LAYERSCAPE
>>>>>>>         bool "Freescale Layerscape PCIe controller"
>>>>>>>         depends on OF && ARM
>>>>>>> diff --git a/drivers/pci/host/Makefile
>>>>>>> b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644
>>>>>>> --- a/drivers/pci/host/Makefile
>>>>>>> +++ b/drivers/pci/host/Makefile
>>>>>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>>>>>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>>>>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>>>>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>>>>>> diff --git a/drivers/pci/host/pci-xgene-msi.c
>>>>>>> b/drivers/pci/host/pci-xgene-msi.c
>>>>>>> new file mode 100644
>>>>>>> index 0000000..4f0ff42
>>>>>>> --- /dev/null
>>>>>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>>>>>> @@ -0,0 +1,407 @@
>>>>>>> +/*
>>>>>>> + * APM X-Gene MSI Driver
>>>>>>> + *
>>>>>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>>>>>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>> + *        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 as
>>>>>>> published by the
>>>>>>> + * Free Software Foundation;  either version 2 of the  License,
>>>>>>> or (at your
>>>>>>> + * option) any later version.
>>>>>>> + *
>>>>>>> + * 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.
>>>>>>> + */
>>>>>>> +#include <linux/interrupt.h>
>>>>>>> +#include <linux/module.h>
>>>>>>> +#include <linux/msi.h>
>>>>>>> +#include <linux/of_irq.h>
>>>>>>> +#include <linux/pci.h>
>>>>>>> +#include <linux/platform_device.h>
>>>>>>> +#include <linux/of_pci.h>
>>>>>>> +
>>>>>>> +#define MSI_INDEX0             0x000000
>>>>>>> +#define MSI_INT0               0x800000
>>>>>>> +
>>>>>>> +struct xgene_msi_settings {
>>>>>>> +       u32     index_per_group;
>>>>>>> +       u32     irqs_per_index;
>>>>>>> +       u32     nr_msi_vec;
>>>>>>> +       u32     nr_hw_irqs;
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct xgene_msi {
>>>>>>> +       struct device_node              *node;
>>>>>>> +       struct msi_controller           mchip;
>>>>>>> +       struct irq_domain               *domain;
>>>>>>> +       struct xgene_msi_settings       *settings;
>>>>>>> +       u32                             msi_addr_lo;
>>>>>>> +       u32                             msi_addr_hi;
>>>>>>
>>>>>>
>>>>>> I'd rather see the mailbox address directly, and only do the
>>>>>> split when assigning it to the message (you seem to play all kind
>>>>>> of tricks on the address anyway).
>>>>>
>>>>>
>>>>> msi_addr_lo and msi_addr_hi store the physical base address of MSI
>>>>> controller registers. I will add comment to clarify this.
>>>>
>>>>
>>>> What I mean is that there is no point in keeping this around as a
>>>> pair of 32bit variables. You'd better keep it as a single 64bit,
>>>> and do the split when assigning it the the MSI message.
>>>
>>> Hi Marc,
>>>
>>> These came from device-tree (which describes 64-bit address number as
>>> 2 32-bit words).
>>
>> ... and converted to a resource as a 64bit word, on which you apply
>> {upper,lower}_32_bit(). So much for DT...
>>
>>> If I store them this way, I don't need CPU cycles to do the split
>>> every time assigning them to the MSI message. Please let me know what
>>> do you think about it.
>>
>> This is getting absolutely silly.
>>
>> How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If
>> it takes so long that it is considered to be a bottleneck, I suggest
>> you go and design a better CPU (hint: the answer is probably 1 cycle
>> absolutely everywhere).
>>
>> How often are you configuring MSIs in the face of what is happening in
>> the rest of the kernel? Almost never!
>>
>> So, given that "never" times 1 is still never,  I'll consider that
>> readability of the code trumps it anytime (I can't believe we're having
>> that kind of conversation...).
>>
> I changed to use u64 for msi_addr and split it when composing MSI messages.
> The change is in v4 of the patch set that I just posted.
>>>>
>>>> [...]
>>>>
>>>>>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>>>>>>> +                                 const struct cpumask *mask,
>>>>>>> bool force)
>>>>>>> +{
>>>>>>> +       struct xgene_msi *msi =
>>>>>>> irq_data_get_irq_chip_data(irq_data);
>>>>>>> +       unsigned int gic_irq;
>>>>>>> +       int ret;
>>>>>>> +
>>>>>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq %
>>>>>>> msi->settings->nr_hw_irqs];
>>>>>>> +       ret = irq_set_affinity(gic_irq, mask);
>>>>>>
>>>>>>
>>>>>> Erm... This as the effect of moving *all* the MSIs hanging off
>>>>>> this interrupt to another CPU. I'm not sure that's an acceptable
>>>>>> effect... What if another MSI requires a different affinity?
>>>>>
>>>>>
>>>>> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
>>>>> attached to it.
>>>>> So this will move all MSIs handing off this interrupt to another
>>>>> CPU; and we don't support different affinity settings for
>>>>> different MSIs that are attached to the same hardware IRQ.
>>>>
>>>>
>>>> Well, that's a significant departure from the expected behaviour.
>>>> In other words, I wonder how useful this is. Could you instead
>>>> reconfigure the MSI itself to hit the right CPU (assuming you don't
>>>> have more than 16 CPUs and if
>>>> that's only for XGene-1, this will only be 8 at most)? This would
>>>> reduce your number of possible MSIs, but at least the semantics of
>>>> set_afinity would be preserved.
>>>
>>> X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each
>>> group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have
>>> 16 hardware GIC IRQs for 2688 MSIs).
>>
>> We've already established that.
>>
>>> Setting affinity of single MSI to deliver it to a target CPU will move
>>> all the MSIs mapped to the same GIC IRQ to that CPU as well. This is
>>> not a standard behavior, but limiting the total number of MSIs will
>>> cause a lot of devices to fall back to INTx (with huge performance
>>> penalty) or even fail to load their driver as these devices request
>>> more than 16 MSIs during driver initialization.
>>
>> No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and
>> provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per
>> CPU (having statically assigned 2 IRQs per CPU).
>>
>> Assuming you adopt my scheme, you still have a grand total of 336 MSIs
>> that can be freely moved around without breaking any userspace
>> expectations.
>>
> Thanks Marc. This is a very good idea.
> 
> But to  move MSIs around, I need to change MSI termination address and data
> and write them to device configuration space. This may cause problems
> if the device
> fires an interrupt at the same time when I do the config write?
> 
> What is your opinion here?

There is an inherent race when changing the affinity of any interrupt,
whether that's an MSI or not. The kernel is perfectly prepared to handle
such a situation (to be honest, the kernel doesn't really care).

The write to the config space shouldn't be a concern. You will either
hit the old *or* the new CPU, but that race is only during the write
itself (you can read back from the config space if you're really
paranoid).  By the time you return from this read/write, the device will
be reconfigured.

>> I think that 336 MSIs is a fair number (nowhere near the 16 you claim).
>> Most platforms are doing quite well with that kind of numbers. Also,
>> you don't have to allocate all the MSIs a device can possibly claim (up
>> to 2048 MSI-X per device), as they are all perfectly capable of using
>> less MSI without having to fallback to INTx).
>>
>>> I can document the limitation in affinity setting of X-Gene-1 MSI in
>>> the driver to hopefully not make people surprise and hope to keep the
>>> total number of supported MSI as 2688 so that we can support as many
>>> cards that require MSI/MSI-X as possible.
>>
>> I don't think this is a valid approach. This breaks userspace (think of
>> things like irqbalance), and breaks the SMP affinity model that Linux
>> uses. No amount of documentation is going to solve it, so I think you
>> just have to admit that the HW is mis-designed and do the best you can
>> to make it work like Linux expect it to work.
>>
>> The alternative would to disable affinity setting altogether instead of
>> introducing these horrible side effects.
>>
> I have it disabled (set_affinity does nothing) in my v4 patch.

It would be good if you could try the above approach. It shouldn't be
hard to write, and it would be a lot better than just ignoring the problem.

I'll try to review the patches soon(-ish)...

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-17 10:17                                     ` Marc Zyngier
@ 2015-04-17 12:37                                       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-17 12:37 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Feng Kan, Arnd Bergmann, linux-pci, Liviu Dudau, linux-kernel,
	grant.likely, Tanmay Inamdar, Bjorn Helgaas, linux-arm, Loc Ho

On Fri, Apr 17, 2015 at 3:17 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 17/04/15 11:00, Duc Dang wrote:
>> On Wed, Apr 15, 2015 at 1:16 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>>> On Tue, 14 Apr 2015 19:20:19 +0100
>>> Duc Dang <dhdang@apm.com> wrote:
>>>
>>>> On Sat, Apr 11, 2015 at 5:06 AM, Marc Zyngier <marc.zyngier@arm.com>
>>>> wrote:
>>>>> On 2015-04-11 00:42, Duc Dang wrote:
>>>>>>
>>>>>> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier
>>>>>> <marc.zyngier@arm.com> wrote:
>>>>>>>
>>>>>>> On 09/04/15 18:05, Duc Dang wrote:
>>>>>>>>
>>>>>>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>>>>>>> 16 HW IRQ lines.
>>>>>>>>
>>>>>>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>>>>>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>>> ---
>>>>>>>>  drivers/pci/host/Kconfig         |   6 +
>>>>>>>>  drivers/pci/host/Makefile        |   1 +
>>>>>>>>  drivers/pci/host/pci-xgene-msi.c | 407
>>>>>>>> +++++++++++++++++++++++++++++++++++++++
>>>>>>>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>>>>>>>  4 files changed, 435 insertions(+)
>>>>>>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>>>>>>>
>>>>>>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>>>>>>>> index 7b892a9..c9b61fa 100644
>>>>>>>> --- a/drivers/pci/host/Kconfig
>>>>>>>> +++ b/drivers/pci/host/Kconfig
>>>>>>>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>>>>>>>         depends on ARCH_XGENE
>>>>>>>>         depends on OF
>>>>>>>>         select PCIEPORTBUS
>>>>>>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>>>>>>> +       select PCI_XGENE_MSI if PCI_MSI
>>>>>>>>         help
>>>>>>>>           Say Y here if you want internal PCI support on APM
>>>>>>>> X-Gene SoC. There are 5 internal PCIe ports available. Each port
>>>>>>>> is GEN3 capable
>>>>>>>>           and have varied lanes from x1 to x8.
>>>>>>>>
>>>>>>>> +config PCI_XGENE_MSI
>>>>>>>> +       bool "X-Gene v1 PCIe MSI feature"
>>>>>>>> +       depends on PCI_XGENE && PCI_MSI
>>>>>>>> +
>>>>>>>>  config PCI_LAYERSCAPE
>>>>>>>>         bool "Freescale Layerscape PCIe controller"
>>>>>>>>         depends on OF && ARM
>>>>>>>> diff --git a/drivers/pci/host/Makefile
>>>>>>>> b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644
>>>>>>>> --- a/drivers/pci/host/Makefile
>>>>>>>> +++ b/drivers/pci/host/Makefile
>>>>>>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>>>>>>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>>>>>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>>>>>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>>>>>>> diff --git a/drivers/pci/host/pci-xgene-msi.c
>>>>>>>> b/drivers/pci/host/pci-xgene-msi.c
>>>>>>>> new file mode 100644
>>>>>>>> index 0000000..4f0ff42
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>>>>>>> @@ -0,0 +1,407 @@
>>>>>>>> +/*
>>>>>>>> + * APM X-Gene MSI Driver
>>>>>>>> + *
>>>>>>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>>>>>>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>>> + *        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 as
>>>>>>>> published by the
>>>>>>>> + * Free Software Foundation;  either version 2 of the  License,
>>>>>>>> or (at your
>>>>>>>> + * option) any later version.
>>>>>>>> + *
>>>>>>>> + * 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.
>>>>>>>> + */
>>>>>>>> +#include <linux/interrupt.h>
>>>>>>>> +#include <linux/module.h>
>>>>>>>> +#include <linux/msi.h>
>>>>>>>> +#include <linux/of_irq.h>
>>>>>>>> +#include <linux/pci.h>
>>>>>>>> +#include <linux/platform_device.h>
>>>>>>>> +#include <linux/of_pci.h>
>>>>>>>> +
>>>>>>>> +#define MSI_INDEX0             0x000000
>>>>>>>> +#define MSI_INT0               0x800000
>>>>>>>> +
>>>>>>>> +struct xgene_msi_settings {
>>>>>>>> +       u32     index_per_group;
>>>>>>>> +       u32     irqs_per_index;
>>>>>>>> +       u32     nr_msi_vec;
>>>>>>>> +       u32     nr_hw_irqs;
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +struct xgene_msi {
>>>>>>>> +       struct device_node              *node;
>>>>>>>> +       struct msi_controller           mchip;
>>>>>>>> +       struct irq_domain               *domain;
>>>>>>>> +       struct xgene_msi_settings       *settings;
>>>>>>>> +       u32                             msi_addr_lo;
>>>>>>>> +       u32                             msi_addr_hi;
>>>>>>>
>>>>>>>
>>>>>>> I'd rather see the mailbox address directly, and only do the
>>>>>>> split when assigning it to the message (you seem to play all kind
>>>>>>> of tricks on the address anyway).
>>>>>>
>>>>>>
>>>>>> msi_addr_lo and msi_addr_hi store the physical base address of MSI
>>>>>> controller registers. I will add comment to clarify this.
>>>>>
>>>>>
>>>>> What I mean is that there is no point in keeping this around as a
>>>>> pair of 32bit variables. You'd better keep it as a single 64bit,
>>>>> and do the split when assigning it the the MSI message.
>>>>
>>>> Hi Marc,
>>>>
>>>> These came from device-tree (which describes 64-bit address number as
>>>> 2 32-bit words).
>>>
>>> ... and converted to a resource as a 64bit word, on which you apply
>>> {upper,lower}_32_bit(). So much for DT...
>>>
>>>> If I store them this way, I don't need CPU cycles to do the split
>>>> every time assigning them to the MSI message. Please let me know what
>>>> do you think about it.
>>>
>>> This is getting absolutely silly.
>>>
>>> How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If
>>> it takes so long that it is considered to be a bottleneck, I suggest
>>> you go and design a better CPU (hint: the answer is probably 1 cycle
>>> absolutely everywhere).
>>>
>>> How often are you configuring MSIs in the face of what is happening in
>>> the rest of the kernel? Almost never!
>>>
>>> So, given that "never" times 1 is still never,  I'll consider that
>>> readability of the code trumps it anytime (I can't believe we're having
>>> that kind of conversation...).
>>>
>> I changed to use u64 for msi_addr and split it when composing MSI messages.
>> The change is in v4 of the patch set that I just posted.
>>>>>
>>>>> [...]
>>>>>
>>>>>>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>>>>>>>> +                                 const struct cpumask *mask,
>>>>>>>> bool force)
>>>>>>>> +{
>>>>>>>> +       struct xgene_msi *msi =
>>>>>>>> irq_data_get_irq_chip_data(irq_data);
>>>>>>>> +       unsigned int gic_irq;
>>>>>>>> +       int ret;
>>>>>>>> +
>>>>>>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq %
>>>>>>>> msi->settings->nr_hw_irqs];
>>>>>>>> +       ret = irq_set_affinity(gic_irq, mask);
>>>>>>>
>>>>>>>
>>>>>>> Erm... This as the effect of moving *all* the MSIs hanging off
>>>>>>> this interrupt to another CPU. I'm not sure that's an acceptable
>>>>>>> effect... What if another MSI requires a different affinity?
>>>>>>
>>>>>>
>>>>>> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
>>>>>> attached to it.
>>>>>> So this will move all MSIs handing off this interrupt to another
>>>>>> CPU; and we don't support different affinity settings for
>>>>>> different MSIs that are attached to the same hardware IRQ.
>>>>>
>>>>>
>>>>> Well, that's a significant departure from the expected behaviour.
>>>>> In other words, I wonder how useful this is. Could you instead
>>>>> reconfigure the MSI itself to hit the right CPU (assuming you don't
>>>>> have more than 16 CPUs and if
>>>>> that's only for XGene-1, this will only be 8 at most)? This would
>>>>> reduce your number of possible MSIs, but at least the semantics of
>>>>> set_afinity would be preserved.
>>>>
>>>> X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each
>>>> group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have
>>>> 16 hardware GIC IRQs for 2688 MSIs).
>>>
>>> We've already established that.
>>>
>>>> Setting affinity of single MSI to deliver it to a target CPU will move
>>>> all the MSIs mapped to the same GIC IRQ to that CPU as well. This is
>>>> not a standard behavior, but limiting the total number of MSIs will
>>>> cause a lot of devices to fall back to INTx (with huge performance
>>>> penalty) or even fail to load their driver as these devices request
>>>> more than 16 MSIs during driver initialization.
>>>
>>> No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and
>>> provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per
>>> CPU (having statically assigned 2 IRQs per CPU).
>>>
>>> Assuming you adopt my scheme, you still have a grand total of 336 MSIs
>>> that can be freely moved around without breaking any userspace
>>> expectations.
>>>
>> Thanks Marc. This is a very good idea.
>>
>> But to  move MSIs around, I need to change MSI termination address and data
>> and write them to device configuration space. This may cause problems
>> if the device
>> fires an interrupt at the same time when I do the config write?
>>
>> What is your opinion here?
>
> There is an inherent race when changing the affinity of any interrupt,
> whether that's an MSI or not. The kernel is perfectly prepared to handle
> such a situation (to be honest, the kernel doesn't really care).
>
> The write to the config space shouldn't be a concern. You will either
> hit the old *or* the new CPU, but that race is only during the write
> itself (you can read back from the config space if you're really
> paranoid).  By the time you return from this read/write, the device will
> be reconfigured.
>
>>> I think that 336 MSIs is a fair number (nowhere near the 16 you claim).
>>> Most platforms are doing quite well with that kind of numbers. Also,
>>> you don't have to allocate all the MSIs a device can possibly claim (up
>>> to 2048 MSI-X per device), as they are all perfectly capable of using
>>> less MSI without having to fallback to INTx).
>>>
>>>> I can document the limitation in affinity setting of X-Gene-1 MSI in
>>>> the driver to hopefully not make people surprise and hope to keep the
>>>> total number of supported MSI as 2688 so that we can support as many
>>>> cards that require MSI/MSI-X as possible.
>>>
>>> I don't think this is a valid approach. This breaks userspace (think of
>>> things like irqbalance), and breaks the SMP affinity model that Linux
>>> uses. No amount of documentation is going to solve it, so I think you
>>> just have to admit that the HW is mis-designed and do the best you can
>>> to make it work like Linux expect it to work.
>>>
>>> The alternative would to disable affinity setting altogether instead of
>>> introducing these horrible side effects.
>>>
>> I have it disabled (set_affinity does nothing) in my v4 patch.
>
> It would be good if you could try the above approach. It shouldn't be
> hard to write, and it would be a lot better than just ignoring the problem.
>
Yes. I am working on this change.

> I'll try to review the patches soon(-ish)...
>
>         M.
> --
> Jazz is not dead. It just smells funny...
Regards,
Duc Dang.

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-17 12:37                                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-17 12:37 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 17, 2015 at 3:17 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 17/04/15 11:00, Duc Dang wrote:
>> On Wed, Apr 15, 2015 at 1:16 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>>> On Tue, 14 Apr 2015 19:20:19 +0100
>>> Duc Dang <dhdang@apm.com> wrote:
>>>
>>>> On Sat, Apr 11, 2015 at 5:06 AM, Marc Zyngier <marc.zyngier@arm.com>
>>>> wrote:
>>>>> On 2015-04-11 00:42, Duc Dang wrote:
>>>>>>
>>>>>> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier
>>>>>> <marc.zyngier@arm.com> wrote:
>>>>>>>
>>>>>>> On 09/04/15 18:05, Duc Dang wrote:
>>>>>>>>
>>>>>>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>>>>>>> 16 HW IRQ lines.
>>>>>>>>
>>>>>>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>>>>>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>>> ---
>>>>>>>>  drivers/pci/host/Kconfig         |   6 +
>>>>>>>>  drivers/pci/host/Makefile        |   1 +
>>>>>>>>  drivers/pci/host/pci-xgene-msi.c | 407
>>>>>>>> +++++++++++++++++++++++++++++++++++++++
>>>>>>>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>>>>>>>  4 files changed, 435 insertions(+)
>>>>>>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>>>>>>>
>>>>>>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>>>>>>>> index 7b892a9..c9b61fa 100644
>>>>>>>> --- a/drivers/pci/host/Kconfig
>>>>>>>> +++ b/drivers/pci/host/Kconfig
>>>>>>>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>>>>>>>         depends on ARCH_XGENE
>>>>>>>>         depends on OF
>>>>>>>>         select PCIEPORTBUS
>>>>>>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>>>>>>> +       select PCI_XGENE_MSI if PCI_MSI
>>>>>>>>         help
>>>>>>>>           Say Y here if you want internal PCI support on APM
>>>>>>>> X-Gene SoC. There are 5 internal PCIe ports available. Each port
>>>>>>>> is GEN3 capable
>>>>>>>>           and have varied lanes from x1 to x8.
>>>>>>>>
>>>>>>>> +config PCI_XGENE_MSI
>>>>>>>> +       bool "X-Gene v1 PCIe MSI feature"
>>>>>>>> +       depends on PCI_XGENE && PCI_MSI
>>>>>>>> +
>>>>>>>>  config PCI_LAYERSCAPE
>>>>>>>>         bool "Freescale Layerscape PCIe controller"
>>>>>>>>         depends on OF && ARM
>>>>>>>> diff --git a/drivers/pci/host/Makefile
>>>>>>>> b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644
>>>>>>>> --- a/drivers/pci/host/Makefile
>>>>>>>> +++ b/drivers/pci/host/Makefile
>>>>>>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>>>>>>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>>>>>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>>>>>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>>>>>>> diff --git a/drivers/pci/host/pci-xgene-msi.c
>>>>>>>> b/drivers/pci/host/pci-xgene-msi.c
>>>>>>>> new file mode 100644
>>>>>>>> index 0000000..4f0ff42
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>>>>>>> @@ -0,0 +1,407 @@
>>>>>>>> +/*
>>>>>>>> + * APM X-Gene MSI Driver
>>>>>>>> + *
>>>>>>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>>>>>>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>>> + *        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 as
>>>>>>>> published by the
>>>>>>>> + * Free Software Foundation;  either version 2 of the  License,
>>>>>>>> or (at your
>>>>>>>> + * option) any later version.
>>>>>>>> + *
>>>>>>>> + * 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.
>>>>>>>> + */
>>>>>>>> +#include <linux/interrupt.h>
>>>>>>>> +#include <linux/module.h>
>>>>>>>> +#include <linux/msi.h>
>>>>>>>> +#include <linux/of_irq.h>
>>>>>>>> +#include <linux/pci.h>
>>>>>>>> +#include <linux/platform_device.h>
>>>>>>>> +#include <linux/of_pci.h>
>>>>>>>> +
>>>>>>>> +#define MSI_INDEX0             0x000000
>>>>>>>> +#define MSI_INT0               0x800000
>>>>>>>> +
>>>>>>>> +struct xgene_msi_settings {
>>>>>>>> +       u32     index_per_group;
>>>>>>>> +       u32     irqs_per_index;
>>>>>>>> +       u32     nr_msi_vec;
>>>>>>>> +       u32     nr_hw_irqs;
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +struct xgene_msi {
>>>>>>>> +       struct device_node              *node;
>>>>>>>> +       struct msi_controller           mchip;
>>>>>>>> +       struct irq_domain               *domain;
>>>>>>>> +       struct xgene_msi_settings       *settings;
>>>>>>>> +       u32                             msi_addr_lo;
>>>>>>>> +       u32                             msi_addr_hi;
>>>>>>>
>>>>>>>
>>>>>>> I'd rather see the mailbox address directly, and only do the
>>>>>>> split when assigning it to the message (you seem to play all kind
>>>>>>> of tricks on the address anyway).
>>>>>>
>>>>>>
>>>>>> msi_addr_lo and msi_addr_hi store the physical base address of MSI
>>>>>> controller registers. I will add comment to clarify this.
>>>>>
>>>>>
>>>>> What I mean is that there is no point in keeping this around as a
>>>>> pair of 32bit variables. You'd better keep it as a single 64bit,
>>>>> and do the split when assigning it the the MSI message.
>>>>
>>>> Hi Marc,
>>>>
>>>> These came from device-tree (which describes 64-bit address number as
>>>> 2 32-bit words).
>>>
>>> ... and converted to a resource as a 64bit word, on which you apply
>>> {upper,lower}_32_bit(). So much for DT...
>>>
>>>> If I store them this way, I don't need CPU cycles to do the split
>>>> every time assigning them to the MSI message. Please let me know what
>>>> do you think about it.
>>>
>>> This is getting absolutely silly.
>>>
>>> How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If
>>> it takes so long that it is considered to be a bottleneck, I suggest
>>> you go and design a better CPU (hint: the answer is probably 1 cycle
>>> absolutely everywhere).
>>>
>>> How often are you configuring MSIs in the face of what is happening in
>>> the rest of the kernel? Almost never!
>>>
>>> So, given that "never" times 1 is still never,  I'll consider that
>>> readability of the code trumps it anytime (I can't believe we're having
>>> that kind of conversation...).
>>>
>> I changed to use u64 for msi_addr and split it when composing MSI messages.
>> The change is in v4 of the patch set that I just posted.
>>>>>
>>>>> [...]
>>>>>
>>>>>>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>>>>>>>> +                                 const struct cpumask *mask,
>>>>>>>> bool force)
>>>>>>>> +{
>>>>>>>> +       struct xgene_msi *msi =
>>>>>>>> irq_data_get_irq_chip_data(irq_data);
>>>>>>>> +       unsigned int gic_irq;
>>>>>>>> +       int ret;
>>>>>>>> +
>>>>>>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq %
>>>>>>>> msi->settings->nr_hw_irqs];
>>>>>>>> +       ret = irq_set_affinity(gic_irq, mask);
>>>>>>>
>>>>>>>
>>>>>>> Erm... This as the effect of moving *all* the MSIs hanging off
>>>>>>> this interrupt to another CPU. I'm not sure that's an acceptable
>>>>>>> effect... What if another MSI requires a different affinity?
>>>>>>
>>>>>>
>>>>>> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
>>>>>> attached to it.
>>>>>> So this will move all MSIs handing off this interrupt to another
>>>>>> CPU; and we don't support different affinity settings for
>>>>>> different MSIs that are attached to the same hardware IRQ.
>>>>>
>>>>>
>>>>> Well, that's a significant departure from the expected behaviour.
>>>>> In other words, I wonder how useful this is. Could you instead
>>>>> reconfigure the MSI itself to hit the right CPU (assuming you don't
>>>>> have more than 16 CPUs and if
>>>>> that's only for XGene-1, this will only be 8 at most)? This would
>>>>> reduce your number of possible MSIs, but at least the semantics of
>>>>> set_afinity would be preserved.
>>>>
>>>> X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each
>>>> group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have
>>>> 16 hardware GIC IRQs for 2688 MSIs).
>>>
>>> We've already established that.
>>>
>>>> Setting affinity of single MSI to deliver it to a target CPU will move
>>>> all the MSIs mapped to the same GIC IRQ to that CPU as well. This is
>>>> not a standard behavior, but limiting the total number of MSIs will
>>>> cause a lot of devices to fall back to INTx (with huge performance
>>>> penalty) or even fail to load their driver as these devices request
>>>> more than 16 MSIs during driver initialization.
>>>
>>> No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and
>>> provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per
>>> CPU (having statically assigned 2 IRQs per CPU).
>>>
>>> Assuming you adopt my scheme, you still have a grand total of 336 MSIs
>>> that can be freely moved around without breaking any userspace
>>> expectations.
>>>
>> Thanks Marc. This is a very good idea.
>>
>> But to  move MSIs around, I need to change MSI termination address and data
>> and write them to device configuration space. This may cause problems
>> if the device
>> fires an interrupt at the same time when I do the config write?
>>
>> What is your opinion here?
>
> There is an inherent race when changing the affinity of any interrupt,
> whether that's an MSI or not. The kernel is perfectly prepared to handle
> such a situation (to be honest, the kernel doesn't really care).
>
> The write to the config space shouldn't be a concern. You will either
> hit the old *or* the new CPU, but that race is only during the write
> itself (you can read back from the config space if you're really
> paranoid).  By the time you return from this read/write, the device will
> be reconfigured.
>
>>> I think that 336 MSIs is a fair number (nowhere near the 16 you claim).
>>> Most platforms are doing quite well with that kind of numbers. Also,
>>> you don't have to allocate all the MSIs a device can possibly claim (up
>>> to 2048 MSI-X per device), as they are all perfectly capable of using
>>> less MSI without having to fallback to INTx).
>>>
>>>> I can document the limitation in affinity setting of X-Gene-1 MSI in
>>>> the driver to hopefully not make people surprise and hope to keep the
>>>> total number of supported MSI as 2688 so that we can support as many
>>>> cards that require MSI/MSI-X as possible.
>>>
>>> I don't think this is a valid approach. This breaks userspace (think of
>>> things like irqbalance), and breaks the SMP affinity model that Linux
>>> uses. No amount of documentation is going to solve it, so I think you
>>> just have to admit that the HW is mis-designed and do the best you can
>>> to make it work like Linux expect it to work.
>>>
>>> The alternative would to disable affinity setting altogether instead of
>>> introducing these horrible side effects.
>>>
>> I have it disabled (set_affinity does nothing) in my v4 patch.
>
> It would be good if you could try the above approach. It shouldn't be
> hard to write, and it would be a lot better than just ignoring the problem.
>
Yes. I am working on this change.

> I'll try to review the patches soon(-ish)...
>
>         M.
> --
> Jazz is not dead. It just smells funny...
Regards,
Duc Dang.

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-17 12:37                                       ` Duc Dang
@ 2015-04-17 12:45                                         ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-17 12:45 UTC (permalink / raw)
  To: Duc Dang
  Cc: Feng Kan, Arnd Bergmann, linux-pci, Liviu Dudau, linux-kernel,
	grant.likely, Tanmay Inamdar, Bjorn Helgaas, linux-arm, Loc Ho

On 17/04/15 13:37, Duc Dang wrote:
> On Fri, Apr 17, 2015 at 3:17 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> On 17/04/15 11:00, Duc Dang wrote:
>>> On Wed, Apr 15, 2015 at 1:16 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>>>> On Tue, 14 Apr 2015 19:20:19 +0100
>>>> Duc Dang <dhdang@apm.com> wrote:
>>>>
>>>>> On Sat, Apr 11, 2015 at 5:06 AM, Marc Zyngier <marc.zyngier@arm.com>
>>>>> wrote:
>>>>>> On 2015-04-11 00:42, Duc Dang wrote:
>>>>>>>
>>>>>>> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier
>>>>>>> <marc.zyngier@arm.com> wrote:
>>>>>>>>
>>>>>>>> On 09/04/15 18:05, Duc Dang wrote:
>>>>>>>>>
>>>>>>>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>>>>>>>> 16 HW IRQ lines.
>>>>>>>>>
>>>>>>>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>>>>>>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>>>> ---
>>>>>>>>>  drivers/pci/host/Kconfig         |   6 +
>>>>>>>>>  drivers/pci/host/Makefile        |   1 +
>>>>>>>>>  drivers/pci/host/pci-xgene-msi.c | 407
>>>>>>>>> +++++++++++++++++++++++++++++++++++++++
>>>>>>>>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>>>>>>>>  4 files changed, 435 insertions(+)
>>>>>>>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>>>>>>>>> index 7b892a9..c9b61fa 100644
>>>>>>>>> --- a/drivers/pci/host/Kconfig
>>>>>>>>> +++ b/drivers/pci/host/Kconfig
>>>>>>>>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>>>>>>>>         depends on ARCH_XGENE
>>>>>>>>>         depends on OF
>>>>>>>>>         select PCIEPORTBUS
>>>>>>>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>>>>>>>> +       select PCI_XGENE_MSI if PCI_MSI
>>>>>>>>>         help
>>>>>>>>>           Say Y here if you want internal PCI support on APM
>>>>>>>>> X-Gene SoC. There are 5 internal PCIe ports available. Each port
>>>>>>>>> is GEN3 capable
>>>>>>>>>           and have varied lanes from x1 to x8.
>>>>>>>>>
>>>>>>>>> +config PCI_XGENE_MSI
>>>>>>>>> +       bool "X-Gene v1 PCIe MSI feature"
>>>>>>>>> +       depends on PCI_XGENE && PCI_MSI
>>>>>>>>> +
>>>>>>>>>  config PCI_LAYERSCAPE
>>>>>>>>>         bool "Freescale Layerscape PCIe controller"
>>>>>>>>>         depends on OF && ARM
>>>>>>>>> diff --git a/drivers/pci/host/Makefile
>>>>>>>>> b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644
>>>>>>>>> --- a/drivers/pci/host/Makefile
>>>>>>>>> +++ b/drivers/pci/host/Makefile
>>>>>>>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>>>>>>>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>>>>>>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>>>>>>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>>>>>>>> diff --git a/drivers/pci/host/pci-xgene-msi.c
>>>>>>>>> b/drivers/pci/host/pci-xgene-msi.c
>>>>>>>>> new file mode 100644
>>>>>>>>> index 0000000..4f0ff42
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>>>>>>>> @@ -0,0 +1,407 @@
>>>>>>>>> +/*
>>>>>>>>> + * APM X-Gene MSI Driver
>>>>>>>>> + *
>>>>>>>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>>>>>>>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>>>> + *        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 as
>>>>>>>>> published by the
>>>>>>>>> + * Free Software Foundation;  either version 2 of the  License,
>>>>>>>>> or (at your
>>>>>>>>> + * option) any later version.
>>>>>>>>> + *
>>>>>>>>> + * 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.
>>>>>>>>> + */
>>>>>>>>> +#include <linux/interrupt.h>
>>>>>>>>> +#include <linux/module.h>
>>>>>>>>> +#include <linux/msi.h>
>>>>>>>>> +#include <linux/of_irq.h>
>>>>>>>>> +#include <linux/pci.h>
>>>>>>>>> +#include <linux/platform_device.h>
>>>>>>>>> +#include <linux/of_pci.h>
>>>>>>>>> +
>>>>>>>>> +#define MSI_INDEX0             0x000000
>>>>>>>>> +#define MSI_INT0               0x800000
>>>>>>>>> +
>>>>>>>>> +struct xgene_msi_settings {
>>>>>>>>> +       u32     index_per_group;
>>>>>>>>> +       u32     irqs_per_index;
>>>>>>>>> +       u32     nr_msi_vec;
>>>>>>>>> +       u32     nr_hw_irqs;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +struct xgene_msi {
>>>>>>>>> +       struct device_node              *node;
>>>>>>>>> +       struct msi_controller           mchip;
>>>>>>>>> +       struct irq_domain               *domain;
>>>>>>>>> +       struct xgene_msi_settings       *settings;
>>>>>>>>> +       u32                             msi_addr_lo;
>>>>>>>>> +       u32                             msi_addr_hi;
>>>>>>>>
>>>>>>>>
>>>>>>>> I'd rather see the mailbox address directly, and only do the
>>>>>>>> split when assigning it to the message (you seem to play all kind
>>>>>>>> of tricks on the address anyway).
>>>>>>>
>>>>>>>
>>>>>>> msi_addr_lo and msi_addr_hi store the physical base address of MSI
>>>>>>> controller registers. I will add comment to clarify this.
>>>>>>
>>>>>>
>>>>>> What I mean is that there is no point in keeping this around as a
>>>>>> pair of 32bit variables. You'd better keep it as a single 64bit,
>>>>>> and do the split when assigning it the the MSI message.
>>>>>
>>>>> Hi Marc,
>>>>>
>>>>> These came from device-tree (which describes 64-bit address number as
>>>>> 2 32-bit words).
>>>>
>>>> ... and converted to a resource as a 64bit word, on which you apply
>>>> {upper,lower}_32_bit(). So much for DT...
>>>>
>>>>> If I store them this way, I don't need CPU cycles to do the split
>>>>> every time assigning them to the MSI message. Please let me know what
>>>>> do you think about it.
>>>>
>>>> This is getting absolutely silly.
>>>>
>>>> How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If
>>>> it takes so long that it is considered to be a bottleneck, I suggest
>>>> you go and design a better CPU (hint: the answer is probably 1 cycle
>>>> absolutely everywhere).
>>>>
>>>> How often are you configuring MSIs in the face of what is happening in
>>>> the rest of the kernel? Almost never!
>>>>
>>>> So, given that "never" times 1 is still never,  I'll consider that
>>>> readability of the code trumps it anytime (I can't believe we're having
>>>> that kind of conversation...).
>>>>
>>> I changed to use u64 for msi_addr and split it when composing MSI messages.
>>> The change is in v4 of the patch set that I just posted.
>>>>>>
>>>>>> [...]
>>>>>>
>>>>>>>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>>>>>>>>> +                                 const struct cpumask *mask,
>>>>>>>>> bool force)
>>>>>>>>> +{
>>>>>>>>> +       struct xgene_msi *msi =
>>>>>>>>> irq_data_get_irq_chip_data(irq_data);
>>>>>>>>> +       unsigned int gic_irq;
>>>>>>>>> +       int ret;
>>>>>>>>> +
>>>>>>>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq %
>>>>>>>>> msi->settings->nr_hw_irqs];
>>>>>>>>> +       ret = irq_set_affinity(gic_irq, mask);
>>>>>>>>
>>>>>>>>
>>>>>>>> Erm... This as the effect of moving *all* the MSIs hanging off
>>>>>>>> this interrupt to another CPU. I'm not sure that's an acceptable
>>>>>>>> effect... What if another MSI requires a different affinity?
>>>>>>>
>>>>>>>
>>>>>>> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
>>>>>>> attached to it.
>>>>>>> So this will move all MSIs handing off this interrupt to another
>>>>>>> CPU; and we don't support different affinity settings for
>>>>>>> different MSIs that are attached to the same hardware IRQ.
>>>>>>
>>>>>>
>>>>>> Well, that's a significant departure from the expected behaviour.
>>>>>> In other words, I wonder how useful this is. Could you instead
>>>>>> reconfigure the MSI itself to hit the right CPU (assuming you don't
>>>>>> have more than 16 CPUs and if
>>>>>> that's only for XGene-1, this will only be 8 at most)? This would
>>>>>> reduce your number of possible MSIs, but at least the semantics of
>>>>>> set_afinity would be preserved.
>>>>>
>>>>> X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each
>>>>> group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have
>>>>> 16 hardware GIC IRQs for 2688 MSIs).
>>>>
>>>> We've already established that.
>>>>
>>>>> Setting affinity of single MSI to deliver it to a target CPU will move
>>>>> all the MSIs mapped to the same GIC IRQ to that CPU as well. This is
>>>>> not a standard behavior, but limiting the total number of MSIs will
>>>>> cause a lot of devices to fall back to INTx (with huge performance
>>>>> penalty) or even fail to load their driver as these devices request
>>>>> more than 16 MSIs during driver initialization.
>>>>
>>>> No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and
>>>> provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per
>>>> CPU (having statically assigned 2 IRQs per CPU).
>>>>
>>>> Assuming you adopt my scheme, you still have a grand total of 336 MSIs
>>>> that can be freely moved around without breaking any userspace
>>>> expectations.
>>>>
>>> Thanks Marc. This is a very good idea.
>>>
>>> But to  move MSIs around, I need to change MSI termination address and data
>>> and write them to device configuration space. This may cause problems
>>> if the device
>>> fires an interrupt at the same time when I do the config write?
>>>
>>> What is your opinion here?
>>
>> There is an inherent race when changing the affinity of any interrupt,
>> whether that's an MSI or not. The kernel is perfectly prepared to handle
>> such a situation (to be honest, the kernel doesn't really care).
>>
>> The write to the config space shouldn't be a concern. You will either
>> hit the old *or* the new CPU, but that race is only during the write
>> itself (you can read back from the config space if you're really
>> paranoid).  By the time you return from this read/write, the device will
>> be reconfigured.
>>
>>>> I think that 336 MSIs is a fair number (nowhere near the 16 you claim).
>>>> Most platforms are doing quite well with that kind of numbers. Also,
>>>> you don't have to allocate all the MSIs a device can possibly claim (up
>>>> to 2048 MSI-X per device), as they are all perfectly capable of using
>>>> less MSI without having to fallback to INTx).
>>>>
>>>>> I can document the limitation in affinity setting of X-Gene-1 MSI in
>>>>> the driver to hopefully not make people surprise and hope to keep the
>>>>> total number of supported MSI as 2688 so that we can support as many
>>>>> cards that require MSI/MSI-X as possible.
>>>>
>>>> I don't think this is a valid approach. This breaks userspace (think of
>>>> things like irqbalance), and breaks the SMP affinity model that Linux
>>>> uses. No amount of documentation is going to solve it, so I think you
>>>> just have to admit that the HW is mis-designed and do the best you can
>>>> to make it work like Linux expect it to work.
>>>>
>>>> The alternative would to disable affinity setting altogether instead of
>>>> introducing these horrible side effects.
>>>>
>>> I have it disabled (set_affinity does nothing) in my v4 patch.
>>
>> It would be good if you could try the above approach. It shouldn't be
>> hard to write, and it would be a lot better than just ignoring the problem.
>>
> Yes. I am working on this change.

In which case, I'll wait for a v5. No need to spend time on something
that is going to change anyway.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-17 12:45                                         ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-17 12:45 UTC (permalink / raw)
  To: linux-arm-kernel

On 17/04/15 13:37, Duc Dang wrote:
> On Fri, Apr 17, 2015 at 3:17 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> On 17/04/15 11:00, Duc Dang wrote:
>>> On Wed, Apr 15, 2015 at 1:16 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>>>> On Tue, 14 Apr 2015 19:20:19 +0100
>>>> Duc Dang <dhdang@apm.com> wrote:
>>>>
>>>>> On Sat, Apr 11, 2015 at 5:06 AM, Marc Zyngier <marc.zyngier@arm.com>
>>>>> wrote:
>>>>>> On 2015-04-11 00:42, Duc Dang wrote:
>>>>>>>
>>>>>>> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier
>>>>>>> <marc.zyngier@arm.com> wrote:
>>>>>>>>
>>>>>>>> On 09/04/15 18:05, Duc Dang wrote:
>>>>>>>>>
>>>>>>>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>>>>>>>> 16 HW IRQ lines.
>>>>>>>>>
>>>>>>>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>>>>>>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>>>> ---
>>>>>>>>>  drivers/pci/host/Kconfig         |   6 +
>>>>>>>>>  drivers/pci/host/Makefile        |   1 +
>>>>>>>>>  drivers/pci/host/pci-xgene-msi.c | 407
>>>>>>>>> +++++++++++++++++++++++++++++++++++++++
>>>>>>>>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>>>>>>>>  4 files changed, 435 insertions(+)
>>>>>>>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>>>>>>>>> index 7b892a9..c9b61fa 100644
>>>>>>>>> --- a/drivers/pci/host/Kconfig
>>>>>>>>> +++ b/drivers/pci/host/Kconfig
>>>>>>>>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>>>>>>>>         depends on ARCH_XGENE
>>>>>>>>>         depends on OF
>>>>>>>>>         select PCIEPORTBUS
>>>>>>>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>>>>>>>> +       select PCI_XGENE_MSI if PCI_MSI
>>>>>>>>>         help
>>>>>>>>>           Say Y here if you want internal PCI support on APM
>>>>>>>>> X-Gene SoC. There are 5 internal PCIe ports available. Each port
>>>>>>>>> is GEN3 capable
>>>>>>>>>           and have varied lanes from x1 to x8.
>>>>>>>>>
>>>>>>>>> +config PCI_XGENE_MSI
>>>>>>>>> +       bool "X-Gene v1 PCIe MSI feature"
>>>>>>>>> +       depends on PCI_XGENE && PCI_MSI
>>>>>>>>> +
>>>>>>>>>  config PCI_LAYERSCAPE
>>>>>>>>>         bool "Freescale Layerscape PCIe controller"
>>>>>>>>>         depends on OF && ARM
>>>>>>>>> diff --git a/drivers/pci/host/Makefile
>>>>>>>>> b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644
>>>>>>>>> --- a/drivers/pci/host/Makefile
>>>>>>>>> +++ b/drivers/pci/host/Makefile
>>>>>>>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>>>>>>>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>>>>>>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>>>>>>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>>>>>>>> diff --git a/drivers/pci/host/pci-xgene-msi.c
>>>>>>>>> b/drivers/pci/host/pci-xgene-msi.c
>>>>>>>>> new file mode 100644
>>>>>>>>> index 0000000..4f0ff42
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>>>>>>>> @@ -0,0 +1,407 @@
>>>>>>>>> +/*
>>>>>>>>> + * APM X-Gene MSI Driver
>>>>>>>>> + *
>>>>>>>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>>>>>>>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>>>> + *        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 as
>>>>>>>>> published by the
>>>>>>>>> + * Free Software Foundation;  either version 2 of the  License,
>>>>>>>>> or (at your
>>>>>>>>> + * option) any later version.
>>>>>>>>> + *
>>>>>>>>> + * 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.
>>>>>>>>> + */
>>>>>>>>> +#include <linux/interrupt.h>
>>>>>>>>> +#include <linux/module.h>
>>>>>>>>> +#include <linux/msi.h>
>>>>>>>>> +#include <linux/of_irq.h>
>>>>>>>>> +#include <linux/pci.h>
>>>>>>>>> +#include <linux/platform_device.h>
>>>>>>>>> +#include <linux/of_pci.h>
>>>>>>>>> +
>>>>>>>>> +#define MSI_INDEX0             0x000000
>>>>>>>>> +#define MSI_INT0               0x800000
>>>>>>>>> +
>>>>>>>>> +struct xgene_msi_settings {
>>>>>>>>> +       u32     index_per_group;
>>>>>>>>> +       u32     irqs_per_index;
>>>>>>>>> +       u32     nr_msi_vec;
>>>>>>>>> +       u32     nr_hw_irqs;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +struct xgene_msi {
>>>>>>>>> +       struct device_node              *node;
>>>>>>>>> +       struct msi_controller           mchip;
>>>>>>>>> +       struct irq_domain               *domain;
>>>>>>>>> +       struct xgene_msi_settings       *settings;
>>>>>>>>> +       u32                             msi_addr_lo;
>>>>>>>>> +       u32                             msi_addr_hi;
>>>>>>>>
>>>>>>>>
>>>>>>>> I'd rather see the mailbox address directly, and only do the
>>>>>>>> split when assigning it to the message (you seem to play all kind
>>>>>>>> of tricks on the address anyway).
>>>>>>>
>>>>>>>
>>>>>>> msi_addr_lo and msi_addr_hi store the physical base address of MSI
>>>>>>> controller registers. I will add comment to clarify this.
>>>>>>
>>>>>>
>>>>>> What I mean is that there is no point in keeping this around as a
>>>>>> pair of 32bit variables. You'd better keep it as a single 64bit,
>>>>>> and do the split when assigning it the the MSI message.
>>>>>
>>>>> Hi Marc,
>>>>>
>>>>> These came from device-tree (which describes 64-bit address number as
>>>>> 2 32-bit words).
>>>>
>>>> ... and converted to a resource as a 64bit word, on which you apply
>>>> {upper,lower}_32_bit(). So much for DT...
>>>>
>>>>> If I store them this way, I don't need CPU cycles to do the split
>>>>> every time assigning them to the MSI message. Please let me know what
>>>>> do you think about it.
>>>>
>>>> This is getting absolutely silly.
>>>>
>>>> How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If
>>>> it takes so long that it is considered to be a bottleneck, I suggest
>>>> you go and design a better CPU (hint: the answer is probably 1 cycle
>>>> absolutely everywhere).
>>>>
>>>> How often are you configuring MSIs in the face of what is happening in
>>>> the rest of the kernel? Almost never!
>>>>
>>>> So, given that "never" times 1 is still never,  I'll consider that
>>>> readability of the code trumps it anytime (I can't believe we're having
>>>> that kind of conversation...).
>>>>
>>> I changed to use u64 for msi_addr and split it when composing MSI messages.
>>> The change is in v4 of the patch set that I just posted.
>>>>>>
>>>>>> [...]
>>>>>>
>>>>>>>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>>>>>>>>> +                                 const struct cpumask *mask,
>>>>>>>>> bool force)
>>>>>>>>> +{
>>>>>>>>> +       struct xgene_msi *msi =
>>>>>>>>> irq_data_get_irq_chip_data(irq_data);
>>>>>>>>> +       unsigned int gic_irq;
>>>>>>>>> +       int ret;
>>>>>>>>> +
>>>>>>>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq %
>>>>>>>>> msi->settings->nr_hw_irqs];
>>>>>>>>> +       ret = irq_set_affinity(gic_irq, mask);
>>>>>>>>
>>>>>>>>
>>>>>>>> Erm... This as the effect of moving *all* the MSIs hanging off
>>>>>>>> this interrupt to another CPU. I'm not sure that's an acceptable
>>>>>>>> effect... What if another MSI requires a different affinity?
>>>>>>>
>>>>>>>
>>>>>>> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
>>>>>>> attached to it.
>>>>>>> So this will move all MSIs handing off this interrupt to another
>>>>>>> CPU; and we don't support different affinity settings for
>>>>>>> different MSIs that are attached to the same hardware IRQ.
>>>>>>
>>>>>>
>>>>>> Well, that's a significant departure from the expected behaviour.
>>>>>> In other words, I wonder how useful this is. Could you instead
>>>>>> reconfigure the MSI itself to hit the right CPU (assuming you don't
>>>>>> have more than 16 CPUs and if
>>>>>> that's only for XGene-1, this will only be 8 at most)? This would
>>>>>> reduce your number of possible MSIs, but at least the semantics of
>>>>>> set_afinity would be preserved.
>>>>>
>>>>> X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each
>>>>> group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have
>>>>> 16 hardware GIC IRQs for 2688 MSIs).
>>>>
>>>> We've already established that.
>>>>
>>>>> Setting affinity of single MSI to deliver it to a target CPU will move
>>>>> all the MSIs mapped to the same GIC IRQ to that CPU as well. This is
>>>>> not a standard behavior, but limiting the total number of MSIs will
>>>>> cause a lot of devices to fall back to INTx (with huge performance
>>>>> penalty) or even fail to load their driver as these devices request
>>>>> more than 16 MSIs during driver initialization.
>>>>
>>>> No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and
>>>> provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per
>>>> CPU (having statically assigned 2 IRQs per CPU).
>>>>
>>>> Assuming you adopt my scheme, you still have a grand total of 336 MSIs
>>>> that can be freely moved around without breaking any userspace
>>>> expectations.
>>>>
>>> Thanks Marc. This is a very good idea.
>>>
>>> But to  move MSIs around, I need to change MSI termination address and data
>>> and write them to device configuration space. This may cause problems
>>> if the device
>>> fires an interrupt at the same time when I do the config write?
>>>
>>> What is your opinion here?
>>
>> There is an inherent race when changing the affinity of any interrupt,
>> whether that's an MSI or not. The kernel is perfectly prepared to handle
>> such a situation (to be honest, the kernel doesn't really care).
>>
>> The write to the config space shouldn't be a concern. You will either
>> hit the old *or* the new CPU, but that race is only during the write
>> itself (you can read back from the config space if you're really
>> paranoid).  By the time you return from this read/write, the device will
>> be reconfigured.
>>
>>>> I think that 336 MSIs is a fair number (nowhere near the 16 you claim).
>>>> Most platforms are doing quite well with that kind of numbers. Also,
>>>> you don't have to allocate all the MSIs a device can possibly claim (up
>>>> to 2048 MSI-X per device), as they are all perfectly capable of using
>>>> less MSI without having to fallback to INTx).
>>>>
>>>>> I can document the limitation in affinity setting of X-Gene-1 MSI in
>>>>> the driver to hopefully not make people surprise and hope to keep the
>>>>> total number of supported MSI as 2688 so that we can support as many
>>>>> cards that require MSI/MSI-X as possible.
>>>>
>>>> I don't think this is a valid approach. This breaks userspace (think of
>>>> things like irqbalance), and breaks the SMP affinity model that Linux
>>>> uses. No amount of documentation is going to solve it, so I think you
>>>> just have to admit that the HW is mis-designed and do the best you can
>>>> to make it work like Linux expect it to work.
>>>>
>>>> The alternative would to disable affinity setting altogether instead of
>>>> introducing these horrible side effects.
>>>>
>>> I have it disabled (set_affinity does nothing) in my v4 patch.
>>
>> It would be good if you could try the above approach. It shouldn't be
>> hard to write, and it would be a lot better than just ignoring the problem.
>>
> Yes. I am working on this change.

In which case, I'll wait for a v5. No need to spend time on something
that is going to change anyway.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-17  9:50                                   ` Duc Dang
@ 2015-04-17 14:10                                     ` Arnd Bergmann
  -1 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-04-17 14:10 UTC (permalink / raw)
  To: Duc Dang
  Cc: Bjorn Helgaas, Grant Likely, Liviu Dudau, Marc Zyngier,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Friday 17 April 2015 02:50:07 Duc Dang wrote:
> +
> +       /*
> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> +        * If bit x of this register is set (x is 0..7), one or more interupts
> +        * corresponding to MSInIRx is set.
> +        */
> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               /*
> +                * Calculate MSInIRx address to read to check for interrupts
> +                * (refer to termination address and data assignment
> +                * described in xgene_compose_msi_msg function)
> +                */
> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> +               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       /*
> +                        * Calculate MSI vector number (refer to the termination
> +                        * address and data assignment described in
> +                        * xgene_compose_msi_msg function)
> +                        */
> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> +                                NR_HW_IRQS) + msi_grp;
> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> +                       if (virq != 0)
> +                               generic_handle_irq(virq);
> +                       msir_val &= ~(1 << intr_index);
> +                       processed++;
> +               }
> +               grp_select &= ~(1 << msir_index);
> +       }
> 

As the MSI is forwarded to the GIC here, how do you maintain ordering
between DMA data getting forwarded from the PCI host bridge to RAM
with regard to the MSI handler getting entered from this code?

	Arnd

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

* [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-17 14:10                                     ` Arnd Bergmann
  0 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-04-17 14:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Friday 17 April 2015 02:50:07 Duc Dang wrote:
> +
> +       /*
> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> +        * If bit x of this register is set (x is 0..7), one or more interupts
> +        * corresponding to MSInIRx is set.
> +        */
> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               /*
> +                * Calculate MSInIRx address to read to check for interrupts
> +                * (refer to termination address and data assignment
> +                * described in xgene_compose_msi_msg function)
> +                */
> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> +               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       /*
> +                        * Calculate MSI vector number (refer to the termination
> +                        * address and data assignment described in
> +                        * xgene_compose_msi_msg function)
> +                        */
> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> +                                NR_HW_IRQS) + msi_grp;
> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> +                       if (virq != 0)
> +                               generic_handle_irq(virq);
> +                       msir_val &= ~(1 << intr_index);
> +                       processed++;
> +               }
> +               grp_select &= ~(1 << msir_index);
> +       }
> 

As the MSI is forwarded to the GIC here, how do you maintain ordering
between DMA data getting forwarded from the PCI host bridge to RAM
with regard to the MSI handler getting entered from this code?

	Arnd

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

* Re: [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-17 14:10                                     ` Arnd Bergmann
@ 2015-04-19 18:40                                       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-19 18:40 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Bjorn Helgaas, Grant Likely, Liviu Dudau, Marc Zyngier,
	linux-pci, linux-arm, linux-kernel, Tanmay Inamdar, Loc Ho,
	Feng Kan

On Fri, Apr 17, 2015 at 7:10 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Friday 17 April 2015 02:50:07 Duc Dang wrote:
>> +
>> +       /*
>> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
>> +        * If bit x of this register is set (x is 0..7), one or more interupts
>> +        * corresponding to MSInIRx is set.
>> +        */
>> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +       while (grp_select) {
>> +               msir_index = ffs(grp_select) - 1;
>> +               /*
>> +                * Calculate MSInIRx address to read to check for interrupts
>> +                * (refer to termination address and data assignment
>> +                * described in xgene_compose_msi_msg function)
>> +                */
>> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
>> +               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
>> +               while (msir_val) {
>> +                       intr_index = ffs(msir_val) - 1;
>> +                       /*
>> +                        * Calculate MSI vector number (refer to the termination
>> +                        * address and data assignment described in
>> +                        * xgene_compose_msi_msg function)
>> +                        */
>> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
>> +                                NR_HW_IRQS) + msi_grp;
>> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> +                       if (virq != 0)
>> +                               generic_handle_irq(virq);
>> +                       msir_val &= ~(1 << intr_index);
>> +                       processed++;
>> +               }
>> +               grp_select &= ~(1 << msir_index);
>> +       }
>>
>
> As the MSI is forwarded to the GIC here, how do you maintain ordering
> between DMA data getting forwarded from the PCI host bridge to RAM
> with regard to the MSI handler getting entered from this code?

When device perform a DMA transfer, the order of PCIE inbound requests
will be like this:
1. DMA data get transferred via PCIe inbound request
2. After devices issue DMA transfer request, the device fires an MSI
interrupt by issuing another inbound write to write MSI data to MSI
termination address.

As these 2 requests are transferred via PCIe bus in order, the DMA
data will be all in DDR before the MSI data hit the termination
address to trigger the MSI handler in interrupt handler code.
>
>         Arnd
Regards,
Duc Dang.

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

* [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-19 18:40                                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-19 18:40 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 17, 2015 at 7:10 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Friday 17 April 2015 02:50:07 Duc Dang wrote:
>> +
>> +       /*
>> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
>> +        * If bit x of this register is set (x is 0..7), one or more interupts
>> +        * corresponding to MSInIRx is set.
>> +        */
>> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +       while (grp_select) {
>> +               msir_index = ffs(grp_select) - 1;
>> +               /*
>> +                * Calculate MSInIRx address to read to check for interrupts
>> +                * (refer to termination address and data assignment
>> +                * described in xgene_compose_msi_msg function)
>> +                */
>> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
>> +               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
>> +               while (msir_val) {
>> +                       intr_index = ffs(msir_val) - 1;
>> +                       /*
>> +                        * Calculate MSI vector number (refer to the termination
>> +                        * address and data assignment described in
>> +                        * xgene_compose_msi_msg function)
>> +                        */
>> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
>> +                                NR_HW_IRQS) + msi_grp;
>> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> +                       if (virq != 0)
>> +                               generic_handle_irq(virq);
>> +                       msir_val &= ~(1 << intr_index);
>> +                       processed++;
>> +               }
>> +               grp_select &= ~(1 << msir_index);
>> +       }
>>
>
> As the MSI is forwarded to the GIC here, how do you maintain ordering
> between DMA data getting forwarded from the PCI host bridge to RAM
> with regard to the MSI handler getting entered from this code?

When device perform a DMA transfer, the order of PCIE inbound requests
will be like this:
1. DMA data get transferred via PCIe inbound request
2. After devices issue DMA transfer request, the device fires an MSI
interrupt by issuing another inbound write to write MSI data to MSI
termination address.

As these 2 requests are transferred via PCIe bus in order, the DMA
data will be all in DDR before the MSI data hit the termination
address to trigger the MSI handler in interrupt handler code.
>
>         Arnd
Regards,
Duc Dang.

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

* Re: [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-19 18:40                                       ` Duc Dang
@ 2015-04-19 19:55                                         ` Arnd Bergmann
  -1 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-04-19 19:55 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Duc Dang, Feng Kan, Marc Zyngier, linux-pci, Liviu Dudau,
	linux-kernel, Grant Likely, Tanmay Inamdar, Bjorn Helgaas,
	Loc Ho

On Sunday 19 April 2015 11:40:09 Duc Dang wrote:
> On Fri, Apr 17, 2015 at 7:10 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> > On Friday 17 April 2015 02:50:07 Duc Dang wrote:
> >> +
> >> +       /*
> >> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> >> +        * If bit x of this register is set (x is 0..7), one or more interupts
> >> +        * corresponding to MSInIRx is set.
> >> +        */
> >> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> >> +       while (grp_select) {
> >> +               msir_index = ffs(grp_select) - 1;
> >> +               /*
> >> +                * Calculate MSInIRx address to read to check for interrupts
> >> +                * (refer to termination address and data assignment
> >> +                * described in xgene_compose_msi_msg function)
> >> +                */
> >> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> >> +               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
> >> +               while (msir_val) {
> >> +                       intr_index = ffs(msir_val) - 1;
> >> +                       /*
> >> +                        * Calculate MSI vector number (refer to the termination
> >> +                        * address and data assignment described in
> >> +                        * xgene_compose_msi_msg function)
> >> +                        */
> >> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> >> +                                NR_HW_IRQS) + msi_grp;
> >> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> >> +                       if (virq != 0)
> >> +                               generic_handle_irq(virq);
> >> +                       msir_val &= ~(1 << intr_index);
> >> +                       processed++;
> >> +               }
> >> +               grp_select &= ~(1 << msir_index);
> >> +       }
> >>
> >
> > As the MSI is forwarded to the GIC here, how do you maintain ordering
> > between DMA data getting forwarded from the PCI host bridge to RAM
> > with regard to the MSI handler getting entered from this code?
> 
> When device perform a DMA transfer, the order of PCIE inbound requests
> will be like this:
> 1. DMA data get transferred via PCIe inbound request
> 2. After devices issue DMA transfer request, the device fires an MSI
> interrupt by issuing another inbound write to write MSI data to MSI
> termination address.
> 
> As these 2 requests are transferred via PCIe bus in order, the DMA
> data will be all in DDR before the MSI data hit the termination
> address to trigger the MSI handler in interrupt handler code.

Obviously they appear on the PCI host bridge in order, because that
is a how PCI works. My question was about what happens then. On a lot
of SoCs, there is something like an AXI bus that uses posted
transactions between PCI and RAM, so you have a do a full manual
syncronization of ongoing PIC DMAs when the MSI catcher signals the
top-level interrupt. Do you have a bus between PCI and RAM that does
not require this, or does the MSI catcher have logic to flush all DMAs?

	Arnd

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

* [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-19 19:55                                         ` Arnd Bergmann
  0 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-04-19 19:55 UTC (permalink / raw)
  To: linux-arm-kernel

On Sunday 19 April 2015 11:40:09 Duc Dang wrote:
> On Fri, Apr 17, 2015 at 7:10 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> > On Friday 17 April 2015 02:50:07 Duc Dang wrote:
> >> +
> >> +       /*
> >> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> >> +        * If bit x of this register is set (x is 0..7), one or more interupts
> >> +        * corresponding to MSInIRx is set.
> >> +        */
> >> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> >> +       while (grp_select) {
> >> +               msir_index = ffs(grp_select) - 1;
> >> +               /*
> >> +                * Calculate MSInIRx address to read to check for interrupts
> >> +                * (refer to termination address and data assignment
> >> +                * described in xgene_compose_msi_msg function)
> >> +                */
> >> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> >> +               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
> >> +               while (msir_val) {
> >> +                       intr_index = ffs(msir_val) - 1;
> >> +                       /*
> >> +                        * Calculate MSI vector number (refer to the termination
> >> +                        * address and data assignment described in
> >> +                        * xgene_compose_msi_msg function)
> >> +                        */
> >> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> >> +                                NR_HW_IRQS) + msi_grp;
> >> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> >> +                       if (virq != 0)
> >> +                               generic_handle_irq(virq);
> >> +                       msir_val &= ~(1 << intr_index);
> >> +                       processed++;
> >> +               }
> >> +               grp_select &= ~(1 << msir_index);
> >> +       }
> >>
> >
> > As the MSI is forwarded to the GIC here, how do you maintain ordering
> > between DMA data getting forwarded from the PCI host bridge to RAM
> > with regard to the MSI handler getting entered from this code?
> 
> When device perform a DMA transfer, the order of PCIE inbound requests
> will be like this:
> 1. DMA data get transferred via PCIe inbound request
> 2. After devices issue DMA transfer request, the device fires an MSI
> interrupt by issuing another inbound write to write MSI data to MSI
> termination address.
> 
> As these 2 requests are transferred via PCIe bus in order, the DMA
> data will be all in DDR before the MSI data hit the termination
> address to trigger the MSI handler in interrupt handler code.

Obviously they appear on the PCI host bridge in order, because that
is a how PCI works. My question was about what happens then. On a lot
of SoCs, there is something like an AXI bus that uses posted
transactions between PCI and RAM, so you have a do a full manual
syncronization of ongoing PIC DMAs when the MSI catcher signals the
top-level interrupt. Do you have a bus between PCI and RAM that does
not require this, or does the MSI catcher have logic to flush all DMAs?

	Arnd

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

* Re: [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-19 19:55                                         ` Arnd Bergmann
  (?)
@ 2015-04-20 18:49                                           ` Feng Kan
  -1 siblings, 0 replies; 230+ messages in thread
From: Feng Kan @ 2015-04-20 18:49 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel, Duc Dang, Marc Zyngier, linux-pci, Liviu Dudau,
	linux-kernel, Grant Likely, Tanmay Inamdar, Bjorn Helgaas,
	Loc Ho

On Sun, Apr 19, 2015 at 12:55 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Sunday 19 April 2015 11:40:09 Duc Dang wrote:
>> On Fri, Apr 17, 2015 at 7:10 AM, Arnd Bergmann <arnd@arndb.de> wrote:
>> > On Friday 17 April 2015 02:50:07 Duc Dang wrote:
>> >> +
>> >> +       /*
>> >> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
>> >> +        * If bit x of this register is set (x is 0..7), one or more interupts
>> >> +        * corresponding to MSInIRx is set.
>> >> +        */
>> >> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> >> +       while (grp_select) {
>> >> +               msir_index = ffs(grp_select) - 1;
>> >> +               /*
>> >> +                * Calculate MSInIRx address to read to check for interrupts
>> >> +                * (refer to termination address and data assignment
>> >> +                * described in xgene_compose_msi_msg function)
>> >> +                */
>> >> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
>> >> +               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
>> >> +               while (msir_val) {
>> >> +                       intr_index = ffs(msir_val) - 1;
>> >> +                       /*
>> >> +                        * Calculate MSI vector number (refer to the termination
>> >> +                        * address and data assignment described in
>> >> +                        * xgene_compose_msi_msg function)
>> >> +                        */
>> >> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
>> >> +                                NR_HW_IRQS) + msi_grp;
>> >> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> >> +                       if (virq != 0)
>> >> +                               generic_handle_irq(virq);
>> >> +                       msir_val &= ~(1 << intr_index);
>> >> +                       processed++;
>> >> +               }
>> >> +               grp_select &= ~(1 << msir_index);
>> >> +       }
>> >>
>> >
>> > As the MSI is forwarded to the GIC here, how do you maintain ordering
>> > between DMA data getting forwarded from the PCI host bridge to RAM
>> > with regard to the MSI handler getting entered from this code?
>>
>> When device perform a DMA transfer, the order of PCIE inbound requests
>> will be like this:
>> 1. DMA data get transferred via PCIe inbound request
>> 2. After devices issue DMA transfer request, the device fires an MSI
>> interrupt by issuing another inbound write to write MSI data to MSI
>> termination address.
>>
>> As these 2 requests are transferred via PCIe bus in order, the DMA
>> data will be all in DDR before the MSI data hit the termination
>> address to trigger the MSI handler in interrupt handler code.
>
> Obviously they appear on the PCI host bridge in order, because that
> is a how PCI works. My question was about what happens then. On a lot
> of SoCs, there is something like an AXI bus that uses posted
> transactions between PCI and RAM, so you have a do a full manual
> syncronization of ongoing PIC DMAs when the MSI catcher signals the
> top-level interrupt. Do you have a bus between PCI and RAM that does
> not require this, or does the MSI catcher have logic to flush all DMAs?

Our hardware has an automatic mechanism to flush the content to DRAM before the
MSI write is committed.

>
>         Arnd
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-20 18:49                                           ` Feng Kan
  0 siblings, 0 replies; 230+ messages in thread
From: Feng Kan @ 2015-04-20 18:49 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel, Duc Dang, Marc Zyngier, linux-pci, Liviu Dudau,
	linux-kernel, Grant Likely, Tanmay Inamdar, Bjorn Helgaas,
	Loc Ho

On Sun, Apr 19, 2015 at 12:55 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Sunday 19 April 2015 11:40:09 Duc Dang wrote:
>> On Fri, Apr 17, 2015 at 7:10 AM, Arnd Bergmann <arnd@arndb.de> wrote:
>> > On Friday 17 April 2015 02:50:07 Duc Dang wrote:
>> >> +
>> >> +       /*
>> >> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
>> >> +        * If bit x of this register is set (x is 0..7), one or more interupts
>> >> +        * corresponding to MSInIRx is set.
>> >> +        */
>> >> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> >> +       while (grp_select) {
>> >> +               msir_index = ffs(grp_select) - 1;
>> >> +               /*
>> >> +                * Calculate MSInIRx address to read to check for interrupts
>> >> +                * (refer to termination address and data assignment
>> >> +                * described in xgene_compose_msi_msg function)
>> >> +                */
>> >> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
>> >> +               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
>> >> +               while (msir_val) {
>> >> +                       intr_index = ffs(msir_val) - 1;
>> >> +                       /*
>> >> +                        * Calculate MSI vector number (refer to the termination
>> >> +                        * address and data assignment described in
>> >> +                        * xgene_compose_msi_msg function)
>> >> +                        */
>> >> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
>> >> +                                NR_HW_IRQS) + msi_grp;
>> >> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> >> +                       if (virq != 0)
>> >> +                               generic_handle_irq(virq);
>> >> +                       msir_val &= ~(1 << intr_index);
>> >> +                       processed++;
>> >> +               }
>> >> +               grp_select &= ~(1 << msir_index);
>> >> +       }
>> >>
>> >
>> > As the MSI is forwarded to the GIC here, how do you maintain ordering
>> > between DMA data getting forwarded from the PCI host bridge to RAM
>> > with regard to the MSI handler getting entered from this code?
>>
>> When device perform a DMA transfer, the order of PCIE inbound requests
>> will be like this:
>> 1. DMA data get transferred via PCIe inbound request
>> 2. After devices issue DMA transfer request, the device fires an MSI
>> interrupt by issuing another inbound write to write MSI data to MSI
>> termination address.
>>
>> As these 2 requests are transferred via PCIe bus in order, the DMA
>> data will be all in DDR before the MSI data hit the termination
>> address to trigger the MSI handler in interrupt handler code.
>
> Obviously they appear on the PCI host bridge in order, because that
> is a how PCI works. My question was about what happens then. On a lot
> of SoCs, there is something like an AXI bus that uses posted
> transactions between PCI and RAM, so you have a do a full manual
> syncronization of ongoing PIC DMAs when the MSI catcher signals the
> top-level interrupt. Do you have a bus between PCI and RAM that does
> not require this, or does the MSI catcher have logic to flush all DMAs?

Our hardware has an automatic mechanism to flush the content to DRAM before the
MSI write is committed.

>
>         Arnd
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-20 18:49                                           ` Feng Kan
  0 siblings, 0 replies; 230+ messages in thread
From: Feng Kan @ 2015-04-20 18:49 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Apr 19, 2015 at 12:55 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Sunday 19 April 2015 11:40:09 Duc Dang wrote:
>> On Fri, Apr 17, 2015 at 7:10 AM, Arnd Bergmann <arnd@arndb.de> wrote:
>> > On Friday 17 April 2015 02:50:07 Duc Dang wrote:
>> >> +
>> >> +       /*
>> >> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
>> >> +        * If bit x of this register is set (x is 0..7), one or more interupts
>> >> +        * corresponding to MSInIRx is set.
>> >> +        */
>> >> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> >> +       while (grp_select) {
>> >> +               msir_index = ffs(grp_select) - 1;
>> >> +               /*
>> >> +                * Calculate MSInIRx address to read to check for interrupts
>> >> +                * (refer to termination address and data assignment
>> >> +                * described in xgene_compose_msi_msg function)
>> >> +                */
>> >> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
>> >> +               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
>> >> +               while (msir_val) {
>> >> +                       intr_index = ffs(msir_val) - 1;
>> >> +                       /*
>> >> +                        * Calculate MSI vector number (refer to the termination
>> >> +                        * address and data assignment described in
>> >> +                        * xgene_compose_msi_msg function)
>> >> +                        */
>> >> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
>> >> +                                NR_HW_IRQS) + msi_grp;
>> >> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> >> +                       if (virq != 0)
>> >> +                               generic_handle_irq(virq);
>> >> +                       msir_val &= ~(1 << intr_index);
>> >> +                       processed++;
>> >> +               }
>> >> +               grp_select &= ~(1 << msir_index);
>> >> +       }
>> >>
>> >
>> > As the MSI is forwarded to the GIC here, how do you maintain ordering
>> > between DMA data getting forwarded from the PCI host bridge to RAM
>> > with regard to the MSI handler getting entered from this code?
>>
>> When device perform a DMA transfer, the order of PCIE inbound requests
>> will be like this:
>> 1. DMA data get transferred via PCIe inbound request
>> 2. After devices issue DMA transfer request, the device fires an MSI
>> interrupt by issuing another inbound write to write MSI data to MSI
>> termination address.
>>
>> As these 2 requests are transferred via PCIe bus in order, the DMA
>> data will be all in DDR before the MSI data hit the termination
>> address to trigger the MSI handler in interrupt handler code.
>
> Obviously they appear on the PCI host bridge in order, because that
> is a how PCI works. My question was about what happens then. On a lot
> of SoCs, there is something like an AXI bus that uses posted
> transactions between PCI and RAM, so you have a do a full manual
> syncronization of ongoing PIC DMAs when the MSI catcher signals the
> top-level interrupt. Do you have a bus between PCI and RAM that does
> not require this, or does the MSI catcher have logic to flush all DMAs?

Our hardware has an automatic mechanism to flush the content to DRAM before the
MSI write is committed.

>
>         Arnd
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-17 12:45                                         ` Marc Zyngier
@ 2015-04-20 18:51                                           ` Feng Kan
  -1 siblings, 0 replies; 230+ messages in thread
From: Feng Kan @ 2015-04-20 18:51 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Duc Dang, Arnd Bergmann, linux-pci, Liviu Dudau, linux-kernel,
	grant.likely, Tanmay Inamdar, Bjorn Helgaas, linux-arm, Loc Ho

On Fri, Apr 17, 2015 at 5:45 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 17/04/15 13:37, Duc Dang wrote:
>> On Fri, Apr 17, 2015 at 3:17 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>>> On 17/04/15 11:00, Duc Dang wrote:
>>>> On Wed, Apr 15, 2015 at 1:16 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>>>>> On Tue, 14 Apr 2015 19:20:19 +0100
>>>>> Duc Dang <dhdang@apm.com> wrote:
>>>>>
>>>>>> On Sat, Apr 11, 2015 at 5:06 AM, Marc Zyngier <marc.zyngier@arm.com>
>>>>>> wrote:
>>>>>>> On 2015-04-11 00:42, Duc Dang wrote:
>>>>>>>>
>>>>>>>> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier
>>>>>>>> <marc.zyngier@arm.com> wrote:
>>>>>>>>>
>>>>>>>>> On 09/04/15 18:05, Duc Dang wrote:
>>>>>>>>>>
>>>>>>>>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>>>>>>>>> 16 HW IRQ lines.
>>>>>>>>>>
>>>>>>>>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>>>>>>>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>>>>> ---
>>>>>>>>>>  drivers/pci/host/Kconfig         |   6 +
>>>>>>>>>>  drivers/pci/host/Makefile        |   1 +
>>>>>>>>>>  drivers/pci/host/pci-xgene-msi.c | 407
>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>>>>>>>>>  4 files changed, 435 insertions(+)
>>>>>>>>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>>>>>>>>>
>>>>>>>>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>>>>>>>>>> index 7b892a9..c9b61fa 100644
>>>>>>>>>> --- a/drivers/pci/host/Kconfig
>>>>>>>>>> +++ b/drivers/pci/host/Kconfig
>>>>>>>>>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>>>>>>>>>         depends on ARCH_XGENE
>>>>>>>>>>         depends on OF
>>>>>>>>>>         select PCIEPORTBUS
>>>>>>>>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>>>>>>>>> +       select PCI_XGENE_MSI if PCI_MSI
>>>>>>>>>>         help
>>>>>>>>>>           Say Y here if you want internal PCI support on APM
>>>>>>>>>> X-Gene SoC. There are 5 internal PCIe ports available. Each port
>>>>>>>>>> is GEN3 capable
>>>>>>>>>>           and have varied lanes from x1 to x8.
>>>>>>>>>>
>>>>>>>>>> +config PCI_XGENE_MSI
>>>>>>>>>> +       bool "X-Gene v1 PCIe MSI feature"
>>>>>>>>>> +       depends on PCI_XGENE && PCI_MSI
>>>>>>>>>> +
>>>>>>>>>>  config PCI_LAYERSCAPE
>>>>>>>>>>         bool "Freescale Layerscape PCIe controller"
>>>>>>>>>>         depends on OF && ARM
>>>>>>>>>> diff --git a/drivers/pci/host/Makefile
>>>>>>>>>> b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644
>>>>>>>>>> --- a/drivers/pci/host/Makefile
>>>>>>>>>> +++ b/drivers/pci/host/Makefile
>>>>>>>>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>>>>>>>>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>>>>>>>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>>>>>>>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>>>>>>>>> diff --git a/drivers/pci/host/pci-xgene-msi.c
>>>>>>>>>> b/drivers/pci/host/pci-xgene-msi.c
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 0000000..4f0ff42
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>>>>>>>>> @@ -0,0 +1,407 @@
>>>>>>>>>> +/*
>>>>>>>>>> + * APM X-Gene MSI Driver
>>>>>>>>>> + *
>>>>>>>>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>>>>>>>>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>>>>> + *        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 as
>>>>>>>>>> published by the
>>>>>>>>>> + * Free Software Foundation;  either version 2 of the  License,
>>>>>>>>>> or (at your
>>>>>>>>>> + * option) any later version.
>>>>>>>>>> + *
>>>>>>>>>> + * 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.
>>>>>>>>>> + */
>>>>>>>>>> +#include <linux/interrupt.h>
>>>>>>>>>> +#include <linux/module.h>
>>>>>>>>>> +#include <linux/msi.h>
>>>>>>>>>> +#include <linux/of_irq.h>
>>>>>>>>>> +#include <linux/pci.h>
>>>>>>>>>> +#include <linux/platform_device.h>
>>>>>>>>>> +#include <linux/of_pci.h>
>>>>>>>>>> +
>>>>>>>>>> +#define MSI_INDEX0             0x000000
>>>>>>>>>> +#define MSI_INT0               0x800000
>>>>>>>>>> +
>>>>>>>>>> +struct xgene_msi_settings {
>>>>>>>>>> +       u32     index_per_group;
>>>>>>>>>> +       u32     irqs_per_index;
>>>>>>>>>> +       u32     nr_msi_vec;
>>>>>>>>>> +       u32     nr_hw_irqs;
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +struct xgene_msi {
>>>>>>>>>> +       struct device_node              *node;
>>>>>>>>>> +       struct msi_controller           mchip;
>>>>>>>>>> +       struct irq_domain               *domain;
>>>>>>>>>> +       struct xgene_msi_settings       *settings;
>>>>>>>>>> +       u32                             msi_addr_lo;
>>>>>>>>>> +       u32                             msi_addr_hi;
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> I'd rather see the mailbox address directly, and only do the
>>>>>>>>> split when assigning it to the message (you seem to play all kind
>>>>>>>>> of tricks on the address anyway).
>>>>>>>>
>>>>>>>>
>>>>>>>> msi_addr_lo and msi_addr_hi store the physical base address of MSI
>>>>>>>> controller registers. I will add comment to clarify this.
>>>>>>>
>>>>>>>
>>>>>>> What I mean is that there is no point in keeping this around as a
>>>>>>> pair of 32bit variables. You'd better keep it as a single 64bit,
>>>>>>> and do the split when assigning it the the MSI message.
>>>>>>
>>>>>> Hi Marc,
>>>>>>
>>>>>> These came from device-tree (which describes 64-bit address number as
>>>>>> 2 32-bit words).
>>>>>
>>>>> ... and converted to a resource as a 64bit word, on which you apply
>>>>> {upper,lower}_32_bit(). So much for DT...
>>>>>
>>>>>> If I store them this way, I don't need CPU cycles to do the split
>>>>>> every time assigning them to the MSI message. Please let me know what
>>>>>> do you think about it.
>>>>>
>>>>> This is getting absolutely silly.
>>>>>
>>>>> How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If
>>>>> it takes so long that it is considered to be a bottleneck, I suggest
>>>>> you go and design a better CPU (hint: the answer is probably 1 cycle
>>>>> absolutely everywhere).
>>>>>
>>>>> How often are you configuring MSIs in the face of what is happening in
>>>>> the rest of the kernel? Almost never!
>>>>>
>>>>> So, given that "never" times 1 is still never,  I'll consider that
>>>>> readability of the code trumps it anytime (I can't believe we're having
>>>>> that kind of conversation...).
>>>>>
>>>> I changed to use u64 for msi_addr and split it when composing MSI messages.
>>>> The change is in v4 of the patch set that I just posted.
>>>>>>>
>>>>>>> [...]
>>>>>>>
>>>>>>>>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>>>>>>>>>> +                                 const struct cpumask *mask,
>>>>>>>>>> bool force)
>>>>>>>>>> +{
>>>>>>>>>> +       struct xgene_msi *msi =
>>>>>>>>>> irq_data_get_irq_chip_data(irq_data);
>>>>>>>>>> +       unsigned int gic_irq;
>>>>>>>>>> +       int ret;
>>>>>>>>>> +
>>>>>>>>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq %
>>>>>>>>>> msi->settings->nr_hw_irqs];
>>>>>>>>>> +       ret = irq_set_affinity(gic_irq, mask);
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Erm... This as the effect of moving *all* the MSIs hanging off
>>>>>>>>> this interrupt to another CPU. I'm not sure that's an acceptable
>>>>>>>>> effect... What if another MSI requires a different affinity?
>>>>>>>>
>>>>>>>>
>>>>>>>> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
>>>>>>>> attached to it.
>>>>>>>> So this will move all MSIs handing off this interrupt to another
>>>>>>>> CPU; and we don't support different affinity settings for
>>>>>>>> different MSIs that are attached to the same hardware IRQ.
>>>>>>>
>>>>>>>
>>>>>>> Well, that's a significant departure from the expected behaviour.
>>>>>>> In other words, I wonder how useful this is. Could you instead
>>>>>>> reconfigure the MSI itself to hit the right CPU (assuming you don't
>>>>>>> have more than 16 CPUs and if
>>>>>>> that's only for XGene-1, this will only be 8 at most)? This would
>>>>>>> reduce your number of possible MSIs, but at least the semantics of
>>>>>>> set_afinity would be preserved.
>>>>>>
>>>>>> X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each
>>>>>> group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have
>>>>>> 16 hardware GIC IRQs for 2688 MSIs).
>>>>>
>>>>> We've already established that.
>>>>>
>>>>>> Setting affinity of single MSI to deliver it to a target CPU will move
>>>>>> all the MSIs mapped to the same GIC IRQ to that CPU as well. This is
>>>>>> not a standard behavior, but limiting the total number of MSIs will
>>>>>> cause a lot of devices to fall back to INTx (with huge performance
>>>>>> penalty) or even fail to load their driver as these devices request
>>>>>> more than 16 MSIs during driver initialization.
>>>>>
>>>>> No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and
>>>>> provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per
>>>>> CPU (having statically assigned 2 IRQs per CPU).
>>>>>
>>>>> Assuming you adopt my scheme, you still have a grand total of 336 MSIs
>>>>> that can be freely moved around without breaking any userspace
>>>>> expectations.
>>>>>
>>>> Thanks Marc. This is a very good idea.
>>>>
>>>> But to  move MSIs around, I need to change MSI termination address and data
>>>> and write them to device configuration space. This may cause problems
>>>> if the device
>>>> fires an interrupt at the same time when I do the config write?
>>>>
>>>> What is your opinion here?
>>>
>>> There is an inherent race when changing the affinity of any interrupt,
>>> whether that's an MSI or not. The kernel is perfectly prepared to handle
>>> such a situation (to be honest, the kernel doesn't really care).
>>>
>>> The write to the config space shouldn't be a concern. You will either
>>> hit the old *or* the new CPU, but that race is only during the write
>>> itself (you can read back from the config space if you're really
>>> paranoid).  By the time you return from this read/write, the device will
>>> be reconfigured.
>>>
>>>>> I think that 336 MSIs is a fair number (nowhere near the 16 you claim).
>>>>> Most platforms are doing quite well with that kind of numbers. Also,
>>>>> you don't have to allocate all the MSIs a device can possibly claim (up
>>>>> to 2048 MSI-X per device), as they are all perfectly capable of using
>>>>> less MSI without having to fallback to INTx).
>>>>>
>>>>>> I can document the limitation in affinity setting of X-Gene-1 MSI in
>>>>>> the driver to hopefully not make people surprise and hope to keep the
>>>>>> total number of supported MSI as 2688 so that we can support as many
>>>>>> cards that require MSI/MSI-X as possible.
>>>>>
>>>>> I don't think this is a valid approach. This breaks userspace (think of
>>>>> things like irqbalance), and breaks the SMP affinity model that Linux
>>>>> uses. No amount of documentation is going to solve it, so I think you
>>>>> just have to admit that the HW is mis-designed and do the best you can
>>>>> to make it work like Linux expect it to work.
>>>>>
>>>>> The alternative would to disable affinity setting altogether instead of
>>>>> introducing these horrible side effects.
>>>>>
>>>> I have it disabled (set_affinity does nothing) in my v4 patch.
>>>
>>> It would be good if you could try the above approach. It shouldn't be
>>> hard to write, and it would be a lot better than just ignoring the problem.
>>>
>> Yes. I am working on this change.
>
> In which case, I'll wait for a v5. No need to spend time on something
> that is going to change anyway.

Marc:

Is it possible to use the pci_msi_create_default_irq_domain for the X-Gene MSI
driver. Or is this going to be removed in the future. Thanks

>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-20 18:51                                           ` Feng Kan
  0 siblings, 0 replies; 230+ messages in thread
From: Feng Kan @ 2015-04-20 18:51 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 17, 2015 at 5:45 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 17/04/15 13:37, Duc Dang wrote:
>> On Fri, Apr 17, 2015 at 3:17 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>>> On 17/04/15 11:00, Duc Dang wrote:
>>>> On Wed, Apr 15, 2015 at 1:16 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>>>>> On Tue, 14 Apr 2015 19:20:19 +0100
>>>>> Duc Dang <dhdang@apm.com> wrote:
>>>>>
>>>>>> On Sat, Apr 11, 2015 at 5:06 AM, Marc Zyngier <marc.zyngier@arm.com>
>>>>>> wrote:
>>>>>>> On 2015-04-11 00:42, Duc Dang wrote:
>>>>>>>>
>>>>>>>> On Fri, Apr 10, 2015 at 10:20 AM, Marc Zyngier
>>>>>>>> <marc.zyngier@arm.com> wrote:
>>>>>>>>>
>>>>>>>>> On 09/04/15 18:05, Duc Dang wrote:
>>>>>>>>>>
>>>>>>>>>> X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into
>>>>>>>>>> 16 HW IRQ lines.
>>>>>>>>>>
>>>>>>>>>> Signed-off-by: Duc Dang <dhdang@apm.com>
>>>>>>>>>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>>>>> ---
>>>>>>>>>>  drivers/pci/host/Kconfig         |   6 +
>>>>>>>>>>  drivers/pci/host/Makefile        |   1 +
>>>>>>>>>>  drivers/pci/host/pci-xgene-msi.c | 407
>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>>>>>>>>>  4 files changed, 435 insertions(+)
>>>>>>>>>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>>>>>>>>>
>>>>>>>>>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>>>>>>>>>> index 7b892a9..c9b61fa 100644
>>>>>>>>>> --- a/drivers/pci/host/Kconfig
>>>>>>>>>> +++ b/drivers/pci/host/Kconfig
>>>>>>>>>> @@ -89,11 +89,17 @@ config PCI_XGENE
>>>>>>>>>>         depends on ARCH_XGENE
>>>>>>>>>>         depends on OF
>>>>>>>>>>         select PCIEPORTBUS
>>>>>>>>>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>>>>>>>>> +       select PCI_XGENE_MSI if PCI_MSI
>>>>>>>>>>         help
>>>>>>>>>>           Say Y here if you want internal PCI support on APM
>>>>>>>>>> X-Gene SoC. There are 5 internal PCIe ports available. Each port
>>>>>>>>>> is GEN3 capable
>>>>>>>>>>           and have varied lanes from x1 to x8.
>>>>>>>>>>
>>>>>>>>>> +config PCI_XGENE_MSI
>>>>>>>>>> +       bool "X-Gene v1 PCIe MSI feature"
>>>>>>>>>> +       depends on PCI_XGENE && PCI_MSI
>>>>>>>>>> +
>>>>>>>>>>  config PCI_LAYERSCAPE
>>>>>>>>>>         bool "Freescale Layerscape PCIe controller"
>>>>>>>>>>         depends on OF && ARM
>>>>>>>>>> diff --git a/drivers/pci/host/Makefile
>>>>>>>>>> b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644
>>>>>>>>>> --- a/drivers/pci/host/Makefile
>>>>>>>>>> +++ b/drivers/pci/host/Makefile
>>>>>>>>>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>>>>>>>>>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>>>>>>>>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>>>>>>>>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>>>>>>>>> diff --git a/drivers/pci/host/pci-xgene-msi.c
>>>>>>>>>> b/drivers/pci/host/pci-xgene-msi.c
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 0000000..4f0ff42
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/drivers/pci/host/pci-xgene-msi.c
>>>>>>>>>> @@ -0,0 +1,407 @@
>>>>>>>>>> +/*
>>>>>>>>>> + * APM X-Gene MSI Driver
>>>>>>>>>> + *
>>>>>>>>>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>>>>>>>>>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>>>>>>>>>> + *        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 as
>>>>>>>>>> published by the
>>>>>>>>>> + * Free Software Foundation;  either version 2 of the  License,
>>>>>>>>>> or (at your
>>>>>>>>>> + * option) any later version.
>>>>>>>>>> + *
>>>>>>>>>> + * 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.
>>>>>>>>>> + */
>>>>>>>>>> +#include <linux/interrupt.h>
>>>>>>>>>> +#include <linux/module.h>
>>>>>>>>>> +#include <linux/msi.h>
>>>>>>>>>> +#include <linux/of_irq.h>
>>>>>>>>>> +#include <linux/pci.h>
>>>>>>>>>> +#include <linux/platform_device.h>
>>>>>>>>>> +#include <linux/of_pci.h>
>>>>>>>>>> +
>>>>>>>>>> +#define MSI_INDEX0             0x000000
>>>>>>>>>> +#define MSI_INT0               0x800000
>>>>>>>>>> +
>>>>>>>>>> +struct xgene_msi_settings {
>>>>>>>>>> +       u32     index_per_group;
>>>>>>>>>> +       u32     irqs_per_index;
>>>>>>>>>> +       u32     nr_msi_vec;
>>>>>>>>>> +       u32     nr_hw_irqs;
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +struct xgene_msi {
>>>>>>>>>> +       struct device_node              *node;
>>>>>>>>>> +       struct msi_controller           mchip;
>>>>>>>>>> +       struct irq_domain               *domain;
>>>>>>>>>> +       struct xgene_msi_settings       *settings;
>>>>>>>>>> +       u32                             msi_addr_lo;
>>>>>>>>>> +       u32                             msi_addr_hi;
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> I'd rather see the mailbox address directly, and only do the
>>>>>>>>> split when assigning it to the message (you seem to play all kind
>>>>>>>>> of tricks on the address anyway).
>>>>>>>>
>>>>>>>>
>>>>>>>> msi_addr_lo and msi_addr_hi store the physical base address of MSI
>>>>>>>> controller registers. I will add comment to clarify this.
>>>>>>>
>>>>>>>
>>>>>>> What I mean is that there is no point in keeping this around as a
>>>>>>> pair of 32bit variables. You'd better keep it as a single 64bit,
>>>>>>> and do the split when assigning it the the MSI message.
>>>>>>
>>>>>> Hi Marc,
>>>>>>
>>>>>> These came from device-tree (which describes 64-bit address number as
>>>>>> 2 32-bit words).
>>>>>
>>>>> ... and converted to a resource as a 64bit word, on which you apply
>>>>> {upper,lower}_32_bit(). So much for DT...
>>>>>
>>>>>> If I store them this way, I don't need CPU cycles to do the split
>>>>>> every time assigning them to the MSI message. Please let me know what
>>>>>> do you think about it.
>>>>>
>>>>> This is getting absolutely silly.
>>>>>
>>>>> How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If
>>>>> it takes so long that it is considered to be a bottleneck, I suggest
>>>>> you go and design a better CPU (hint: the answer is probably 1 cycle
>>>>> absolutely everywhere).
>>>>>
>>>>> How often are you configuring MSIs in the face of what is happening in
>>>>> the rest of the kernel? Almost never!
>>>>>
>>>>> So, given that "never" times 1 is still never,  I'll consider that
>>>>> readability of the code trumps it anytime (I can't believe we're having
>>>>> that kind of conversation...).
>>>>>
>>>> I changed to use u64 for msi_addr and split it when composing MSI messages.
>>>> The change is in v4 of the patch set that I just posted.
>>>>>>>
>>>>>>> [...]
>>>>>>>
>>>>>>>>>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>>>>>>>>>> +                                 const struct cpumask *mask,
>>>>>>>>>> bool force)
>>>>>>>>>> +{
>>>>>>>>>> +       struct xgene_msi *msi =
>>>>>>>>>> irq_data_get_irq_chip_data(irq_data);
>>>>>>>>>> +       unsigned int gic_irq;
>>>>>>>>>> +       int ret;
>>>>>>>>>> +
>>>>>>>>>> +       gic_irq = msi->msi_virqs[irq_data->hwirq %
>>>>>>>>>> msi->settings->nr_hw_irqs];
>>>>>>>>>> +       ret = irq_set_affinity(gic_irq, mask);
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Erm... This as the effect of moving *all* the MSIs hanging off
>>>>>>>>> this interrupt to another CPU. I'm not sure that's an acceptable
>>>>>>>>> effect... What if another MSI requires a different affinity?
>>>>>>>>
>>>>>>>>
>>>>>>>> We have 16 'real' hardware IRQs. Each of these has multiple MSIs
>>>>>>>> attached to it.
>>>>>>>> So this will move all MSIs handing off this interrupt to another
>>>>>>>> CPU; and we don't support different affinity settings for
>>>>>>>> different MSIs that are attached to the same hardware IRQ.
>>>>>>>
>>>>>>>
>>>>>>> Well, that's a significant departure from the expected behaviour.
>>>>>>> In other words, I wonder how useful this is. Could you instead
>>>>>>> reconfigure the MSI itself to hit the right CPU (assuming you don't
>>>>>>> have more than 16 CPUs and if
>>>>>>> that's only for XGene-1, this will only be 8 at most)? This would
>>>>>>> reduce your number of possible MSIs, but at least the semantics of
>>>>>>> set_afinity would be preserved.
>>>>>>
>>>>>> X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each
>>>>>> group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have
>>>>>> 16 hardware GIC IRQs for 2688 MSIs).
>>>>>
>>>>> We've already established that.
>>>>>
>>>>>> Setting affinity of single MSI to deliver it to a target CPU will move
>>>>>> all the MSIs mapped to the same GIC IRQ to that CPU as well. This is
>>>>>> not a standard behavior, but limiting the total number of MSIs will
>>>>>> cause a lot of devices to fall back to INTx (with huge performance
>>>>>> penalty) or even fail to load their driver as these devices request
>>>>>> more than 16 MSIs during driver initialization.
>>>>>
>>>>> No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and
>>>>> provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per
>>>>> CPU (having statically assigned 2 IRQs per CPU).
>>>>>
>>>>> Assuming you adopt my scheme, you still have a grand total of 336 MSIs
>>>>> that can be freely moved around without breaking any userspace
>>>>> expectations.
>>>>>
>>>> Thanks Marc. This is a very good idea.
>>>>
>>>> But to  move MSIs around, I need to change MSI termination address and data
>>>> and write them to device configuration space. This may cause problems
>>>> if the device
>>>> fires an interrupt at the same time when I do the config write?
>>>>
>>>> What is your opinion here?
>>>
>>> There is an inherent race when changing the affinity of any interrupt,
>>> whether that's an MSI or not. The kernel is perfectly prepared to handle
>>> such a situation (to be honest, the kernel doesn't really care).
>>>
>>> The write to the config space shouldn't be a concern. You will either
>>> hit the old *or* the new CPU, but that race is only during the write
>>> itself (you can read back from the config space if you're really
>>> paranoid).  By the time you return from this read/write, the device will
>>> be reconfigured.
>>>
>>>>> I think that 336 MSIs is a fair number (nowhere near the 16 you claim).
>>>>> Most platforms are doing quite well with that kind of numbers. Also,
>>>>> you don't have to allocate all the MSIs a device can possibly claim (up
>>>>> to 2048 MSI-X per device), as they are all perfectly capable of using
>>>>> less MSI without having to fallback to INTx).
>>>>>
>>>>>> I can document the limitation in affinity setting of X-Gene-1 MSI in
>>>>>> the driver to hopefully not make people surprise and hope to keep the
>>>>>> total number of supported MSI as 2688 so that we can support as many
>>>>>> cards that require MSI/MSI-X as possible.
>>>>>
>>>>> I don't think this is a valid approach. This breaks userspace (think of
>>>>> things like irqbalance), and breaks the SMP affinity model that Linux
>>>>> uses. No amount of documentation is going to solve it, so I think you
>>>>> just have to admit that the HW is mis-designed and do the best you can
>>>>> to make it work like Linux expect it to work.
>>>>>
>>>>> The alternative would to disable affinity setting altogether instead of
>>>>> introducing these horrible side effects.
>>>>>
>>>> I have it disabled (set_affinity does nothing) in my v4 patch.
>>>
>>> It would be good if you could try the above approach. It shouldn't be
>>> hard to write, and it would be a lot better than just ignoring the problem.
>>>
>> Yes. I am working on this change.
>
> In which case, I'll wait for a v5. No need to spend time on something
> that is going to change anyway.

Marc:

Is it possible to use the pci_msi_create_default_irq_domain for the X-Gene MSI
driver. Or is this going to be removed in the future. Thanks

>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...

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

* [PATCH v5 0/4]PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-04-17 12:45                                         ` Marc Zyngier
@ 2015-04-21  4:04                                           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21  4:04 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI
vectors this driver supports is reduced to 256 to maintain the correct set_affinity 
behavior for each MSI.

v5 changes:
	1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs
	for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer
	them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1
	supports is reduced to 256.

v4 changes:
        1. Remove affinity setting for each MSI
        2. Add description about register layout, MSI termination address and data
        3. Correct total number of MSI vectors to 2048
        4. Clean up error messages
        5. Remove unused module code

v3 changes:
        1. Implement MSI support using PCI MSI IRQ domain
        2. Only use msi_controller to store IRQ domain
v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  63 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   6 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 477 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 603 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1


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

* [PATCH v5 0/4]PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-04-21  4:04                                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21  4:04 UTC (permalink / raw)
  To: linux-arm-kernel

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI
vectors this driver supports is reduced to 256 to maintain the correct set_affinity 
behavior for each MSI.

v5 changes:
	1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs
	for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer
	them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1
	supports is reduced to 256.

v4 changes:
        1. Remove affinity setting for each MSI
        2. Add description about register layout, MSI termination address and data
        3. Correct total number of MSI vectors to 2048
        4. Clean up error messages
        5. Remove unused module code

v3 changes:
        1. Implement MSI support using PCI MSI IRQ domain
        2. Only use msi_controller to store IRQ domain
v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  63 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   6 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 477 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 603 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1

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

* [PATCH v5 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-21  4:04                                           ` Duc Dang
@ 2015-04-21  4:04                                           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21  4:04 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

X-Gene v1 SoC supports total 256 MSI/MSIX vectors coalesced into
16 HW IRQ lines.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 477 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 505 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9..c9b61fa 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,17 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c..f39bde3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..910f5db
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,477 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0			0x000000
+#define MSI_INT0		0x800000
+#define IDX_PER_GROUP		8
+#define IRQS_PER_IDX		16
+#define NR_HW_IRQS		16
+#define NR_MSI_VEC		(IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	u64				msi_addr;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	int				*msi_virqs;
+	int				num_cpus;
+};
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0			base_addr
+ * MSI0IR1			base_addr +  0x10000
+ * ...				...
+ * MSI0IR6			base_addr +  0x60000
+ * MSI0IR7			base_addr +  0x70000
+ * MSI1IR0			base_addr +  0x80000
+ * MSI1IR1			base_addr +  0x90000
+ * ...				...
+ * MSI1IR7			base_addr +  0xF0000
+ * MSI2IR0			base_addr + 0x100000
+ * ...				...
+ * MSIFIR0			base_addr + 0x780000
+ * MSIFIR1			base_addr + 0x790000
+ * ...				...
+ * MSIFIR7			base_addr + 0x7F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *		Group 0: 0-255
+ *		Group 1: 256-511
+ *		Group 2: 512-767
+ *		...
+ *		Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *	As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *		Group 0: 0-15
+ *		Group 1: 16-32
+ *		...
+ *		Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+	u32 group = data->hwirq % NR_HW_IRQS;
+
+	msg->address_hi = upper_32_bits(msi->msi_addr);
+	msg->address_lo = lower_32_bits(msi->msi_addr) +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocate to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each cores). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+				  const struct cpumask *mask, bool force)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+	struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
+	int target_cpu = cpumask_first(mask);
+	int curr_cpu;
+	struct msi_msg msg;
+	u32 reg_set, group;
+
+	if (!desc)
+		return IRQ_SET_MASK_OK_DONE;
+
+	curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
+	if (curr_cpu == target_cpu)
+		return IRQ_SET_MASK_OK_DONE;
+
+	irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+	reg_set = irq_data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+	group = irq_data->hwirq % NR_HW_IRQS;
+
+	/* Prepare new MSI msg to steer MSI to target CPU */
+	msg.address_hi = upper_32_bits(msi->msi_addr);
+	msg.address_lo = lower_32_bits(msi->msi_addr) +
+		(((8 * group) + reg_set) << 16);
+	msg.data = (irq_data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+	/*
+	 * Write new MSI termination address and data into
+	 * PCIe device configuration space
+	 */
+	pci_write_msi_msg(irq_data->irq, &msg);
+	return IRQ_SET_MASK_OK_DONE;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+	struct xgene_msi *msi = domain->host_data;
+	int msi_irq;
+	int i;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = find_first_zero_bit(msi->bitmap, NR_MSI_VEC);
+	if (msi_irq < NR_MSI_VEC)
+		set_bit(msi_irq, msi->bitmap);
+	else
+		msi_irq = -ENOSPC;
+
+	/* Check if we found a valid MSI */
+	if ((msi_irq >= 0) && ((msi_irq % msi->num_cpus) == 0)) {
+		/* Allocate other vectors as well */
+		for (i = 1; i < msi->num_cpus; i++) {
+			if ((msi_irq + i) >= NR_MSI_VEC) {
+				msi_irq = -ENOSPC;
+				break;
+			}
+			if (!test_bit((msi_irq + i), msi->bitmap))
+				set_bit(msi_irq + i, msi->bitmap);
+			else {
+				msi_irq = -ENOSPC;
+				break;
+			}
+		}
+	}
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq,
+			    &xgene_msi_bottom_irq_chip, domain->host_data,
+			    handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+	int i;
+	u32 hwirq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
+	for (i = 0; i < msi->num_cpus; i++)
+		if (test_bit(hwirq + i, msi->bitmap))
+			clear_bit(hwirq + i, msi->bitmap);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+					    &msi_domain_ops, msi);
+	if (!msi->domain)
+		return -ENOMEM;
+
+	msi->mchip.of_node = msi->node;
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
+	if (!xgene_msi->msi_virqs)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct xgene_msi *xgene_msi;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp, processed = 0;
+
+	chained_irq_enter(chip, desc);
+
+	xgene_msi = irq_desc_get_handler_data(desc);
+
+	msi_grp = irq - xgene_msi->msi_virqs[0];
+	if (msi_grp >= NR_HW_IRQS) {
+		chained_irq_exit(chip, desc);
+		return;
+	}
+
+	/*
+	 * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+	 * If bit x of this register is set (x is 0..7), one or more interupts
+	 * corresponding to MSInIRx is set.
+	 */
+	grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		/*
+		 * Calculate MSInIRx address to read to check for interrupts
+		 * (refer to termination address and data assignment
+		 * described in xgene_compose_msi_msg function)
+		 */
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			/*
+			 * Calculate MSI vector number (refer to the termination
+			 * address and data assignment described in
+			 * xgene_compose_msi_msg function)
+			 */
+			hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+				 NR_HW_IRQS) + msi_grp;
+			hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+			processed++;
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		virq = msi->msi_virqs[i];
+		if (virq != 0)
+			free_irq(virq, msi);
+	}
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+				 struct platform_device *pdev,
+				 int irq_index)
+{
+	int virt_msir;
+	cpumask_var_t mask;
+	int err;
+
+	virt_msir = platform_get_irq(pdev, irq_index);
+	if (virt_msir < 0) {
+		dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+			irq_index);
+		return -EINVAL;
+	}
+
+	irq_set_chained_handler(virt_msir, xgene_msi_isr);
+	err = irq_set_handler_data(virt_msir, msi);
+	if (err) {
+		dev_err(&pdev->dev, "request irq failed\n");
+		return err;
+	}
+
+	/*
+	 * Statically allocate MSI GIC IRQs to each CPU core
+	 * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+	 * to each core.
+	 */
+	if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+		cpumask_clear(mask);
+		cpumask_set_cpu(irq_index % msi->num_cpus, mask);
+		irq_set_affinity(virt_msir, mask);
+		free_cpumask_var(mask);
+	}
+
+	msi->msi_virqs[irq_index] = virt_msir;
+
+	return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi"},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct device_node *np;
+	const struct of_device_id *matched_np;
+	struct xgene_msi *xgene_msi;
+
+	np = of_find_matching_node_and_match(NULL,
+			     xgene_msi_match_table, &matched_np);
+	if (!np)
+		return -ENODEV;
+
+	xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
+	if (!xgene_msi) {
+		dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
+		return -ENOMEM;
+	}
+
+	xgene_msi->node = np;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	xgene_msi->msi_addr = res->start;
+
+	xgene_msi->num_cpus = num_online_cpus();
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+		if (rc)
+			goto error;
+	}
+
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 22751ed..3e6faa1 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1


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

* [PATCH v5 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-21  4:04                                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21  4:04 UTC (permalink / raw)
  To: linux-arm-kernel

X-Gene v1 SoC supports total 256 MSI/MSIX vectors coalesced into
16 HW IRQ lines.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 477 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 505 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9..c9b61fa 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,17 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c..f39bde3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..910f5db
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,477 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0			0x000000
+#define MSI_INT0		0x800000
+#define IDX_PER_GROUP		8
+#define IRQS_PER_IDX		16
+#define NR_HW_IRQS		16
+#define NR_MSI_VEC		(IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	u64				msi_addr;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	int				*msi_virqs;
+	int				num_cpus;
+};
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0			base_addr
+ * MSI0IR1			base_addr +  0x10000
+ * ...				...
+ * MSI0IR6			base_addr +  0x60000
+ * MSI0IR7			base_addr +  0x70000
+ * MSI1IR0			base_addr +  0x80000
+ * MSI1IR1			base_addr +  0x90000
+ * ...				...
+ * MSI1IR7			base_addr +  0xF0000
+ * MSI2IR0			base_addr + 0x100000
+ * ...				...
+ * MSIFIR0			base_addr + 0x780000
+ * MSIFIR1			base_addr + 0x790000
+ * ...				...
+ * MSIFIR7			base_addr + 0x7F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *		Group 0: 0-255
+ *		Group 1: 256-511
+ *		Group 2: 512-767
+ *		...
+ *		Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *	As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *		Group 0: 0-15
+ *		Group 1: 16-32
+ *		...
+ *		Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+	u32 group = data->hwirq % NR_HW_IRQS;
+
+	msg->address_hi = upper_32_bits(msi->msi_addr);
+	msg->address_lo = lower_32_bits(msi->msi_addr) +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocate to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each cores). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+				  const struct cpumask *mask, bool force)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+	struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
+	int target_cpu = cpumask_first(mask);
+	int curr_cpu;
+	struct msi_msg msg;
+	u32 reg_set, group;
+
+	if (!desc)
+		return IRQ_SET_MASK_OK_DONE;
+
+	curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
+	if (curr_cpu == target_cpu)
+		return IRQ_SET_MASK_OK_DONE;
+
+	irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+	reg_set = irq_data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+	group = irq_data->hwirq % NR_HW_IRQS;
+
+	/* Prepare new MSI msg to steer MSI to target CPU */
+	msg.address_hi = upper_32_bits(msi->msi_addr);
+	msg.address_lo = lower_32_bits(msi->msi_addr) +
+		(((8 * group) + reg_set) << 16);
+	msg.data = (irq_data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+	/*
+	 * Write new MSI termination address and data into
+	 * PCIe device configuration space
+	 */
+	pci_write_msi_msg(irq_data->irq, &msg);
+	return IRQ_SET_MASK_OK_DONE;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+	struct xgene_msi *msi = domain->host_data;
+	int msi_irq;
+	int i;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = find_first_zero_bit(msi->bitmap, NR_MSI_VEC);
+	if (msi_irq < NR_MSI_VEC)
+		set_bit(msi_irq, msi->bitmap);
+	else
+		msi_irq = -ENOSPC;
+
+	/* Check if we found a valid MSI */
+	if ((msi_irq >= 0) && ((msi_irq % msi->num_cpus) == 0)) {
+		/* Allocate other vectors as well */
+		for (i = 1; i < msi->num_cpus; i++) {
+			if ((msi_irq + i) >= NR_MSI_VEC) {
+				msi_irq = -ENOSPC;
+				break;
+			}
+			if (!test_bit((msi_irq + i), msi->bitmap))
+				set_bit(msi_irq + i, msi->bitmap);
+			else {
+				msi_irq = -ENOSPC;
+				break;
+			}
+		}
+	}
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq,
+			    &xgene_msi_bottom_irq_chip, domain->host_data,
+			    handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+	int i;
+	u32 hwirq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
+	for (i = 0; i < msi->num_cpus; i++)
+		if (test_bit(hwirq + i, msi->bitmap))
+			clear_bit(hwirq + i, msi->bitmap);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+					    &msi_domain_ops, msi);
+	if (!msi->domain)
+		return -ENOMEM;
+
+	msi->mchip.of_node = msi->node;
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
+	if (!xgene_msi->msi_virqs)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct xgene_msi *xgene_msi;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp, processed = 0;
+
+	chained_irq_enter(chip, desc);
+
+	xgene_msi = irq_desc_get_handler_data(desc);
+
+	msi_grp = irq - xgene_msi->msi_virqs[0];
+	if (msi_grp >= NR_HW_IRQS) {
+		chained_irq_exit(chip, desc);
+		return;
+	}
+
+	/*
+	 * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+	 * If bit x of this register is set (x is 0..7), one or more interupts
+	 * corresponding to MSInIRx is set.
+	 */
+	grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		/*
+		 * Calculate MSInIRx address to read to check for interrupts
+		 * (refer to termination address and data assignment
+		 * described in xgene_compose_msi_msg function)
+		 */
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			/*
+			 * Calculate MSI vector number (refer to the termination
+			 * address and data assignment described in
+			 * xgene_compose_msi_msg function)
+			 */
+			hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+				 NR_HW_IRQS) + msi_grp;
+			hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+			processed++;
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		virq = msi->msi_virqs[i];
+		if (virq != 0)
+			free_irq(virq, msi);
+	}
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+				 struct platform_device *pdev,
+				 int irq_index)
+{
+	int virt_msir;
+	cpumask_var_t mask;
+	int err;
+
+	virt_msir = platform_get_irq(pdev, irq_index);
+	if (virt_msir < 0) {
+		dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+			irq_index);
+		return -EINVAL;
+	}
+
+	irq_set_chained_handler(virt_msir, xgene_msi_isr);
+	err = irq_set_handler_data(virt_msir, msi);
+	if (err) {
+		dev_err(&pdev->dev, "request irq failed\n");
+		return err;
+	}
+
+	/*
+	 * Statically allocate MSI GIC IRQs to each CPU core
+	 * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+	 * to each core.
+	 */
+	if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+		cpumask_clear(mask);
+		cpumask_set_cpu(irq_index % msi->num_cpus, mask);
+		irq_set_affinity(virt_msir, mask);
+		free_cpumask_var(mask);
+	}
+
+	msi->msi_virqs[irq_index] = virt_msir;
+
+	return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi"},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct device_node *np;
+	const struct of_device_id *matched_np;
+	struct xgene_msi *xgene_msi;
+
+	np = of_find_matching_node_and_match(NULL,
+			     xgene_msi_match_table, &matched_np);
+	if (!np)
+		return -ENODEV;
+
+	xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
+	if (!xgene_msi) {
+		dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
+		return -ENOMEM;
+	}
+
+	xgene_msi->node = np;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	xgene_msi->msi_addr = res->start;
+
+	xgene_msi->num_cpus = num_online_cpus();
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+		if (rc)
+			goto error;
+	}
+
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 22751ed..3e6faa1 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1

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

* [PATCH v5 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
  2015-04-21  4:04                                           ` Duc Dang
@ 2015-04-21  4:04                                           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21  4:04 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..4b719c9 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi@79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie@1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie@1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie@1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie@1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie@1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial@1c020000 {
-- 
1.9.1


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

* [PATCH v5 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
@ 2015-04-21  4:04                                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21  4:04 UTC (permalink / raw)
  To: linux-arm-kernel

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..4b719c9 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
-- 
1.9.1

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

* [PATCH v5 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
  2015-04-21  4:04                                           ` Duc Dang
@ 2015-04-21  4:04                                           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21  4:04 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..0ffdcb3
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,63 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene1-msi" to identify the core.
+- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi@79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = <  0x0 0x10 0x4
+				0x0 0x11 0x4
+				0x0 0x12 0x4
+				0x0 0x13 0x4
+				0x0 0x14 0x4
+				0x0 0x15 0x4
+				0x0 0x16 0x4
+				0x0 0x17 0x4
+				0x0 0x18 0x4
+				0x0 0x19 0x4
+				0x0 0x1a 0x4
+				0x0 0x1b 0x4
+				0x0 0x1c 0x4
+				0x0 0x1d 0x4
+				0x0 0x1e 0x4
+				0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie@1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1


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

* [PATCH v5 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
@ 2015-04-21  4:04                                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21  4:04 UTC (permalink / raw)
  To: linux-arm-kernel

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..0ffdcb3
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,63 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene1-msi" to identify the core.
+- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = <  0x0 0x10 0x4
+				0x0 0x11 0x4
+				0x0 0x12 0x4
+				0x0 0x13 0x4
+				0x0 0x14 0x4
+				0x0 0x15 0x4
+				0x0 0x16 0x4
+				0x0 0x17 0x4
+				0x0 0x18 0x4
+				0x0 0x19 0x4
+				0x0 0x1a 0x4
+				0x0 0x1b 0x4
+				0x0 0x1c 0x4
+				0x0 0x1d 0x4
+				0x0 0x1e 0x4
+				0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1

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

* [PATCH v5 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
  2015-04-21  4:04                                           ` Duc Dang
@ 2015-04-21  4:04                                           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21  4:04 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddc5a8c..a1b119b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7490,6 +7490,14 @@ L:	linux-pci@vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci@vger.kernel.org
+L:	linux-arm-kernel@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia@lists.infradead.org
-- 
1.9.1


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

* [PATCH v5 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
@ 2015-04-21  4:04                                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21  4:04 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddc5a8c..a1b119b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7490,6 +7490,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
-- 
1.9.1

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

* Re: [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-20 18:49                                           ` Feng Kan
@ 2015-04-21  7:16                                             ` Arnd Bergmann
  -1 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-04-21  7:16 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Feng Kan, Marc Zyngier, linux-pci, Duc Dang, Liviu Dudau,
	linux-kernel, Bjorn Helgaas, Tanmay Inamdar, Grant Likely,
	Loc Ho

On Monday 20 April 2015 11:49:37 Feng Kan wrote:
> >
> > Obviously they appear on the PCI host bridge in order, because that
> > is a how PCI works. My question was about what happens then. On a lot
> > of SoCs, there is something like an AXI bus that uses posted
> > transactions between PCI and RAM, so you have a do a full manual
> > syncronization of ongoing PIC DMAs when the MSI catcher signals the
> > top-level interrupt. Do you have a bus between PCI and RAM that does
> > not require this, or does the MSI catcher have logic to flush all DMAs?
> 
> Our hardware has an automatic mechanism to flush the content to DRAM before the
> MSI write is committed.

Ok, excellent!

	Arnd

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

* [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-21  7:16                                             ` Arnd Bergmann
  0 siblings, 0 replies; 230+ messages in thread
From: Arnd Bergmann @ 2015-04-21  7:16 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 20 April 2015 11:49:37 Feng Kan wrote:
> >
> > Obviously they appear on the PCI host bridge in order, because that
> > is a how PCI works. My question was about what happens then. On a lot
> > of SoCs, there is something like an AXI bus that uses posted
> > transactions between PCI and RAM, so you have a do a full manual
> > syncronization of ongoing PIC DMAs when the MSI catcher signals the
> > top-level interrupt. Do you have a bus between PCI and RAM that does
> > not require this, or does the MSI catcher have logic to flush all DMAs?
> 
> Our hardware has an automatic mechanism to flush the content to DRAM before the
> MSI write is committed.

Ok, excellent!

	Arnd

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

* Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-20 18:51                                           ` Feng Kan
@ 2015-04-21  8:32                                             ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-21  8:32 UTC (permalink / raw)
  To: Feng Kan
  Cc: Duc Dang, Arnd Bergmann, linux-pci, Liviu Dudau, linux-kernel,
	grant.likely, Tanmay Inamdar, Bjorn Helgaas, linux-arm, Loc Ho

On 20/04/15 19:51, Feng Kan wrote:

> Is it possible to use the pci_msi_create_default_irq_domain for the X-Gene MSI
> driver. Or is this going to be removed in the future. Thanks

This is not the preferred way. pci_msi_create_default_irq_domain is only
there for legacy purposes, so that we can lookup an MSI without
specifying the domain. New code shouldn't have to rely on it.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-21  8:32                                             ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-21  8:32 UTC (permalink / raw)
  To: linux-arm-kernel

On 20/04/15 19:51, Feng Kan wrote:

> Is it possible to use the pci_msi_create_default_irq_domain for the X-Gene MSI
> driver. Or is this going to be removed in the future. Thanks

This is not the preferred way. pci_msi_create_default_irq_domain is only
there for legacy purposes, so that we can lookup an MSI without
specifying the domain. New code shouldn't have to rely on it.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH v5 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-21  4:04                                           ` Duc Dang
  (?)
@ 2015-04-21 15:08                                             ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-21 15:08 UTC (permalink / raw)
  To: Duc Dang, Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On 21/04/15 05:04, Duc Dang wrote:
> X-Gene v1 SoC supports total 256 MSI/MSIX vectors coalesced into
> 16 HW IRQ lines.

This commit message could be beefed up to describe what this really does.

> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |   6 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 477 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 505 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..c9b61fa 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,17 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..910f5db
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,477 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       int                             *msi_virqs;
> +       int                             num_cpus;
> +};
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> +       u32 group = data->hwirq % NR_HW_IRQS;
> +
> +       msg->address_hi = upper_32_bits(msi->msi_addr);
> +       msg->address_lo = lower_32_bits(msi->msi_addr) +
> +                         (((8 * group) + reg_set) << 16);
> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocate to 8 X-Gene
                                              nit: allocated
> + * v1 cores (2 GIC IRQs for each cores). The MSI vector is moved fom 1
                                nit: core
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +       struct msi_msg msg;
> +       u32 reg_set, group;
> +
> +       if (!desc)
> +               return IRQ_SET_MASK_OK_DONE;

That can't be right. You really should return an error code here.

> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> +       reg_set = irq_data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> +       group = irq_data->hwirq % NR_HW_IRQS;
> +
> +       /* Prepare new MSI msg to steer MSI to target CPU */
> +       msg.address_hi = upper_32_bits(msi->msi_addr);
> +       msg.address_lo = lower_32_bits(msi->msi_addr) +
> +               (((8 * group) + reg_set) << 16);
> +       msg.data = (irq_data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +       /*
> +        * Write new MSI termination address and data into
> +        * PCIe device configuration space
> +        */
> +       pci_write_msi_msg(irq_data->irq, &msg);
> +       return IRQ_SET_MASK_OK_DONE;

That feels like the wrong thing to do. You are duplicating a
functionality that already exists in msi_domain_set_affinity.

You should probably just update hwirq to reflect the new absolute MSI
number, return IRQ_SET_MASK_OK and let compose_msi_msg/write_msi_msg do
the right thing.

> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +       struct xgene_msi *msi = domain->host_data;
> +       int msi_irq;
> +       int i;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = find_first_zero_bit(msi->bitmap, NR_MSI_VEC);
> +       if (msi_irq < NR_MSI_VEC)
> +               set_bit(msi_irq, msi->bitmap);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       /* Check if we found a valid MSI */
> +       if ((msi_irq >= 0) && ((msi_irq % msi->num_cpus) == 0)) {
> +               /* Allocate other vectors as well */
> +               for (i = 1; i < msi->num_cpus; i++) {
> +                       if ((msi_irq + i) >= NR_MSI_VEC) {
> +                               msi_irq = -ENOSPC;
> +                               break;

Eh? Right in the middle of the allocation? You end up with an
inconsistent bitmap, don't you?

Consider using bitmap_find_next_zero_area() to allocate a chunk of 8
consecutive bits, which will give you the MSI for your 8 CPUs.

> +                       }
> +                       if (!test_bit((msi_irq + i), msi->bitmap))
> +                               set_bit(msi_irq + i, msi->bitmap);
> +                       else {
> +                               msi_irq = -ENOSPC;
> +                               break;

Same breakage.

> +                       }
> +               }
> +       }
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq,
> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> +                           handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +       int i;
> +       u32 hwirq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
> +       for (i = 0; i < msi->num_cpus; i++)
> +               if (test_bit(hwirq + i, msi->bitmap))
> +                       clear_bit(hwirq + i, msi->bitmap);

bitmap_release_region()?

> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> +                                           &msi_domain_ops, msi);
> +       if (!msi->domain)
> +               return -ENOMEM;
> +
> +       msi->mchip.of_node = msi->node;
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
> +       if (!xgene_msi->msi_virqs)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> +{
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       struct xgene_msi *xgene_msi;
> +       unsigned int virq;
> +       int msir_index, msir_reg, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp, processed = 0;
> +
> +       chained_irq_enter(chip, desc);
> +
> +       xgene_msi = irq_desc_get_handler_data(desc);
> +
> +       msi_grp = irq - xgene_msi->msi_virqs[0];

Arghh. Absolutely nothing guarantees that your 16 IRQs are contiguous in
the virtual space. This only works by pure luck. Why don't you use the
handler data to contain your group? Here, you're storing you xgene_msi
structure, which is unique anyway.

> +       if (msi_grp >= NR_HW_IRQS) {
> +               chained_irq_exit(chip, desc);
> +               return;
> +       }
> +
> +       /*
> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> +        * If bit x of this register is set (x is 0..7), one or more interupts
> +        * corresponding to MSInIRx is set.
> +        */
> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));

Can't you use readl_relaxed?

> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               /*
> +                * Calculate MSInIRx address to read to check for interrupts
> +                * (refer to termination address and data assignment
> +                * described in xgene_compose_msi_msg function)
> +                */
> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> +               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);

Same here?

> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       /*
> +                        * Calculate MSI vector number (refer to the termination
> +                        * address and data assignment described in
> +                        * xgene_compose_msi_msg function)
> +                        */
> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> +                                NR_HW_IRQS) + msi_grp;
> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);

Maybe a comment indicating that the domain is always looked up with the
hwirq as seen from CPU0?

> +                       if (virq != 0)
> +                               generic_handle_irq(virq);
> +                       msir_val &= ~(1 << intr_index);
> +                       processed++;

What's the purpose of processed? Seems fairly useless to me.

> +               }
> +               grp_select &= ~(1 << msir_index);
> +       }
> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               virq = msi->msi_virqs[i];
> +               if (virq != 0)

Under which circumstances could that be zero?

> +                       free_irq(virq, msi);
> +       }
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);

Who frees msi->msi_virqs?

> +       return 0;
> +}
> +
> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
> +                                struct platform_device *pdev,
> +                                int irq_index)
> +{
> +       int virt_msir;
> +       cpumask_var_t mask;
> +       int err;
> +
> +       virt_msir = platform_get_irq(pdev, irq_index);
> +       if (virt_msir < 0) {
> +               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                       irq_index);
> +               return -EINVAL;
> +       }
> +
> +       irq_set_chained_handler(virt_msir, xgene_msi_isr);
> +       err = irq_set_handler_data(virt_msir, msi);
> +       if (err) {
> +               dev_err(&pdev->dev, "request irq failed\n");
> +               return err;
> +       }
> +
> +       /*
> +        * Statically allocate MSI GIC IRQs to each CPU core
> +        * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> +        * to each core.
> +        */
> +       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +               cpumask_clear(mask);
> +               cpumask_set_cpu(irq_index % msi->num_cpus, mask);
> +               irq_set_affinity(virt_msir, mask);

How about testing the return value of irq_set_affinity?

> +               free_cpumask_var(mask);
> +       }
> +
> +       msi->msi_virqs[irq_index] = virt_msir;
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi"},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct device_node *np;
> +       const struct of_device_id *matched_np;
> +       struct xgene_msi *xgene_msi;
> +
> +       np = of_find_matching_node_and_match(NULL,
> +                            xgene_msi_match_table, &matched_np);
> +       if (!np)
> +               return -ENODEV;

This is getting silly, really. You already have the device node pointed
to by pdev...

> +
> +       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
> +       if (!xgene_msi) {
> +               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
> +               return -ENOMEM;
> +       }
> +
> +       xgene_msi->node = np;
> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +       xgene_msi->msi_addr = res->start;
> +
> +       xgene_msi->num_cpus = num_online_cpus();

How are you going to support CPU hotplug? This probably should be
num_possible_cpus(), and you will need a CPU notifier to handle CPUs
being brought up.

> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
> +               if (rc)
> +                       goto error;
> +       }
> +
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +error:
> +       xgene_msi_remove(pdev);
> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

Looks like there is some more work to do...

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH v5 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-21 15:08                                             ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-21 15:08 UTC (permalink / raw)
  To: Duc Dang, Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On 21/04/15 05:04, Duc Dang wrote:
> X-Gene v1 SoC supports total 256 MSI/MSIX vectors coalesced into
> 16 HW IRQ lines.

This commit message could be beefed up to describe what this really does.

> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |   6 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 477 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 505 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..c9b61fa 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,17 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..910f5db
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,477 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       int                             *msi_virqs;
> +       int                             num_cpus;
> +};
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> +       u32 group = data->hwirq % NR_HW_IRQS;
> +
> +       msg->address_hi = upper_32_bits(msi->msi_addr);
> +       msg->address_lo = lower_32_bits(msi->msi_addr) +
> +                         (((8 * group) + reg_set) << 16);
> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocate to 8 X-Gene
                                              nit: allocated
> + * v1 cores (2 GIC IRQs for each cores). The MSI vector is moved fom 1
                                nit: core
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +       struct msi_msg msg;
> +       u32 reg_set, group;
> +
> +       if (!desc)
> +               return IRQ_SET_MASK_OK_DONE;

That can't be right. You really should return an error code here.

> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> +       reg_set = irq_data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> +       group = irq_data->hwirq % NR_HW_IRQS;
> +
> +       /* Prepare new MSI msg to steer MSI to target CPU */
> +       msg.address_hi = upper_32_bits(msi->msi_addr);
> +       msg.address_lo = lower_32_bits(msi->msi_addr) +
> +               (((8 * group) + reg_set) << 16);
> +       msg.data = (irq_data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +       /*
> +        * Write new MSI termination address and data into
> +        * PCIe device configuration space
> +        */
> +       pci_write_msi_msg(irq_data->irq, &msg);
> +       return IRQ_SET_MASK_OK_DONE;

That feels like the wrong thing to do. You are duplicating a
functionality that already exists in msi_domain_set_affinity.

You should probably just update hwirq to reflect the new absolute MSI
number, return IRQ_SET_MASK_OK and let compose_msi_msg/write_msi_msg do
the right thing.

> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +       struct xgene_msi *msi = domain->host_data;
> +       int msi_irq;
> +       int i;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = find_first_zero_bit(msi->bitmap, NR_MSI_VEC);
> +       if (msi_irq < NR_MSI_VEC)
> +               set_bit(msi_irq, msi->bitmap);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       /* Check if we found a valid MSI */
> +       if ((msi_irq >= 0) && ((msi_irq % msi->num_cpus) == 0)) {
> +               /* Allocate other vectors as well */
> +               for (i = 1; i < msi->num_cpus; i++) {
> +                       if ((msi_irq + i) >= NR_MSI_VEC) {
> +                               msi_irq = -ENOSPC;
> +                               break;

Eh? Right in the middle of the allocation? You end up with an
inconsistent bitmap, don't you?

Consider using bitmap_find_next_zero_area() to allocate a chunk of 8
consecutive bits, which will give you the MSI for your 8 CPUs.

> +                       }
> +                       if (!test_bit((msi_irq + i), msi->bitmap))
> +                               set_bit(msi_irq + i, msi->bitmap);
> +                       else {
> +                               msi_irq = -ENOSPC;
> +                               break;

Same breakage.

> +                       }
> +               }
> +       }
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq,
> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> +                           handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +       int i;
> +       u32 hwirq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
> +       for (i = 0; i < msi->num_cpus; i++)
> +               if (test_bit(hwirq + i, msi->bitmap))
> +                       clear_bit(hwirq + i, msi->bitmap);

bitmap_release_region()?

> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> +                                           &msi_domain_ops, msi);
> +       if (!msi->domain)
> +               return -ENOMEM;
> +
> +       msi->mchip.of_node = msi->node;
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
> +       if (!xgene_msi->msi_virqs)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> +{
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       struct xgene_msi *xgene_msi;
> +       unsigned int virq;
> +       int msir_index, msir_reg, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp, processed = 0;
> +
> +       chained_irq_enter(chip, desc);
> +
> +       xgene_msi = irq_desc_get_handler_data(desc);
> +
> +       msi_grp = irq - xgene_msi->msi_virqs[0];

Arghh. Absolutely nothing guarantees that your 16 IRQs are contiguous in
the virtual space. This only works by pure luck. Why don't you use the
handler data to contain your group? Here, you're storing you xgene_msi
structure, which is unique anyway.

> +       if (msi_grp >= NR_HW_IRQS) {
> +               chained_irq_exit(chip, desc);
> +               return;
> +       }
> +
> +       /*
> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> +        * If bit x of this register is set (x is 0..7), one or more interupts
> +        * corresponding to MSInIRx is set.
> +        */
> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));

Can't you use readl_relaxed?

> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               /*
> +                * Calculate MSInIRx address to read to check for interrupts
> +                * (refer to termination address and data assignment
> +                * described in xgene_compose_msi_msg function)
> +                */
> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> +               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);

Same here?

> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       /*
> +                        * Calculate MSI vector number (refer to the termination
> +                        * address and data assignment described in
> +                        * xgene_compose_msi_msg function)
> +                        */
> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> +                                NR_HW_IRQS) + msi_grp;
> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);

Maybe a comment indicating that the domain is always looked up with the
hwirq as seen from CPU0?

> +                       if (virq != 0)
> +                               generic_handle_irq(virq);
> +                       msir_val &= ~(1 << intr_index);
> +                       processed++;

What's the purpose of processed? Seems fairly useless to me.

> +               }
> +               grp_select &= ~(1 << msir_index);
> +       }
> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               virq = msi->msi_virqs[i];
> +               if (virq != 0)

Under which circumstances could that be zero?

> +                       free_irq(virq, msi);
> +       }
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);

Who frees msi->msi_virqs?

> +       return 0;
> +}
> +
> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
> +                                struct platform_device *pdev,
> +                                int irq_index)
> +{
> +       int virt_msir;
> +       cpumask_var_t mask;
> +       int err;
> +
> +       virt_msir = platform_get_irq(pdev, irq_index);
> +       if (virt_msir < 0) {
> +               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                       irq_index);
> +               return -EINVAL;
> +       }
> +
> +       irq_set_chained_handler(virt_msir, xgene_msi_isr);
> +       err = irq_set_handler_data(virt_msir, msi);
> +       if (err) {
> +               dev_err(&pdev->dev, "request irq failed\n");
> +               return err;
> +       }
> +
> +       /*
> +        * Statically allocate MSI GIC IRQs to each CPU core
> +        * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> +        * to each core.
> +        */
> +       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +               cpumask_clear(mask);
> +               cpumask_set_cpu(irq_index % msi->num_cpus, mask);
> +               irq_set_affinity(virt_msir, mask);

How about testing the return value of irq_set_affinity?

> +               free_cpumask_var(mask);
> +       }
> +
> +       msi->msi_virqs[irq_index] = virt_msir;
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi"},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct device_node *np;
> +       const struct of_device_id *matched_np;
> +       struct xgene_msi *xgene_msi;
> +
> +       np = of_find_matching_node_and_match(NULL,
> +                            xgene_msi_match_table, &matched_np);
> +       if (!np)
> +               return -ENODEV;

This is getting silly, really. You already have the device node pointed
to by pdev...

> +
> +       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
> +       if (!xgene_msi) {
> +               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
> +               return -ENOMEM;
> +       }
> +
> +       xgene_msi->node = np;
> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +       xgene_msi->msi_addr = res->start;
> +
> +       xgene_msi->num_cpus = num_online_cpus();

How are you going to support CPU hotplug? This probably should be
num_possible_cpus(), and you will need a CPU notifier to handle CPUs
being brought up.

> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
> +               if (rc)
> +                       goto error;
> +       }
> +
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +error:
> +       xgene_msi_remove(pdev);
> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

Looks like there is some more work to do...

	M.
-- 
Jazz is not dead. It just smells funny...

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

* [PATCH v5 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-21 15:08                                             ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-21 15:08 UTC (permalink / raw)
  To: linux-arm-kernel

On 21/04/15 05:04, Duc Dang wrote:
> X-Gene v1 SoC supports total 256 MSI/MSIX vectors coalesced into
> 16 HW IRQ lines.

This commit message could be beefed up to describe what this really does.

> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |   6 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 477 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 505 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..c9b61fa 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,17 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..910f5db
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,477 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       int                             *msi_virqs;
> +       int                             num_cpus;
> +};
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> +       u32 group = data->hwirq % NR_HW_IRQS;
> +
> +       msg->address_hi = upper_32_bits(msi->msi_addr);
> +       msg->address_lo = lower_32_bits(msi->msi_addr) +
> +                         (((8 * group) + reg_set) << 16);
> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocate to 8 X-Gene
                                              nit: allocated
> + * v1 cores (2 GIC IRQs for each cores). The MSI vector is moved fom 1
                                nit: core
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +       struct msi_msg msg;
> +       u32 reg_set, group;
> +
> +       if (!desc)
> +               return IRQ_SET_MASK_OK_DONE;

That can't be right. You really should return an error code here.

> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> +       reg_set = irq_data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> +       group = irq_data->hwirq % NR_HW_IRQS;
> +
> +       /* Prepare new MSI msg to steer MSI to target CPU */
> +       msg.address_hi = upper_32_bits(msi->msi_addr);
> +       msg.address_lo = lower_32_bits(msi->msi_addr) +
> +               (((8 * group) + reg_set) << 16);
> +       msg.data = (irq_data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +       /*
> +        * Write new MSI termination address and data into
> +        * PCIe device configuration space
> +        */
> +       pci_write_msi_msg(irq_data->irq, &msg);
> +       return IRQ_SET_MASK_OK_DONE;

That feels like the wrong thing to do. You are duplicating a
functionality that already exists in msi_domain_set_affinity.

You should probably just update hwirq to reflect the new absolute MSI
number, return IRQ_SET_MASK_OK and let compose_msi_msg/write_msi_msg do
the right thing.

> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +       struct xgene_msi *msi = domain->host_data;
> +       int msi_irq;
> +       int i;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = find_first_zero_bit(msi->bitmap, NR_MSI_VEC);
> +       if (msi_irq < NR_MSI_VEC)
> +               set_bit(msi_irq, msi->bitmap);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       /* Check if we found a valid MSI */
> +       if ((msi_irq >= 0) && ((msi_irq % msi->num_cpus) == 0)) {
> +               /* Allocate other vectors as well */
> +               for (i = 1; i < msi->num_cpus; i++) {
> +                       if ((msi_irq + i) >= NR_MSI_VEC) {
> +                               msi_irq = -ENOSPC;
> +                               break;

Eh? Right in the middle of the allocation? You end up with an
inconsistent bitmap, don't you?

Consider using bitmap_find_next_zero_area() to allocate a chunk of 8
consecutive bits, which will give you the MSI for your 8 CPUs.

> +                       }
> +                       if (!test_bit((msi_irq + i), msi->bitmap))
> +                               set_bit(msi_irq + i, msi->bitmap);
> +                       else {
> +                               msi_irq = -ENOSPC;
> +                               break;

Same breakage.

> +                       }
> +               }
> +       }
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq,
> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> +                           handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +       int i;
> +       u32 hwirq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
> +       for (i = 0; i < msi->num_cpus; i++)
> +               if (test_bit(hwirq + i, msi->bitmap))
> +                       clear_bit(hwirq + i, msi->bitmap);

bitmap_release_region()?

> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> +                                           &msi_domain_ops, msi);
> +       if (!msi->domain)
> +               return -ENOMEM;
> +
> +       msi->mchip.of_node = msi->node;
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
> +       if (!xgene_msi->msi_virqs)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> +{
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       struct xgene_msi *xgene_msi;
> +       unsigned int virq;
> +       int msir_index, msir_reg, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp, processed = 0;
> +
> +       chained_irq_enter(chip, desc);
> +
> +       xgene_msi = irq_desc_get_handler_data(desc);
> +
> +       msi_grp = irq - xgene_msi->msi_virqs[0];

Arghh. Absolutely nothing guarantees that your 16 IRQs are contiguous in
the virtual space. This only works by pure luck. Why don't you use the
handler data to contain your group? Here, you're storing you xgene_msi
structure, which is unique anyway.

> +       if (msi_grp >= NR_HW_IRQS) {
> +               chained_irq_exit(chip, desc);
> +               return;
> +       }
> +
> +       /*
> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> +        * If bit x of this register is set (x is 0..7), one or more interupts
> +        * corresponding to MSInIRx is set.
> +        */
> +       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));

Can't you use readl_relaxed?

> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               /*
> +                * Calculate MSInIRx address to read to check for interrupts
> +                * (refer to termination address and data assignment
> +                * described in xgene_compose_msi_msg function)
> +                */
> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> +               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);

Same here?

> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       /*
> +                        * Calculate MSI vector number (refer to the termination
> +                        * address and data assignment described in
> +                        * xgene_compose_msi_msg function)
> +                        */
> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> +                                NR_HW_IRQS) + msi_grp;
> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);

Maybe a comment indicating that the domain is always looked up with the
hwirq as seen from CPU0?

> +                       if (virq != 0)
> +                               generic_handle_irq(virq);
> +                       msir_val &= ~(1 << intr_index);
> +                       processed++;

What's the purpose of processed? Seems fairly useless to me.

> +               }
> +               grp_select &= ~(1 << msir_index);
> +       }
> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               virq = msi->msi_virqs[i];
> +               if (virq != 0)

Under which circumstances could that be zero?

> +                       free_irq(virq, msi);
> +       }
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);

Who frees msi->msi_virqs?

> +       return 0;
> +}
> +
> +static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
> +                                struct platform_device *pdev,
> +                                int irq_index)
> +{
> +       int virt_msir;
> +       cpumask_var_t mask;
> +       int err;
> +
> +       virt_msir = platform_get_irq(pdev, irq_index);
> +       if (virt_msir < 0) {
> +               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                       irq_index);
> +               return -EINVAL;
> +       }
> +
> +       irq_set_chained_handler(virt_msir, xgene_msi_isr);
> +       err = irq_set_handler_data(virt_msir, msi);
> +       if (err) {
> +               dev_err(&pdev->dev, "request irq failed\n");
> +               return err;
> +       }
> +
> +       /*
> +        * Statically allocate MSI GIC IRQs to each CPU core
> +        * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> +        * to each core.
> +        */
> +       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +               cpumask_clear(mask);
> +               cpumask_set_cpu(irq_index % msi->num_cpus, mask);
> +               irq_set_affinity(virt_msir, mask);

How about testing the return value of irq_set_affinity?

> +               free_cpumask_var(mask);
> +       }
> +
> +       msi->msi_virqs[irq_index] = virt_msir;
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi"},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct device_node *np;
> +       const struct of_device_id *matched_np;
> +       struct xgene_msi *xgene_msi;
> +
> +       np = of_find_matching_node_and_match(NULL,
> +                            xgene_msi_match_table, &matched_np);
> +       if (!np)
> +               return -ENODEV;

This is getting silly, really. You already have the device node pointed
to by pdev...

> +
> +       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
> +       if (!xgene_msi) {
> +               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
> +               return -ENOMEM;
> +       }
> +
> +       xgene_msi->node = np;
> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +       xgene_msi->msi_addr = res->start;
> +
> +       xgene_msi->num_cpus = num_online_cpus();

How are you going to support CPU hotplug? This probably should be
num_possible_cpus(), and you will need a CPU notifier to handle CPUs
being brought up.

> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
> +               if (rc)
> +                       goto error;
> +       }
> +
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +error:
> +       xgene_msi_remove(pdev);
> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

Looks like there is some more work to do...

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH v5 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
  2015-04-21  4:04                                           ` Duc Dang
  (?)
@ 2015-04-21 15:19                                             ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-21 15:19 UTC (permalink / raw)
  To: Duc Dang, Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On 21/04/15 05:04, Duc Dang wrote:
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
>  1 file changed, 27 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
> index f1ad9c2..4b719c9 100644
> --- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
> +++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
> @@ -354,6 +354,28 @@
>  			};
>  		};
>  
> +		msi: msi@79000000 {
> +			compatible = "apm,xgene1-msi";
> +			msi-controller;
> +			reg = <0x00 0x79000000 0x0 0x900000>;

I've been repeatedly puzzled by the size of this region. In patch 1, you
say:

+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0                     base_addr
+ * MSI0IR1                     base_addr +  0x10000
+ * ...                         ...
+ * MSI0IR6                     base_addr +  0x60000
+ * MSI0IR7                     base_addr +  0x70000
+ * MSI1IR0                     base_addr +  0x80000
+ * MSI1IR1                     base_addr +  0x90000
+ * ...                         ...
+ * MSI1IR7                     base_addr +  0xF0000
+ * MSI2IR0                     base_addr + 0x100000
+ * ...                         ...
+ * MSIFIR0                     base_addr + 0x780000
+ * MSIFIR1                     base_addr + 0x790000
+ * ...                         ...
+ * MSIFIR7                     base_addr + 0x7F0000

which implies that the size of the region is 0x800000. Or is there
something hidden in the last 16 64k pages?

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH v5 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
@ 2015-04-21 15:19                                             ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-21 15:19 UTC (permalink / raw)
  To: Duc Dang, Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On 21/04/15 05:04, Duc Dang wrote:
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
>  1 file changed, 27 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
> index f1ad9c2..4b719c9 100644
> --- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
> +++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
> @@ -354,6 +354,28 @@
>  			};
>  		};
>  
> +		msi: msi@79000000 {
> +			compatible = "apm,xgene1-msi";
> +			msi-controller;
> +			reg = <0x00 0x79000000 0x0 0x900000>;

I've been repeatedly puzzled by the size of this region. In patch 1, you
say:

+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0                     base_addr
+ * MSI0IR1                     base_addr +  0x10000
+ * ...                         ...
+ * MSI0IR6                     base_addr +  0x60000
+ * MSI0IR7                     base_addr +  0x70000
+ * MSI1IR0                     base_addr +  0x80000
+ * MSI1IR1                     base_addr +  0x90000
+ * ...                         ...
+ * MSI1IR7                     base_addr +  0xF0000
+ * MSI2IR0                     base_addr + 0x100000
+ * ...                         ...
+ * MSIFIR0                     base_addr + 0x780000
+ * MSIFIR1                     base_addr + 0x790000
+ * ...                         ...
+ * MSIFIR7                     base_addr + 0x7F0000

which implies that the size of the region is 0x800000. Or is there
something hidden in the last 16 64k pages?

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* [PATCH v5 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
@ 2015-04-21 15:19                                             ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-21 15:19 UTC (permalink / raw)
  To: linux-arm-kernel

On 21/04/15 05:04, Duc Dang wrote:
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
>  1 file changed, 27 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
> index f1ad9c2..4b719c9 100644
> --- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
> +++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
> @@ -354,6 +354,28 @@
>  			};
>  		};
>  
> +		msi: msi at 79000000 {
> +			compatible = "apm,xgene1-msi";
> +			msi-controller;
> +			reg = <0x00 0x79000000 0x0 0x900000>;

I've been repeatedly puzzled by the size of this region. In patch 1, you
say:

+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0                     base_addr
+ * MSI0IR1                     base_addr +  0x10000
+ * ...                         ...
+ * MSI0IR6                     base_addr +  0x60000
+ * MSI0IR7                     base_addr +  0x70000
+ * MSI1IR0                     base_addr +  0x80000
+ * MSI1IR1                     base_addr +  0x90000
+ * ...                         ...
+ * MSI1IR7                     base_addr +  0xF0000
+ * MSI2IR0                     base_addr + 0x100000
+ * ...                         ...
+ * MSIFIR0                     base_addr + 0x780000
+ * MSIFIR1                     base_addr + 0x790000
+ * ...                         ...
+ * MSIFIR7                     base_addr + 0x7F0000

which implies that the size of the region is 0x800000. Or is there
something hidden in the last 16 64k pages?

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH v5 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
  2015-04-21  4:04                                           ` Duc Dang
  (?)
@ 2015-04-21 15:42                                             ` Mark Rutland
  -1 siblings, 0 replies; 230+ messages in thread
From: Mark Rutland @ 2015-04-21 15:42 UTC (permalink / raw)
  To: Duc Dang
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	Marc Zyngier, Feng Kan, linux-pci, linux-kernel, Loc Ho,
	linux-arm-kernel, Tanmay Inamdar

On Tue, Apr 21, 2015 at 05:04:23AM +0100, Duc Dang wrote:
> The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Please provide a bit of description of what this device is, and please
place the binding patch _before_ the driver and DTS patches.

> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
>  1 file changed, 63 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> 
> diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> new file mode 100644
> index 0000000..0ffdcb3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> @@ -0,0 +1,63 @@
> +* AppliedMicro X-Gene PCIe MSI interface
> +
> +Required properties:
> +
> +- compatible: should contain "apm,xgene1-msi" to identify the core.

What does the core have to do with the MSI controller?

> +- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
> +- reg: A list of physical base address and length for each set of controller
> +       registers.

How many? Which ones? In which order? Do you need reg-names?

> +- interrupts: A list of interrupt outputs of the controller.

How many? Which ones? In which order? Do you need interrupt-names?

You need to define these for *this particular binding*, in order for
them to actually define the contract. An abstract definition is
completely useless for writing or parsing a DT, and as such this
document is just noise.

Please think about the purpose of this document, and write something
appropriate.

[...]

> +		interrupts = <  0x0 0x10 0x4
> +				0x0 0x11 0x4
> +				0x0 0x12 0x4
> +				0x0 0x13 0x4
> +				0x0 0x14 0x4
> +				0x0 0x15 0x4
> +				0x0 0x16 0x4
> +				0x0 0x17 0x4
> +				0x0 0x18 0x4
> +				0x0 0x19 0x4
> +				0x0 0x1a 0x4
> +				0x0 0x1b 0x4
> +				0x0 0x1c 0x4
> +				0x0 0x1d 0x4
> +				0x0 0x1e 0x4
> +				0x0 0x1f 0x4>;

Nit: please bracket list entries individually.

Mark.

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

* Re: [PATCH v5 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
@ 2015-04-21 15:42                                             ` Mark Rutland
  0 siblings, 0 replies; 230+ messages in thread
From: Mark Rutland @ 2015-04-21 15:42 UTC (permalink / raw)
  To: Duc Dang
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	Marc Zyngier, Feng Kan, linux-pci, linux-kernel, Loc Ho,
	linux-arm-kernel, Tanmay Inamdar

On Tue, Apr 21, 2015 at 05:04:23AM +0100, Duc Dang wrote:
> The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Please provide a bit of description of what this device is, and please
place the binding patch _before_ the driver and DTS patches.

> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
>  1 file changed, 63 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> 
> diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> new file mode 100644
> index 0000000..0ffdcb3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> @@ -0,0 +1,63 @@
> +* AppliedMicro X-Gene PCIe MSI interface
> +
> +Required properties:
> +
> +- compatible: should contain "apm,xgene1-msi" to identify the core.

What does the core have to do with the MSI controller?

> +- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
> +- reg: A list of physical base address and length for each set of controller
> +       registers.

How many? Which ones? In which order? Do you need reg-names?

> +- interrupts: A list of interrupt outputs of the controller.

How many? Which ones? In which order? Do you need interrupt-names?

You need to define these for *this particular binding*, in order for
them to actually define the contract. An abstract definition is
completely useless for writing or parsing a DT, and as such this
document is just noise.

Please think about the purpose of this document, and write something
appropriate.

[...]

> +		interrupts = <  0x0 0x10 0x4
> +				0x0 0x11 0x4
> +				0x0 0x12 0x4
> +				0x0 0x13 0x4
> +				0x0 0x14 0x4
> +				0x0 0x15 0x4
> +				0x0 0x16 0x4
> +				0x0 0x17 0x4
> +				0x0 0x18 0x4
> +				0x0 0x19 0x4
> +				0x0 0x1a 0x4
> +				0x0 0x1b 0x4
> +				0x0 0x1c 0x4
> +				0x0 0x1d 0x4
> +				0x0 0x1e 0x4
> +				0x0 0x1f 0x4>;

Nit: please bracket list entries individually.

Mark.

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

* [PATCH v5 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
@ 2015-04-21 15:42                                             ` Mark Rutland
  0 siblings, 0 replies; 230+ messages in thread
From: Mark Rutland @ 2015-04-21 15:42 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 21, 2015 at 05:04:23AM +0100, Duc Dang wrote:
> The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Please provide a bit of description of what this device is, and please
place the binding patch _before_ the driver and DTS patches.

> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
>  1 file changed, 63 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> 
> diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> new file mode 100644
> index 0000000..0ffdcb3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> @@ -0,0 +1,63 @@
> +* AppliedMicro X-Gene PCIe MSI interface
> +
> +Required properties:
> +
> +- compatible: should contain "apm,xgene1-msi" to identify the core.

What does the core have to do with the MSI controller?

> +- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
> +- reg: A list of physical base address and length for each set of controller
> +       registers.

How many? Which ones? In which order? Do you need reg-names?

> +- interrupts: A list of interrupt outputs of the controller.

How many? Which ones? In which order? Do you need interrupt-names?

You need to define these for *this particular binding*, in order for
them to actually define the contract. An abstract definition is
completely useless for writing or parsing a DT, and as such this
document is just noise.

Please think about the purpose of this document, and write something
appropriate.

[...]

> +		interrupts = <  0x0 0x10 0x4
> +				0x0 0x11 0x4
> +				0x0 0x12 0x4
> +				0x0 0x13 0x4
> +				0x0 0x14 0x4
> +				0x0 0x15 0x4
> +				0x0 0x16 0x4
> +				0x0 0x17 0x4
> +				0x0 0x18 0x4
> +				0x0 0x19 0x4
> +				0x0 0x1a 0x4
> +				0x0 0x1b 0x4
> +				0x0 0x1c 0x4
> +				0x0 0x1d 0x4
> +				0x0 0x1e 0x4
> +				0x0 0x1f 0x4>;

Nit: please bracket list entries individually.

Mark.

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

* Re: [PATCH v5 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
  2015-04-21 15:42                                             ` Mark Rutland
  (?)
@ 2015-04-21 17:37                                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21 17:37 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	Marc Zyngier, Feng Kan, linux-pci, linux-kernel, Loc Ho,
	linux-arm-kernel, Tanmay Inamdar

On Tue, Apr 21, 2015 at 8:42 AM, Mark Rutland <mark.rutland@arm.com> wrote:
>
> On Tue, Apr 21, 2015 at 05:04:23AM +0100, Duc Dang wrote:
> > The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'
>
> Please provide a bit of description of what this device is, and please
> place the binding patch _before_ the driver and DTS patches.

I will add in next version of the patch.
>
>
> > Signed-off-by: Duc Dang <dhdang@apm.com>
> > Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> > ---
> >  .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
> >  1 file changed, 63 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> >
> > diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> > new file mode 100644
> > index 0000000..0ffdcb3
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> > @@ -0,0 +1,63 @@
> > +* AppliedMicro X-Gene PCIe MSI interface
> > +
> > +Required properties:
> > +
> > +- compatible: should contain "apm,xgene1-msi" to identify the core.
>
> What does the core have to do with the MSI controller?

'core' here is the MSI controller block. I will use different word to
avoid confusion with CPU core.
>
> > +- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
> > +- reg: A list of physical base address and length for each set of controller
> > +       registers.
>
> How many? Which ones? In which order? Do you need reg-names?

I will add these details in next version of the patch.
>
> > +- interrupts: A list of interrupt outputs of the controller.

I will add these details in next version of the patch.
>
> How many? Which ones? In which order? Do you need interrupt-names?

I will add these details in next version of the patch.
>
> You need to define these for *this particular binding*, in order for
> them to actually define the contract. An abstract definition is
> completely useless for writing or parsing a DT, and as such this
> document is just noise.
>
> Please think about the purpose of this document, and write something
> appropriate.
>
> [...]
>
> > +             interrupts = <  0x0 0x10 0x4
> > +                             0x0 0x11 0x4
> > +                             0x0 0x12 0x4
> > +                             0x0 0x13 0x4
> > +                             0x0 0x14 0x4
> > +                             0x0 0x15 0x4
> > +                             0x0 0x16 0x4
> > +                             0x0 0x17 0x4
> > +                             0x0 0x18 0x4
> > +                             0x0 0x19 0x4
> > +                             0x0 0x1a 0x4
> > +                             0x0 0x1b 0x4
> > +                             0x0 0x1c 0x4
> > +                             0x0 0x1d 0x4
> > +                             0x0 0x1e 0x4
> > +                             0x0 0x1f 0x4>;
>
> Nit: please bracket list entries individually.

Thanks for your comment, I will address them on next version of the patch.

>
> Mark.

Regards,
Duc Dang.

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

* Re: [PATCH v5 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
@ 2015-04-21 17:37                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21 17:37 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	Marc Zyngier, Feng Kan, linux-pci, linux-kernel, Loc Ho,
	linux-arm-kernel, Tanmay Inamdar

On Tue, Apr 21, 2015 at 8:42 AM, Mark Rutland <mark.rutland@arm.com> wrote:
>
> On Tue, Apr 21, 2015 at 05:04:23AM +0100, Duc Dang wrote:
> > The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'
>
> Please provide a bit of description of what this device is, and please
> place the binding patch _before_ the driver and DTS patches.

I will add in next version of the patch.
>
>
> > Signed-off-by: Duc Dang <dhdang@apm.com>
> > Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> > ---
> >  .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
> >  1 file changed, 63 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> >
> > diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> > new file mode 100644
> > index 0000000..0ffdcb3
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> > @@ -0,0 +1,63 @@
> > +* AppliedMicro X-Gene PCIe MSI interface
> > +
> > +Required properties:
> > +
> > +- compatible: should contain "apm,xgene1-msi" to identify the core.
>
> What does the core have to do with the MSI controller?

'core' here is the MSI controller block. I will use different word to
avoid confusion with CPU core.
>
> > +- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
> > +- reg: A list of physical base address and length for each set of controller
> > +       registers.
>
> How many? Which ones? In which order? Do you need reg-names?

I will add these details in next version of the patch.
>
> > +- interrupts: A list of interrupt outputs of the controller.

I will add these details in next version of the patch.
>
> How many? Which ones? In which order? Do you need interrupt-names?

I will add these details in next version of the patch.
>
> You need to define these for *this particular binding*, in order for
> them to actually define the contract. An abstract definition is
> completely useless for writing or parsing a DT, and as such this
> document is just noise.
>
> Please think about the purpose of this document, and write something
> appropriate.
>
> [...]
>
> > +             interrupts = <  0x0 0x10 0x4
> > +                             0x0 0x11 0x4
> > +                             0x0 0x12 0x4
> > +                             0x0 0x13 0x4
> > +                             0x0 0x14 0x4
> > +                             0x0 0x15 0x4
> > +                             0x0 0x16 0x4
> > +                             0x0 0x17 0x4
> > +                             0x0 0x18 0x4
> > +                             0x0 0x19 0x4
> > +                             0x0 0x1a 0x4
> > +                             0x0 0x1b 0x4
> > +                             0x0 0x1c 0x4
> > +                             0x0 0x1d 0x4
> > +                             0x0 0x1e 0x4
> > +                             0x0 0x1f 0x4>;
>
> Nit: please bracket list entries individually.

Thanks for your comment, I will address them on next version of the patch.

>
> Mark.

Regards,
Duc Dang.

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

* [PATCH v5 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
@ 2015-04-21 17:37                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21 17:37 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 21, 2015 at 8:42 AM, Mark Rutland <mark.rutland@arm.com> wrote:
>
> On Tue, Apr 21, 2015 at 05:04:23AM +0100, Duc Dang wrote:
> > The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'
>
> Please provide a bit of description of what this device is, and please
> place the binding patch _before_ the driver and DTS patches.

I will add in next version of the patch.
>
>
> > Signed-off-by: Duc Dang <dhdang@apm.com>
> > Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> > ---
> >  .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
> >  1 file changed, 63 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> >
> > diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> > new file mode 100644
> > index 0000000..0ffdcb3
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
> > @@ -0,0 +1,63 @@
> > +* AppliedMicro X-Gene PCIe MSI interface
> > +
> > +Required properties:
> > +
> > +- compatible: should contain "apm,xgene1-msi" to identify the core.
>
> What does the core have to do with the MSI controller?

'core' here is the MSI controller block. I will use different word to
avoid confusion with CPU core.
>
> > +- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
> > +- reg: A list of physical base address and length for each set of controller
> > +       registers.
>
> How many? Which ones? In which order? Do you need reg-names?

I will add these details in next version of the patch.
>
> > +- interrupts: A list of interrupt outputs of the controller.

I will add these details in next version of the patch.
>
> How many? Which ones? In which order? Do you need interrupt-names?

I will add these details in next version of the patch.
>
> You need to define these for *this particular binding*, in order for
> them to actually define the contract. An abstract definition is
> completely useless for writing or parsing a DT, and as such this
> document is just noise.
>
> Please think about the purpose of this document, and write something
> appropriate.
>
> [...]
>
> > +             interrupts = <  0x0 0x10 0x4
> > +                             0x0 0x11 0x4
> > +                             0x0 0x12 0x4
> > +                             0x0 0x13 0x4
> > +                             0x0 0x14 0x4
> > +                             0x0 0x15 0x4
> > +                             0x0 0x16 0x4
> > +                             0x0 0x17 0x4
> > +                             0x0 0x18 0x4
> > +                             0x0 0x19 0x4
> > +                             0x0 0x1a 0x4
> > +                             0x0 0x1b 0x4
> > +                             0x0 0x1c 0x4
> > +                             0x0 0x1d 0x4
> > +                             0x0 0x1e 0x4
> > +                             0x0 0x1f 0x4>;
>
> Nit: please bracket list entries individually.

Thanks for your comment, I will address them on next version of the patch.

>
> Mark.

Regards,
Duc Dang.

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

* Re: [PATCH v5 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
  2015-04-21 15:19                                             ` Marc Zyngier
  (?)
@ 2015-04-21 18:01                                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21 18:01 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Tue, Apr 21, 2015 at 8:19 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 21/04/15 05:04, Duc Dang wrote:
>> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
>>  1 file changed, 27 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
>> index f1ad9c2..4b719c9 100644
>> --- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
>> +++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
>> @@ -354,6 +354,28 @@
>>                       };
>>               };
>>
>> +             msi: msi@79000000 {
>> +                     compatible = "apm,xgene1-msi";
>> +                     msi-controller;
>> +                     reg = <0x00 0x79000000 0x0 0x900000>;
>
> I've been repeatedly puzzled by the size of this region. In patch 1, you
> say:
>
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
>
> which implies that the size of the region is 0x800000. Or is there
> something hidden in the last 16 64k pages?

The registers listed in the first patch are termination registers.
>From offset 0x800000 to 0x8f0000, the MSI controller provides status
registers for software to check if there is pending MSI interrupt in
each MSI group (in the code we read MSI_INTx registers starting from
0x800000 to check if there is an MSI interrupt pending).
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...

Regards,
Duc Dang.

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

* Re: [PATCH v5 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
@ 2015-04-21 18:01                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21 18:01 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Tue, Apr 21, 2015 at 8:19 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 21/04/15 05:04, Duc Dang wrote:
>> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
>>  1 file changed, 27 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
>> index f1ad9c2..4b719c9 100644
>> --- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
>> +++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
>> @@ -354,6 +354,28 @@
>>                       };
>>               };
>>
>> +             msi: msi@79000000 {
>> +                     compatible = "apm,xgene1-msi";
>> +                     msi-controller;
>> +                     reg = <0x00 0x79000000 0x0 0x900000>;
>
> I've been repeatedly puzzled by the size of this region. In patch 1, you
> say:
>
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
>
> which implies that the size of the region is 0x800000. Or is there
> something hidden in the last 16 64k pages?

The registers listed in the first patch are termination registers.
>From offset 0x800000 to 0x8f0000, the MSI controller provides status
registers for software to check if there is pending MSI interrupt in
each MSI group (in the code we read MSI_INTx registers starting from
0x800000 to check if there is an MSI interrupt pending).
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...

Regards,
Duc Dang.

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

* [PATCH v5 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
@ 2015-04-21 18:01                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-21 18:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 21, 2015 at 8:19 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 21/04/15 05:04, Duc Dang wrote:
>> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
>>  1 file changed, 27 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
>> index f1ad9c2..4b719c9 100644
>> --- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
>> +++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
>> @@ -354,6 +354,28 @@
>>                       };
>>               };
>>
>> +             msi: msi at 79000000 {
>> +                     compatible = "apm,xgene1-msi";
>> +                     msi-controller;
>> +                     reg = <0x00 0x79000000 0x0 0x900000>;
>
> I've been repeatedly puzzled by the size of this region. In patch 1, you
> say:
>
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
>
> which implies that the size of the region is 0x800000. Or is there
> something hidden in the last 16 64k pages?

The registers listed in the first patch are termination registers.
>From offset 0x800000 to 0x8f0000, the MSI controller provides status
registers for software to check if there is pending MSI interrupt in
each MSI group (in the code we read MSI_INTx registers starting from
0x800000 to check if there is an MSI interrupt pending).
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...

Regards,
Duc Dang.

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

* Re: [PATCH v5 0/4]PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-04-21  4:04                                           ` Duc Dang
@ 2015-04-22  3:02                                             ` Jon Masters
  -1 siblings, 0 replies; 230+ messages in thread
From: Jon Masters @ 2015-04-22  3:02 UTC (permalink / raw)
  To: Duc Dang, Bjorn Helgaas, Arnd Bergmann, Grant Likely,
	Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On 04/21/2015 12:04 AM, Duc Dang wrote:
> This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI
> vectors this driver supports is reduced to 256 to maintain the correct set_affinity 
> behavior for each MSI.

Quick note that I have tested the previous several versions of this
patch series on several platforms and will test v6 once available with
the pending feedback addressed.

Jon.


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

* [PATCH v5 0/4]PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-04-22  3:02                                             ` Jon Masters
  0 siblings, 0 replies; 230+ messages in thread
From: Jon Masters @ 2015-04-22  3:02 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/21/2015 12:04 AM, Duc Dang wrote:
> This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI
> vectors this driver supports is reduced to 256 to maintain the correct set_affinity 
> behavior for each MSI.

Quick note that I have tested the previous several versions of this
patch series on several platforms and will test v6 once available with
the pending feedback addressed.

Jon.

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

* Re: [PATCH v5 0/4]PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-04-21  4:04                                           ` Duc Dang
@ 2015-04-22  3:02                                             ` Jon Masters
  -1 siblings, 0 replies; 230+ messages in thread
From: Jon Masters @ 2015-04-22  3:02 UTC (permalink / raw)
  To: Duc Dang, Bjorn Helgaas, Arnd Bergmann, Grant Likely,
	Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On 04/21/2015 12:04 AM, Duc Dang wrote:
> This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI
> vectors this driver supports is reduced to 256 to maintain the correct set_affinity 
> behavior for each MSI.

Quick note that I have tested the previous several versions of this
patch series on several platforms and will test v6 once available with
the pending feedback addressed.

Jon.


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

* [PATCH v5 0/4]PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-04-22  3:02                                             ` Jon Masters
  0 siblings, 0 replies; 230+ messages in thread
From: Jon Masters @ 2015-04-22  3:02 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/21/2015 12:04 AM, Duc Dang wrote:
> This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI
> vectors this driver supports is reduced to 256 to maintain the correct set_affinity 
> behavior for each MSI.

Quick note that I have tested the previous several versions of this
patch series on several platforms and will test v6 once available with
the pending feedback addressed.

Jon.

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

* [PATCH v6 0/4]PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-04-21 15:08                                             ` Marc Zyngier
@ 2015-04-22  6:15                                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-22  6:15 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI
vectors this driver supports is reduced to 256 to maintain the correct set_affinity
behavior for each MSI.

v6 changes:
	1. Correctly allocate MSI with bitmap_find_next_zero_area
	2. Add notifier for CPU hotplug case
	3. Driver clean up
	4. Add more detailed information into device tree binding document
	   and move the device tree binding patch before the driver/dts patch

v5 changes:
        1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs
        for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer
        them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1
        supports is reduced to 256.

v4 changes:
        1. Remove affinity setting for each MSI
        2. Add description about register layout, MSI termination address and data
        3. Correct total number of MSI vectors to 2048
        4. Clean up error messages
        5. Remove unused module code

v3 changes:
        1. Implement MSI support using PCI MSI IRQ domain
        2. Only use msi_controller to store IRQ domain
v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
 
 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   8 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 504 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 637 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1


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

* [PATCH v6 0/4]PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-04-22  6:15                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-22  6:15 UTC (permalink / raw)
  To: linux-arm-kernel

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI
vectors this driver supports is reduced to 256 to maintain the correct set_affinity
behavior for each MSI.

v6 changes:
	1. Correctly allocate MSI with bitmap_find_next_zero_area
	2. Add notifier for CPU hotplug case
	3. Driver clean up
	4. Add more detailed information into device tree binding document
	   and move the device tree binding patch before the driver/dts patch

v5 changes:
        1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs
        for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer
        them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1
        supports is reduced to 256.

v4 changes:
        1. Remove affinity setting for each MSI
        2. Add description about register layout, MSI termination address and data
        3. Correct total number of MSI vectors to 2048
        4. Clean up error messages
        5. Remove unused module code

v3 changes:
        1. Implement MSI support using PCI MSI IRQ domain
        2. Only use msi_controller to store IRQ domain
v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
 
 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   8 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 504 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 637 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1

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

* [PATCH v6 1/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
  2015-04-22  6:15                                               ` Duc Dang
@ 2015-04-22  6:15                                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-22  6:15 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

X-Gene v1 PCIe MSI controller block provides PCIE MSI functionality for 5 X-Gene v1
PCIE ports

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..36d881c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+	      X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+	      interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi@79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = 	<0x0 0x10 0x4>
+				<0x0 0x11 0x4>
+				<0x0 0x12 0x4>
+				<0x0 0x13 0x4>
+				<0x0 0x14 0x4>
+				<0x0 0x15 0x4>
+				<0x0 0x16 0x4>
+				<0x0 0x17 0x4>
+				<0x0 0x18 0x4>
+				<0x0 0x19 0x4>
+				<0x0 0x1a 0x4>
+				<0x0 0x1b 0x4>
+				<0x0 0x1c 0x4>
+				<0x0 0x1d 0x4>
+				<0x0 0x1e 0x4>
+				<0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie@1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1


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

* [PATCH v6 1/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
@ 2015-04-22  6:15                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-22  6:15 UTC (permalink / raw)
  To: linux-arm-kernel

X-Gene v1 PCIe MSI controller block provides PCIE MSI functionality for 5 X-Gene v1
PCIE ports

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..36d881c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+	      X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+	      interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = 	<0x0 0x10 0x4>
+				<0x0 0x11 0x4>
+				<0x0 0x12 0x4>
+				<0x0 0x13 0x4>
+				<0x0 0x14 0x4>
+				<0x0 0x15 0x4>
+				<0x0 0x16 0x4>
+				<0x0 0x17 0x4>
+				<0x0 0x18 0x4>
+				<0x0 0x19 0x4>
+				<0x0 0x1a 0x4>
+				<0x0 0x1b 0x4>
+				<0x0 0x1c 0x4>
+				<0x0 0x1d 0x4>
+				<0x0 0x1e 0x4>
+				<0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1

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

* [PATCH v6 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-22  6:15                                               ` Duc Dang
@ 2015-04-22  6:15                                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-22  6:15 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports. 

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity 
correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene 
v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
is moved around these HW IRQs lines. With this approach, the total MSI vectors this 
driver supports is reduced to 256.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 drivers/pci/host/Kconfig         |   8 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 504 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 534 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9..9f1e2b5 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,19 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+	help
+	  Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c..f39bde3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..8bbf925
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,504 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0			0x000000
+#define MSI_INT0		0x800000
+#define IDX_PER_GROUP		8
+#define IRQS_PER_IDX		16
+#define NR_HW_IRQS		16
+#define NR_MSI_VEC		(IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	u64				msi_addr;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	int				*msi_virqs;
+	int				num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0			base_addr
+ * MSI0IR1			base_addr +  0x10000
+ * ...				...
+ * MSI0IR6			base_addr +  0x60000
+ * MSI0IR7			base_addr +  0x70000
+ * MSI1IR0			base_addr +  0x80000
+ * MSI1IR1			base_addr +  0x90000
+ * ...				...
+ * MSI1IR7			base_addr +  0xF0000
+ * MSI2IR0			base_addr + 0x100000
+ * ...				...
+ * MSIFIR0			base_addr + 0x780000
+ * MSIFIR1			base_addr + 0x790000
+ * ...				...
+ * MSIFIR7			base_addr + 0x7F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *		Group 0: 0-255
+ *		Group 1: 256-511
+ *		Group 2: 512-767
+ *		...
+ *		Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *	As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *		Group 0: 0-15
+ *		Group 1: 16-32
+ *		...
+ *		Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+	u32 group = data->hwirq % NR_HW_IRQS;
+
+	msg->address_hi = upper_32_bits(msi->msi_addr);
+	msg->address_lo = lower_32_bits(msi->msi_addr) +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+				  const struct cpumask *mask, bool force)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+	struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
+	int target_cpu = cpumask_first(mask);
+	int curr_cpu;
+
+	if (!desc)
+		return -EINVAL;
+
+	curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
+	if (curr_cpu == target_cpu)
+		return IRQ_SET_MASK_OK_DONE;
+
+	/* Update MSI number to target the new CPU */
+	irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+
+	return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+	struct xgene_msi *msi = domain->host_data;
+	int msi_irq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+					     msi->num_cpus, 0);
+	if (msi_irq < NR_MSI_VEC)
+		bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+	else
+		msi_irq = -ENOSPC;
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq,
+			    &xgene_msi_bottom_irq_chip, domain->host_data,
+			    handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+	u32 hwirq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
+	bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+					    &msi_domain_ops, msi);
+	if (!msi->domain)
+		return -ENOMEM;
+
+	msi->mchip.of_node = msi->node;
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
+	if (!xgene_msi->msi_virqs)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct xgene_msi *xgene_msi;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp;
+	int i;
+
+	chained_irq_enter(chip, desc);
+
+	xgene_msi = irq_desc_get_handler_data(desc);
+
+	msi_grp = NR_HW_IRQS;
+	for (i = 0; i < NR_HW_IRQS; i++)
+		if (irq == xgene_msi->msi_virqs[i]) {
+			msi_grp = i;
+			break;
+		}
+	if (msi_grp >= NR_HW_IRQS) {
+		chained_irq_exit(chip, desc);
+		return;
+	}
+
+	/*
+	 * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+	 * If bit x of this register is set (x is 0..7), one or more interupts
+	 * corresponding to MSInIRx is set.
+	 */
+	grp_select = readl_relaxed(xgene_msi->msi_regs +
+				   MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		/*
+		 * Calculate MSInIRx address to read to check for interrupts
+		 * (refer to termination address and data assignment
+		 * described in xgene_compose_msi_msg function)
+		 */
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl_relaxed(xgene_msi->msi_regs +
+					 MSI_IR0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			/*
+			 * Calculate MSI vector number (refer to the termination
+			 * address and data assignment described in
+			 * xgene_compose_msi_msg function)
+			 */
+			hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+				 NR_HW_IRQS) + msi_grp;
+			/*
+			 * As we have multiple hw_irq that maps to single MSI,
+			 * always look up the virq using the hw_irq as seen from
+			 * CPU0
+			 */
+			hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		virq = msi->msi_virqs[i];
+		if (virq != 0)
+			free_irq(virq, msi);
+	}
+	kfree(msi->msi_virqs);
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static void xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	cpumask_var_t mask;
+	int i;
+	int err;
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		if (i % msi->num_cpus != cpu)
+			continue;
+		if (!msi->msi_virqs[i])
+			continue;
+
+		irq_set_chained_handler(msi->msi_virqs[i], xgene_msi_isr);
+		err = irq_set_handler_data(msi->msi_virqs[i], msi);
+		if (err)
+			continue;
+
+		/*
+		 * Statically allocate MSI GIC IRQs to each CPU core
+		 * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+		 * to each core.
+		 */
+		if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+			cpumask_clear(mask);
+			cpumask_set_cpu(cpu, mask);
+			err = irq_set_affinity(msi->msi_virqs[i], mask);
+			if (err) {
+				free_irq(msi->msi_virqs[i], msi);
+				free_cpumask_var(mask);
+				continue;
+			}
+			free_cpumask_var(mask);
+		}
+	}
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+	/*
+	 * Kernel takes care of moving MSI interrupts that
+	 * are steered to this CPU to another online CPU
+	 * and freeing the interrupt handlers of GIC IRQs
+	 * allocated for this CPU, so simply return here.
+	 */
+	return;
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+				  unsigned long action, void *hcpu)
+{
+	unsigned cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		xgene_msi_hwirq_alloc(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		xgene_msi_hwirq_free(cpu);
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+	.notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi"},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct xgene_msi *xgene_msi;
+	unsigned int cpu;
+	int virt_msir;
+
+	xgene_msi = &xgene_msi_ctrl;
+	xgene_msi->node = pdev->dev.of_node;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	xgene_msi->msi_addr = res->start;
+
+	xgene_msi->num_cpus = num_possible_cpus();
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		virt_msir = platform_get_irq(pdev, irq_index);
+		if (virt_msir < 0) {
+			dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+				irq_index);
+			rc = -EINVAL;
+			goto error;
+		}
+		xgene_msi->msi_virqs[irq_index] = virt_msir;
+	}
+
+	cpu_notifier_register_begin();
+
+	for_each_online_cpu(cpu)
+		xgene_msi_hwirq_alloc(cpu);
+
+	rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+		cpu_notifier_register_done();
+		goto error;
+	}
+
+	cpu_notifier_register_done();
+
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 22751ed..3e6faa1 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1


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

* [PATCH v6 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-22  6:15                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-22  6:15 UTC (permalink / raw)
  To: linux-arm-kernel

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports. 

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity 
correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene 
v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
is moved around these HW IRQs lines. With this approach, the total MSI vectors this 
driver supports is reduced to 256.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 drivers/pci/host/Kconfig         |   8 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 504 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 534 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9..9f1e2b5 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,19 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+	help
+	  Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c..f39bde3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..8bbf925
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,504 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0			0x000000
+#define MSI_INT0		0x800000
+#define IDX_PER_GROUP		8
+#define IRQS_PER_IDX		16
+#define NR_HW_IRQS		16
+#define NR_MSI_VEC		(IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	u64				msi_addr;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	int				*msi_virqs;
+	int				num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0			base_addr
+ * MSI0IR1			base_addr +  0x10000
+ * ...				...
+ * MSI0IR6			base_addr +  0x60000
+ * MSI0IR7			base_addr +  0x70000
+ * MSI1IR0			base_addr +  0x80000
+ * MSI1IR1			base_addr +  0x90000
+ * ...				...
+ * MSI1IR7			base_addr +  0xF0000
+ * MSI2IR0			base_addr + 0x100000
+ * ...				...
+ * MSIFIR0			base_addr + 0x780000
+ * MSIFIR1			base_addr + 0x790000
+ * ...				...
+ * MSIFIR7			base_addr + 0x7F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *		Group 0: 0-255
+ *		Group 1: 256-511
+ *		Group 2: 512-767
+ *		...
+ *		Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *	As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *		Group 0: 0-15
+ *		Group 1: 16-32
+ *		...
+ *		Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+	u32 group = data->hwirq % NR_HW_IRQS;
+
+	msg->address_hi = upper_32_bits(msi->msi_addr);
+	msg->address_lo = lower_32_bits(msi->msi_addr) +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+				  const struct cpumask *mask, bool force)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+	struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
+	int target_cpu = cpumask_first(mask);
+	int curr_cpu;
+
+	if (!desc)
+		return -EINVAL;
+
+	curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
+	if (curr_cpu == target_cpu)
+		return IRQ_SET_MASK_OK_DONE;
+
+	/* Update MSI number to target the new CPU */
+	irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+
+	return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+	struct xgene_msi *msi = domain->host_data;
+	int msi_irq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+					     msi->num_cpus, 0);
+	if (msi_irq < NR_MSI_VEC)
+		bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+	else
+		msi_irq = -ENOSPC;
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq,
+			    &xgene_msi_bottom_irq_chip, domain->host_data,
+			    handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+	u32 hwirq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
+	bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+					    &msi_domain_ops, msi);
+	if (!msi->domain)
+		return -ENOMEM;
+
+	msi->mchip.of_node = msi->node;
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
+	if (!xgene_msi->msi_virqs)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct xgene_msi *xgene_msi;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp;
+	int i;
+
+	chained_irq_enter(chip, desc);
+
+	xgene_msi = irq_desc_get_handler_data(desc);
+
+	msi_grp = NR_HW_IRQS;
+	for (i = 0; i < NR_HW_IRQS; i++)
+		if (irq == xgene_msi->msi_virqs[i]) {
+			msi_grp = i;
+			break;
+		}
+	if (msi_grp >= NR_HW_IRQS) {
+		chained_irq_exit(chip, desc);
+		return;
+	}
+
+	/*
+	 * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+	 * If bit x of this register is set (x is 0..7), one or more interupts
+	 * corresponding to MSInIRx is set.
+	 */
+	grp_select = readl_relaxed(xgene_msi->msi_regs +
+				   MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		/*
+		 * Calculate MSInIRx address to read to check for interrupts
+		 * (refer to termination address and data assignment
+		 * described in xgene_compose_msi_msg function)
+		 */
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl_relaxed(xgene_msi->msi_regs +
+					 MSI_IR0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			/*
+			 * Calculate MSI vector number (refer to the termination
+			 * address and data assignment described in
+			 * xgene_compose_msi_msg function)
+			 */
+			hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+				 NR_HW_IRQS) + msi_grp;
+			/*
+			 * As we have multiple hw_irq that maps to single MSI,
+			 * always look up the virq using the hw_irq as seen from
+			 * CPU0
+			 */
+			hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		virq = msi->msi_virqs[i];
+		if (virq != 0)
+			free_irq(virq, msi);
+	}
+	kfree(msi->msi_virqs);
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static void xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	cpumask_var_t mask;
+	int i;
+	int err;
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		if (i % msi->num_cpus != cpu)
+			continue;
+		if (!msi->msi_virqs[i])
+			continue;
+
+		irq_set_chained_handler(msi->msi_virqs[i], xgene_msi_isr);
+		err = irq_set_handler_data(msi->msi_virqs[i], msi);
+		if (err)
+			continue;
+
+		/*
+		 * Statically allocate MSI GIC IRQs to each CPU core
+		 * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+		 * to each core.
+		 */
+		if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+			cpumask_clear(mask);
+			cpumask_set_cpu(cpu, mask);
+			err = irq_set_affinity(msi->msi_virqs[i], mask);
+			if (err) {
+				free_irq(msi->msi_virqs[i], msi);
+				free_cpumask_var(mask);
+				continue;
+			}
+			free_cpumask_var(mask);
+		}
+	}
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+	/*
+	 * Kernel takes care of moving MSI interrupts that
+	 * are steered to this CPU to another online CPU
+	 * and freeing the interrupt handlers of GIC IRQs
+	 * allocated for this CPU, so simply return here.
+	 */
+	return;
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+				  unsigned long action, void *hcpu)
+{
+	unsigned cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		xgene_msi_hwirq_alloc(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		xgene_msi_hwirq_free(cpu);
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+	.notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi"},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct xgene_msi *xgene_msi;
+	unsigned int cpu;
+	int virt_msir;
+
+	xgene_msi = &xgene_msi_ctrl;
+	xgene_msi->node = pdev->dev.of_node;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	xgene_msi->msi_addr = res->start;
+
+	xgene_msi->num_cpus = num_possible_cpus();
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		virt_msir = platform_get_irq(pdev, irq_index);
+		if (virt_msir < 0) {
+			dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+				irq_index);
+			rc = -EINVAL;
+			goto error;
+		}
+		xgene_msi->msi_virqs[irq_index] = virt_msir;
+	}
+
+	cpu_notifier_register_begin();
+
+	for_each_online_cpu(cpu)
+		xgene_msi_hwirq_alloc(cpu);
+
+	rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+		cpu_notifier_register_done();
+		goto error;
+	}
+
+	cpu_notifier_register_done();
+
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 22751ed..3e6faa1 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1

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

* [PATCH v6 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
  2015-04-22  6:15                                               ` Duc Dang
@ 2015-04-22  6:15                                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-22  6:15 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..4b719c9 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi@79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie@1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie@1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie@1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie@1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie@1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial@1c020000 {
-- 
1.9.1


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

* [PATCH v6 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
@ 2015-04-22  6:15                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-22  6:15 UTC (permalink / raw)
  To: linux-arm-kernel

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..4b719c9 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
-- 
1.9.1

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

* [PATCH v6 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
  2015-04-22  6:15                                               ` Duc Dang
@ 2015-04-22  6:15                                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-22  6:15 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddc5a8c..a1b119b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7490,6 +7490,14 @@ L:	linux-pci@vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci@vger.kernel.org
+L:	linux-arm-kernel@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia@lists.infradead.org
-- 
1.9.1


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

* [PATCH v6 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
@ 2015-04-22  6:15                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-04-22  6:15 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddc5a8c..a1b119b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7490,6 +7490,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
-- 
1.9.1

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

* Re: [PATCH v6 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-22  6:15                                               ` Duc Dang
  (?)
@ 2015-04-22 12:50                                                 ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-22 12:50 UTC (permalink / raw)
  To: Duc Dang
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Wed, 22 Apr 2015 07:15:09 +0100
Duc Dang <dhdang@apm.com> wrote:

> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports.
> 
> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
> driver supports is reduced to 256.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |   8 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 504 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 534 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..9f1e2b5 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,19 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +       help
> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..8bbf925
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,504 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       int                             *msi_virqs;
> +       int                             num_cpus;
> +};
> +
> +/* Global data */
> +static struct xgene_msi xgene_msi_ctrl;
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> +       u32 group = data->hwirq % NR_HW_IRQS;
> +
> +       msg->address_hi = upper_32_bits(msi->msi_addr);
> +       msg->address_lo = lower_32_bits(msi->msi_addr) +
> +                         (((8 * group) + reg_set) << 16);
> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +
> +       if (!desc)
> +               return -EINVAL;

I still don't understand under which circumstances this could ever be
NULL. You got here because someone has created this interrupt, and
inserted it in the domain. Someone also found a way to reference it,
and call this code. It must have been initialized the first place.

Also, nothing uses this variable in what follows, so please drop it
unless you can prove that desc can be NULL is that it is unsafe to
proceed further.

> +
> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       /* Update MSI number to target the new CPU */
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> +
> +       return IRQ_SET_MASK_OK;
> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +       struct xgene_msi *msi = domain->host_data;
> +       int msi_irq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
> +                                            msi->num_cpus, 0);
> +       if (msi_irq < NR_MSI_VEC)
> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq,
> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> +                           handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +       u32 hwirq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> +                                           &msi_domain_ops, msi);
> +       if (!msi->domain)
> +               return -ENOMEM;
> +
> +       msi->mchip.of_node = msi->node;
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
> +       if (!xgene_msi->msi_virqs)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> +{
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       struct xgene_msi *xgene_msi;
> +       unsigned int virq;
> +       int msir_index, msir_reg, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp;
> +       int i;
> +
> +       chained_irq_enter(chip, desc);
> +
> +       xgene_msi = irq_desc_get_handler_data(desc);
> +
> +       msi_grp = NR_HW_IRQS;
> +       for (i = 0; i < NR_HW_IRQS; i++)
> +               if (irq == xgene_msi->msi_virqs[i]) {
> +                       msi_grp = i;
> +                       break;
> +               }

Oh, please!!! In v3, you were giving silly reasons to shave one cycle
off a slow path, but you now seem pretty happy to run this loop on the
bloody hot path!

I already suggested that you actually save the group in handler_data,
and reference the global xgene_msi, since it is guaranteed to be
unique. If you really want to pointlessly follow pointers, you could
replace your msi_virqs array with an array of:

struct xgene_msi_group {
	struct xgene_msi	*xgene_msi;
	int			gic_irq;
	u32			msi_grp;
};

and make the relevant structure pointer to by desc->handler_data. That
will give you the information you need once and for all, without any
silly, cycle wasting loop.

> +       if (msi_grp >= NR_HW_IRQS) {
> +               chained_irq_exit(chip, desc);
> +               return;
> +       }

And I really can't see how this can ever be valid.

> +
> +       /*
> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> +        * If bit x of this register is set (x is 0..7), one or more interupts
> +        * corresponding to MSInIRx is set.
> +        */
> +       grp_select = readl_relaxed(xgene_msi->msi_regs +
> +                                  MSI_INT0 + (msi_grp << 16));
> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               /*
> +                * Calculate MSInIRx address to read to check for interrupts
> +                * (refer to termination address and data assignment
> +                * described in xgene_compose_msi_msg function)
> +                */
> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> +               msir_val = readl_relaxed(xgene_msi->msi_regs +
> +                                        MSI_IR0 + msir_reg);
> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       /*
> +                        * Calculate MSI vector number (refer to the termination
> +                        * address and data assignment described in
> +                        * xgene_compose_msi_msg function)
> +                        */
> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> +                                NR_HW_IRQS) + msi_grp;
> +                       /*
> +                        * As we have multiple hw_irq that maps to single MSI,
> +                        * always look up the virq using the hw_irq as seen from
> +                        * CPU0
> +                        */
> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> +                       if (virq != 0)
> +                               generic_handle_irq(virq);

The (virq == 0) case certainly deserves a warning, because it indicates
something went horribly wrong somewhere.

> +                       msir_val &= ~(1 << intr_index);
> +               }
> +               grp_select &= ~(1 << msir_index);
> +       }
> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               virq = msi->msi_virqs[i];
> +               if (virq != 0)
> +                       free_irq(virq, msi);
> +       }
> +       kfree(msi->msi_virqs);
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_hwirq_alloc(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       cpumask_var_t mask;
> +       int i;
> +       int err;
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               if (i % msi->num_cpus != cpu)
> +                       continue;
> +               if (!msi->msi_virqs[i])
> +                       continue;
> +
> +               irq_set_chained_handler(msi->msi_virqs[i], xgene_msi_isr);
> +               err = irq_set_handler_data(msi->msi_virqs[i], msi);

See? msi is *always* set to &xgene_msi_ctrl. So just encode the group
or use a structure similar to the one I've outlined above.

> +               if (err)
> +                       continue;

So we got a critical error that just makes the driver unusable, and yet
we silently continue? Sounds like a plan...

> +
> +               /*
> +                * Statically allocate MSI GIC IRQs to each CPU core
> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> +                * to each core.
> +                */
> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +                       cpumask_clear(mask);
> +                       cpumask_set_cpu(cpu, mask);
> +                       err = irq_set_affinity(msi->msi_virqs[i], mask);
> +                       if (err) {
> +                               free_irq(msi->msi_virqs[i], msi);
> +                               free_cpumask_var(mask);
> +                               continue;

Same here...

> +                       }
> +                       free_cpumask_var(mask);
> +               }
> +       }
> +}
> +
> +static void xgene_msi_hwirq_free(unsigned int cpu)
> +{
> +       /*
> +        * Kernel takes care of moving MSI interrupts that
> +        * are steered to this CPU to another online CPU
> +        * and freeing the interrupt handlers of GIC IRQs
> +        * allocated for this CPU, so simply return here.
> +        */

Two things:
- I couldn't find the code that backs this statement. MSIs (actually
  all interrupts) will indeed be moved, but I can't see how the kernel
  is going to free GIC interrupt. Care to point me to it?
- Assuming this statement is true, why do we need this function at all?


> +       return;
> +}
> +
> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
> +                                 unsigned long action, void *hcpu)
> +{
> +       unsigned cpu = (unsigned long)hcpu;
> +
> +       switch (action) {
> +       case CPU_ONLINE:
> +       case CPU_ONLINE_FROZEN:
> +               xgene_msi_hwirq_alloc(cpu);
> +               break;
> +       case CPU_DEAD:
> +       case CPU_DEAD_FROZEN:
> +               xgene_msi_hwirq_free(cpu);
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       return NOTIFY_OK;
> +}
> +
> +static struct notifier_block xgene_msi_cpu_notifier = {
> +       .notifier_call = xgene_msi_cpu_callback,
> +};
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi"},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int cpu;
> +       int virt_msir;
> +
> +       xgene_msi = &xgene_msi_ctrl;
> +       xgene_msi->node = pdev->dev.of_node;

The only purpose the xgene_msi->node seems to be an intermediate
storage for set mchip.of_node. Why don't you set the latter directly,
and drop the former?

> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +       xgene_msi->msi_addr = res->start;
> +
> +       xgene_msi->num_cpus = num_possible_cpus();
> +
> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               virt_msir = platform_get_irq(pdev, irq_index);
> +               if (virt_msir < 0) {
> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                               irq_index);
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +               xgene_msi->msi_virqs[irq_index] = virt_msir;
> +       }
> +
> +       cpu_notifier_register_begin();
> +
> +       for_each_online_cpu(cpu)
> +               xgene_msi_hwirq_alloc(cpu);

Why does this need to be in the cpu_notifier critical section? What
will you do when xgene_msi_hwirq_alloc() fails?

> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
> +               cpu_notifier_register_done();
> +               goto error;
> +       }
> +
> +       cpu_notifier_register_done();
> +
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {

Assuming we fail here...

> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +error:
> +       xgene_msi_remove(pdev);

... we end-up here. But xgene_msi_remove doesn't unregister the
notifier...

> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

In somehow related news, this is my last review on this code for
quite some time. You've posted it 4 times in less than two weeks, right
in the middle of the merge window, I've given it a lot of attention,
and you've just run out of credits on the reviewing arcade game.

I suggest you spend some quality time with it, polish it as much as you
can. and post it again in about three weeks. Don't just make it work.
Make it beautiful. Make it something I become madly in love with. By
that time, I'll have forgotten about it and maybe I'll be swept off my
feet when I come back from holiday.

Thanks,

	M.
-- 
Without deviation from the norm, progress is not possible.

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

* Re: [PATCH v6 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-22 12:50                                                 ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-22 12:50 UTC (permalink / raw)
  To: Duc Dang
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Wed, 22 Apr 2015 07:15:09 +0100
Duc Dang <dhdang@apm.com> wrote:

> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports.
> 
> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
> driver supports is reduced to 256.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |   8 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 504 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 534 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..9f1e2b5 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,19 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +       help
> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..8bbf925
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,504 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       int                             *msi_virqs;
> +       int                             num_cpus;
> +};
> +
> +/* Global data */
> +static struct xgene_msi xgene_msi_ctrl;
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> +       u32 group = data->hwirq % NR_HW_IRQS;
> +
> +       msg->address_hi = upper_32_bits(msi->msi_addr);
> +       msg->address_lo = lower_32_bits(msi->msi_addr) +
> +                         (((8 * group) + reg_set) << 16);
> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +
> +       if (!desc)
> +               return -EINVAL;

I still don't understand under which circumstances this could ever be
NULL. You got here because someone has created this interrupt, and
inserted it in the domain. Someone also found a way to reference it,
and call this code. It must have been initialized the first place.

Also, nothing uses this variable in what follows, so please drop it
unless you can prove that desc can be NULL is that it is unsafe to
proceed further.

> +
> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       /* Update MSI number to target the new CPU */
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> +
> +       return IRQ_SET_MASK_OK;
> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +       struct xgene_msi *msi = domain->host_data;
> +       int msi_irq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
> +                                            msi->num_cpus, 0);
> +       if (msi_irq < NR_MSI_VEC)
> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq,
> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> +                           handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +       u32 hwirq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> +                                           &msi_domain_ops, msi);
> +       if (!msi->domain)
> +               return -ENOMEM;
> +
> +       msi->mchip.of_node = msi->node;
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
> +       if (!xgene_msi->msi_virqs)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> +{
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       struct xgene_msi *xgene_msi;
> +       unsigned int virq;
> +       int msir_index, msir_reg, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp;
> +       int i;
> +
> +       chained_irq_enter(chip, desc);
> +
> +       xgene_msi = irq_desc_get_handler_data(desc);
> +
> +       msi_grp = NR_HW_IRQS;
> +       for (i = 0; i < NR_HW_IRQS; i++)
> +               if (irq == xgene_msi->msi_virqs[i]) {
> +                       msi_grp = i;
> +                       break;
> +               }

Oh, please!!! In v3, you were giving silly reasons to shave one cycle
off a slow path, but you now seem pretty happy to run this loop on the
bloody hot path!

I already suggested that you actually save the group in handler_data,
and reference the global xgene_msi, since it is guaranteed to be
unique. If you really want to pointlessly follow pointers, you could
replace your msi_virqs array with an array of:

struct xgene_msi_group {
	struct xgene_msi	*xgene_msi;
	int			gic_irq;
	u32			msi_grp;
};

and make the relevant structure pointer to by desc->handler_data. That
will give you the information you need once and for all, without any
silly, cycle wasting loop.

> +       if (msi_grp >= NR_HW_IRQS) {
> +               chained_irq_exit(chip, desc);
> +               return;
> +       }

And I really can't see how this can ever be valid.

> +
> +       /*
> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> +        * If bit x of this register is set (x is 0..7), one or more interupts
> +        * corresponding to MSInIRx is set.
> +        */
> +       grp_select = readl_relaxed(xgene_msi->msi_regs +
> +                                  MSI_INT0 + (msi_grp << 16));
> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               /*
> +                * Calculate MSInIRx address to read to check for interrupts
> +                * (refer to termination address and data assignment
> +                * described in xgene_compose_msi_msg function)
> +                */
> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> +               msir_val = readl_relaxed(xgene_msi->msi_regs +
> +                                        MSI_IR0 + msir_reg);
> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       /*
> +                        * Calculate MSI vector number (refer to the termination
> +                        * address and data assignment described in
> +                        * xgene_compose_msi_msg function)
> +                        */
> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> +                                NR_HW_IRQS) + msi_grp;
> +                       /*
> +                        * As we have multiple hw_irq that maps to single MSI,
> +                        * always look up the virq using the hw_irq as seen from
> +                        * CPU0
> +                        */
> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> +                       if (virq != 0)
> +                               generic_handle_irq(virq);

The (virq == 0) case certainly deserves a warning, because it indicates
something went horribly wrong somewhere.

> +                       msir_val &= ~(1 << intr_index);
> +               }
> +               grp_select &= ~(1 << msir_index);
> +       }
> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               virq = msi->msi_virqs[i];
> +               if (virq != 0)
> +                       free_irq(virq, msi);
> +       }
> +       kfree(msi->msi_virqs);
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_hwirq_alloc(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       cpumask_var_t mask;
> +       int i;
> +       int err;
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               if (i % msi->num_cpus != cpu)
> +                       continue;
> +               if (!msi->msi_virqs[i])
> +                       continue;
> +
> +               irq_set_chained_handler(msi->msi_virqs[i], xgene_msi_isr);
> +               err = irq_set_handler_data(msi->msi_virqs[i], msi);

See? msi is *always* set to &xgene_msi_ctrl. So just encode the group
or use a structure similar to the one I've outlined above.

> +               if (err)
> +                       continue;

So we got a critical error that just makes the driver unusable, and yet
we silently continue? Sounds like a plan...

> +
> +               /*
> +                * Statically allocate MSI GIC IRQs to each CPU core
> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> +                * to each core.
> +                */
> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +                       cpumask_clear(mask);
> +                       cpumask_set_cpu(cpu, mask);
> +                       err = irq_set_affinity(msi->msi_virqs[i], mask);
> +                       if (err) {
> +                               free_irq(msi->msi_virqs[i], msi);
> +                               free_cpumask_var(mask);
> +                               continue;

Same here...

> +                       }
> +                       free_cpumask_var(mask);
> +               }
> +       }
> +}
> +
> +static void xgene_msi_hwirq_free(unsigned int cpu)
> +{
> +       /*
> +        * Kernel takes care of moving MSI interrupts that
> +        * are steered to this CPU to another online CPU
> +        * and freeing the interrupt handlers of GIC IRQs
> +        * allocated for this CPU, so simply return here.
> +        */

Two things:
- I couldn't find the code that backs this statement. MSIs (actually
  all interrupts) will indeed be moved, but I can't see how the kernel
  is going to free GIC interrupt. Care to point me to it?
- Assuming this statement is true, why do we need this function at all?


> +       return;
> +}
> +
> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
> +                                 unsigned long action, void *hcpu)
> +{
> +       unsigned cpu = (unsigned long)hcpu;
> +
> +       switch (action) {
> +       case CPU_ONLINE:
> +       case CPU_ONLINE_FROZEN:
> +               xgene_msi_hwirq_alloc(cpu);
> +               break;
> +       case CPU_DEAD:
> +       case CPU_DEAD_FROZEN:
> +               xgene_msi_hwirq_free(cpu);
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       return NOTIFY_OK;
> +}
> +
> +static struct notifier_block xgene_msi_cpu_notifier = {
> +       .notifier_call = xgene_msi_cpu_callback,
> +};
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi"},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int cpu;
> +       int virt_msir;
> +
> +       xgene_msi = &xgene_msi_ctrl;
> +       xgene_msi->node = pdev->dev.of_node;

The only purpose the xgene_msi->node seems to be an intermediate
storage for set mchip.of_node. Why don't you set the latter directly,
and drop the former?

> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +       xgene_msi->msi_addr = res->start;
> +
> +       xgene_msi->num_cpus = num_possible_cpus();
> +
> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               virt_msir = platform_get_irq(pdev, irq_index);
> +               if (virt_msir < 0) {
> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                               irq_index);
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +               xgene_msi->msi_virqs[irq_index] = virt_msir;
> +       }
> +
> +       cpu_notifier_register_begin();
> +
> +       for_each_online_cpu(cpu)
> +               xgene_msi_hwirq_alloc(cpu);

Why does this need to be in the cpu_notifier critical section? What
will you do when xgene_msi_hwirq_alloc() fails?

> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
> +               cpu_notifier_register_done();
> +               goto error;
> +       }
> +
> +       cpu_notifier_register_done();
> +
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {

Assuming we fail here...

> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +error:
> +       xgene_msi_remove(pdev);

... we end-up here. But xgene_msi_remove doesn't unregister the
notifier...

> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

In somehow related news, this is my last review on this code for
quite some time. You've posted it 4 times in less than two weeks, right
in the middle of the merge window, I've given it a lot of attention,
and you've just run out of credits on the reviewing arcade game.

I suggest you spend some quality time with it, polish it as much as you
can. and post it again in about three weeks. Don't just make it work.
Make it beautiful. Make it something I become madly in love with. By
that time, I'll have forgotten about it and maybe I'll be swept off my
feet when I come back from holiday.

Thanks,

	M.
-- 
Without deviation from the norm, progress is not possible.

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

* [PATCH v6 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-04-22 12:50                                                 ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-04-22 12:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 22 Apr 2015 07:15:09 +0100
Duc Dang <dhdang@apm.com> wrote:

> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports.
> 
> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
> driver supports is reduced to 256.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |   8 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 504 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 534 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..9f1e2b5 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,19 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +       help
> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..8bbf925
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,504 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       int                             *msi_virqs;
> +       int                             num_cpus;
> +};
> +
> +/* Global data */
> +static struct xgene_msi xgene_msi_ctrl;
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> +       u32 group = data->hwirq % NR_HW_IRQS;
> +
> +       msg->address_hi = upper_32_bits(msi->msi_addr);
> +       msg->address_lo = lower_32_bits(msi->msi_addr) +
> +                         (((8 * group) + reg_set) << 16);
> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +
> +       if (!desc)
> +               return -EINVAL;

I still don't understand under which circumstances this could ever be
NULL. You got here because someone has created this interrupt, and
inserted it in the domain. Someone also found a way to reference it,
and call this code. It must have been initialized the first place.

Also, nothing uses this variable in what follows, so please drop it
unless you can prove that desc can be NULL is that it is unsafe to
proceed further.

> +
> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       /* Update MSI number to target the new CPU */
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> +
> +       return IRQ_SET_MASK_OK;
> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +       struct xgene_msi *msi = domain->host_data;
> +       int msi_irq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
> +                                            msi->num_cpus, 0);
> +       if (msi_irq < NR_MSI_VEC)
> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq,
> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> +                           handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +       u32 hwirq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> +                                           &msi_domain_ops, msi);
> +       if (!msi->domain)
> +               return -ENOMEM;
> +
> +       msi->mchip.of_node = msi->node;
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
> +       if (!xgene_msi->msi_virqs)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> +{
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       struct xgene_msi *xgene_msi;
> +       unsigned int virq;
> +       int msir_index, msir_reg, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp;
> +       int i;
> +
> +       chained_irq_enter(chip, desc);
> +
> +       xgene_msi = irq_desc_get_handler_data(desc);
> +
> +       msi_grp = NR_HW_IRQS;
> +       for (i = 0; i < NR_HW_IRQS; i++)
> +               if (irq == xgene_msi->msi_virqs[i]) {
> +                       msi_grp = i;
> +                       break;
> +               }

Oh, please!!! In v3, you were giving silly reasons to shave one cycle
off a slow path, but you now seem pretty happy to run this loop on the
bloody hot path!

I already suggested that you actually save the group in handler_data,
and reference the global xgene_msi, since it is guaranteed to be
unique. If you really want to pointlessly follow pointers, you could
replace your msi_virqs array with an array of:

struct xgene_msi_group {
	struct xgene_msi	*xgene_msi;
	int			gic_irq;
	u32			msi_grp;
};

and make the relevant structure pointer to by desc->handler_data. That
will give you the information you need once and for all, without any
silly, cycle wasting loop.

> +       if (msi_grp >= NR_HW_IRQS) {
> +               chained_irq_exit(chip, desc);
> +               return;
> +       }

And I really can't see how this can ever be valid.

> +
> +       /*
> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> +        * If bit x of this register is set (x is 0..7), one or more interupts
> +        * corresponding to MSInIRx is set.
> +        */
> +       grp_select = readl_relaxed(xgene_msi->msi_regs +
> +                                  MSI_INT0 + (msi_grp << 16));
> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               /*
> +                * Calculate MSInIRx address to read to check for interrupts
> +                * (refer to termination address and data assignment
> +                * described in xgene_compose_msi_msg function)
> +                */
> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> +               msir_val = readl_relaxed(xgene_msi->msi_regs +
> +                                        MSI_IR0 + msir_reg);
> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       /*
> +                        * Calculate MSI vector number (refer to the termination
> +                        * address and data assignment described in
> +                        * xgene_compose_msi_msg function)
> +                        */
> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> +                                NR_HW_IRQS) + msi_grp;
> +                       /*
> +                        * As we have multiple hw_irq that maps to single MSI,
> +                        * always look up the virq using the hw_irq as seen from
> +                        * CPU0
> +                        */
> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> +                       if (virq != 0)
> +                               generic_handle_irq(virq);

The (virq == 0) case certainly deserves a warning, because it indicates
something went horribly wrong somewhere.

> +                       msir_val &= ~(1 << intr_index);
> +               }
> +               grp_select &= ~(1 << msir_index);
> +       }
> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               virq = msi->msi_virqs[i];
> +               if (virq != 0)
> +                       free_irq(virq, msi);
> +       }
> +       kfree(msi->msi_virqs);
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_hwirq_alloc(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       cpumask_var_t mask;
> +       int i;
> +       int err;
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               if (i % msi->num_cpus != cpu)
> +                       continue;
> +               if (!msi->msi_virqs[i])
> +                       continue;
> +
> +               irq_set_chained_handler(msi->msi_virqs[i], xgene_msi_isr);
> +               err = irq_set_handler_data(msi->msi_virqs[i], msi);

See? msi is *always* set to &xgene_msi_ctrl. So just encode the group
or use a structure similar to the one I've outlined above.

> +               if (err)
> +                       continue;

So we got a critical error that just makes the driver unusable, and yet
we silently continue? Sounds like a plan...

> +
> +               /*
> +                * Statically allocate MSI GIC IRQs to each CPU core
> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> +                * to each core.
> +                */
> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +                       cpumask_clear(mask);
> +                       cpumask_set_cpu(cpu, mask);
> +                       err = irq_set_affinity(msi->msi_virqs[i], mask);
> +                       if (err) {
> +                               free_irq(msi->msi_virqs[i], msi);
> +                               free_cpumask_var(mask);
> +                               continue;

Same here...

> +                       }
> +                       free_cpumask_var(mask);
> +               }
> +       }
> +}
> +
> +static void xgene_msi_hwirq_free(unsigned int cpu)
> +{
> +       /*
> +        * Kernel takes care of moving MSI interrupts that
> +        * are steered to this CPU to another online CPU
> +        * and freeing the interrupt handlers of GIC IRQs
> +        * allocated for this CPU, so simply return here.
> +        */

Two things:
- I couldn't find the code that backs this statement. MSIs (actually
  all interrupts) will indeed be moved, but I can't see how the kernel
  is going to free GIC interrupt. Care to point me to it?
- Assuming this statement is true, why do we need this function at all?


> +       return;
> +}
> +
> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
> +                                 unsigned long action, void *hcpu)
> +{
> +       unsigned cpu = (unsigned long)hcpu;
> +
> +       switch (action) {
> +       case CPU_ONLINE:
> +       case CPU_ONLINE_FROZEN:
> +               xgene_msi_hwirq_alloc(cpu);
> +               break;
> +       case CPU_DEAD:
> +       case CPU_DEAD_FROZEN:
> +               xgene_msi_hwirq_free(cpu);
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       return NOTIFY_OK;
> +}
> +
> +static struct notifier_block xgene_msi_cpu_notifier = {
> +       .notifier_call = xgene_msi_cpu_callback,
> +};
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi"},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int cpu;
> +       int virt_msir;
> +
> +       xgene_msi = &xgene_msi_ctrl;
> +       xgene_msi->node = pdev->dev.of_node;

The only purpose the xgene_msi->node seems to be an intermediate
storage for set mchip.of_node. Why don't you set the latter directly,
and drop the former?

> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +       xgene_msi->msi_addr = res->start;
> +
> +       xgene_msi->num_cpus = num_possible_cpus();
> +
> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               virt_msir = platform_get_irq(pdev, irq_index);
> +               if (virt_msir < 0) {
> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                               irq_index);
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +               xgene_msi->msi_virqs[irq_index] = virt_msir;
> +       }
> +
> +       cpu_notifier_register_begin();
> +
> +       for_each_online_cpu(cpu)
> +               xgene_msi_hwirq_alloc(cpu);

Why does this need to be in the cpu_notifier critical section? What
will you do when xgene_msi_hwirq_alloc() fails?

> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
> +               cpu_notifier_register_done();
> +               goto error;
> +       }
> +
> +       cpu_notifier_register_done();
> +
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {

Assuming we fail here...

> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +error:
> +       xgene_msi_remove(pdev);

... we end-up here. But xgene_msi_remove doesn't unregister the
notifier...

> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

In somehow related news, this is my last review on this code for
quite some time. You've posted it 4 times in less than two weeks, right
in the middle of the merge window, I've given it a lot of attention,
and you've just run out of credits on the reviewing arcade game.

I suggest you spend some quality time with it, polish it as much as you
can. and post it again in about three weeks. Don't just make it work.
Make it beautiful. Make it something I become madly in love with. By
that time, I'll have forgotten about it and maybe I'll be swept off my
feet when I come back from holiday.

Thanks,

	M.
-- 
Without deviation from the norm, progress is not possible.

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

* [PATCH v7 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-04-22 12:50                                                 ` Marc Zyngier
@ 2015-05-18  9:55                                                   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-18  9:55 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI
vectors this driver supports is reduced to 256 to maintain the correct set_affinity
behavior for each MSI.

v7 changes:
	1. Add more error handling cases
	2. Clear spurious interrupts that may happen during driver probe
	3. Not using free_irq for chained irqs
	4. Improve GIC IRQ number look up in chained handler

v6 changes:
        1. Correctly allocate MSI with bitmap_find_next_zero_area
        2. Add notifier for CPU hotplug case
        3. Driver clean up
        4. Add more detailed information into device tree binding document
           and move the device tree binding patch before the driver/dts patch

v5 changes:
        1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs
        for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer
        them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1
        supports is reduced to 256.

v4 changes:
        1. Remove affinity setting for each MSI
        2. Add description about register layout, MSI termination address and data
        3. Correct total number of MSI vectors to 2048
        4. Clean up error messages
        5. Remove unused module code

v3 changes:
        1. Implement MSI support using PCI MSI IRQ domain
        2. Only use msi_controller to store IRQ domain
v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
 

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
 drivers/pci/host/Kconfig                           |  10 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 550 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 685 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1


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

* [PATCH v7 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-05-18  9:55                                                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-18  9:55 UTC (permalink / raw)
  To: linux-arm-kernel

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI
vectors this driver supports is reduced to 256 to maintain the correct set_affinity
behavior for each MSI.

v7 changes:
	1. Add more error handling cases
	2. Clear spurious interrupts that may happen during driver probe
	3. Not using free_irq for chained irqs
	4. Improve GIC IRQ number look up in chained handler

v6 changes:
        1. Correctly allocate MSI with bitmap_find_next_zero_area
        2. Add notifier for CPU hotplug case
        3. Driver clean up
        4. Add more detailed information into device tree binding document
           and move the device tree binding patch before the driver/dts patch

v5 changes:
        1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs
        for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer
        them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1
        supports is reduced to 256.

v4 changes:
        1. Remove affinity setting for each MSI
        2. Add description about register layout, MSI termination address and data
        3. Correct total number of MSI vectors to 2048
        4. Clean up error messages
        5. Remove unused module code

v3 changes:
        1. Implement MSI support using PCI MSI IRQ domain
        2. Only use msi_controller to store IRQ domain
v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
 

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
 drivers/pci/host/Kconfig                           |  10 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 550 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 685 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1

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

* [PATCH v7 1/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
  2015-05-18  9:55                                                   ` Duc Dang
@ 2015-05-18  9:55                                                   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-18  9:55 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

X-Gene v1 PCIe MSI controller block provides PCIE MSI functionality for 5 X-Gene v1
PCIE ports

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..36d881c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+	      X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+	      interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi@79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = 	<0x0 0x10 0x4>
+				<0x0 0x11 0x4>
+				<0x0 0x12 0x4>
+				<0x0 0x13 0x4>
+				<0x0 0x14 0x4>
+				<0x0 0x15 0x4>
+				<0x0 0x16 0x4>
+				<0x0 0x17 0x4>
+				<0x0 0x18 0x4>
+				<0x0 0x19 0x4>
+				<0x0 0x1a 0x4>
+				<0x0 0x1b 0x4>
+				<0x0 0x1c 0x4>
+				<0x0 0x1d 0x4>
+				<0x0 0x1e 0x4>
+				<0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie@1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1


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

* [PATCH v7 1/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
@ 2015-05-18  9:55                                                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-18  9:55 UTC (permalink / raw)
  To: linux-arm-kernel

X-Gene v1 PCIe MSI controller block provides PCIE MSI functionality for 5 X-Gene v1
PCIE ports

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..36d881c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+	      X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+	      interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = 	<0x0 0x10 0x4>
+				<0x0 0x11 0x4>
+				<0x0 0x12 0x4>
+				<0x0 0x13 0x4>
+				<0x0 0x14 0x4>
+				<0x0 0x15 0x4>
+				<0x0 0x16 0x4>
+				<0x0 0x17 0x4>
+				<0x0 0x18 0x4>
+				<0x0 0x19 0x4>
+				<0x0 0x1a 0x4>
+				<0x0 0x1b 0x4>
+				<0x0 0x1c 0x4>
+				<0x0 0x1d 0x4>
+				<0x0 0x1e 0x4>
+				<0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1

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

* [PATCH v7 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-05-18  9:55                                                   ` Duc Dang
@ 2015-05-18  9:55                                                   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-18  9:55 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
is moved around these HW IRQs lines. With this approach, the total MSI vectors this
driver supports is reduced to 256.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 drivers/pci/host/Kconfig         |  10 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 550 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 582 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9..0932118 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,21 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+	help
+	  Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
+	  This MSI driver will provide MSI support for 5 PCIe ports of
+	  APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c..f39bde3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..648bc8f
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,550 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0			0x000000
+#define MSI_INT0		0x800000
+#define IDX_PER_GROUP		8
+#define IRQS_PER_IDX		16
+#define NR_HW_IRQS		16
+#define NR_MSI_VEC		(IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi_group {
+	struct xgene_msi		*msi;
+	int				gic_irq;
+	u32				msi_grp;
+};
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	u64				msi_addr;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	struct xgene_msi_group		*msi_groups;
+	int				num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0			base_addr
+ * MSI0IR1			base_addr +  0x10000
+ * ...				...
+ * MSI0IR6			base_addr +  0x60000
+ * MSI0IR7			base_addr +  0x70000
+ * MSI1IR0			base_addr +  0x80000
+ * MSI1IR1			base_addr +  0x90000
+ * ...				...
+ * MSI1IR7			base_addr +  0xF0000
+ * MSI2IR0			base_addr + 0x100000
+ * ...				...
+ * MSIFIR0			base_addr + 0x780000
+ * MSIFIR1			base_addr + 0x790000
+ * ...				...
+ * MSIFIR7			base_addr + 0x7F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *		Group 0: 0-255
+ *		Group 1: 256-511
+ *		Group 2: 512-767
+ *		...
+ *		Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *	As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *		Group 0: 0-15
+ *		Group 1: 16-32
+ *		...
+ *		Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+	u32 group = data->hwirq % NR_HW_IRQS;
+
+	msg->address_hi = upper_32_bits(msi->msi_addr);
+	msg->address_lo = lower_32_bits(msi->msi_addr) +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+				  const struct cpumask *mask, bool force)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+	int target_cpu = cpumask_first(mask);
+	int curr_cpu;
+
+	curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
+	if (curr_cpu == target_cpu)
+		return IRQ_SET_MASK_OK_DONE;
+
+	/* Update MSI number to target the new CPU */
+	irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+
+	return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+	struct xgene_msi *msi = domain->host_data;
+	int msi_irq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+					     msi->num_cpus, 0);
+	if (msi_irq < NR_MSI_VEC)
+		bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+	else
+		msi_irq = -ENOSPC;
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq,
+			    &xgene_msi_bottom_irq_chip, domain->host_data,
+			    handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+	u32 hwirq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
+	bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+					    &msi_domain_ops, msi);
+	if (!msi->domain)
+		return -ENOMEM;
+
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
+					sizeof(struct xgene_msi_group),
+					GFP_KERNEL);
+	if (!xgene_msi->msi_groups)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct xgene_msi_group *msi_groups;
+	struct xgene_msi *xgene_msi;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp;
+
+	chained_irq_enter(chip, desc);
+
+	msi_groups = irq_desc_get_handler_data(desc);
+	xgene_msi = msi_groups->msi;
+	msi_grp = msi_groups->msi_grp;
+
+	/*
+	 * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+	 * If bit x of this register is set (x is 0..7), one or more interupts
+	 * corresponding to MSInIRx is set.
+	 */
+	grp_select = readl_relaxed(xgene_msi->msi_regs +
+				   MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		/*
+		 * Calculate MSInIRx address to read to check for interrupts
+		 * (refer to termination address and data assignment
+		 * described in xgene_compose_msi_msg function)
+		 */
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl_relaxed(xgene_msi->msi_regs +
+					 MSI_IR0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			/*
+			 * Calculate MSI vector number (refer to the termination
+			 * address and data assignment described in
+			 * xgene_compose_msi_msg function)
+			 */
+			hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+				 NR_HW_IRQS) + msi_grp;
+			/*
+			 * As we have multiple hw_irq that maps to single MSI,
+			 * always look up the virq using the hw_irq as seen from
+			 * CPU0
+			 */
+			hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			WARN_ON(!virq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		virq = msi->msi_groups[i].gic_irq;
+		if (virq != 0) {
+			irq_set_chained_handler(virq, NULL);
+			irq_set_handler_data(virq, NULL);
+		}
+	}
+	kfree(msi->msi_groups);
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static int xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	cpumask_var_t mask;
+	int i;
+	int err;
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		if (i % msi->num_cpus != cpu)
+			continue;
+
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq,
+					xgene_msi_isr);
+		err = irq_set_handler_data(msi_group->gic_irq, msi_group);
+		if (err) {
+			pr_err("failed to register GIC IRQ handler\n");
+			return -EINVAL;
+		}
+		/*
+		 * Statically allocate MSI GIC IRQs to each CPU core
+		 * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+		 * to each core.
+		 */
+		if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+			cpumask_clear(mask);
+			cpumask_set_cpu(cpu, mask);
+			err = irq_set_affinity(msi_group->gic_irq, mask);
+			if (err) {
+				irq_set_chained_handler(msi_group->gic_irq,
+							NULL);
+				irq_set_handler_data(msi_group->gic_irq, NULL);
+				free_cpumask_var(mask);
+				pr_err("failed to set affinity for GIC IRQ");
+				return -EINVAL;
+			}
+			free_cpumask_var(mask);
+		}
+	}
+
+	return 0;
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	int i;
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		if (i % msi->num_cpus != cpu)
+			continue;
+
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq, NULL);
+		irq_set_handler_data(msi_group->gic_irq, NULL);
+	}
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+				  unsigned long action, void *hcpu)
+{
+	unsigned cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		xgene_msi_hwirq_alloc(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		xgene_msi_hwirq_free(cpu);
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+	.notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi"},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct xgene_msi *xgene_msi;
+	unsigned int cpu;
+	int virt_msir;
+	u32 msi_reg, msi_val, msi_idx;
+
+	xgene_msi = &xgene_msi_ctrl;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	xgene_msi->msi_addr = res->start;
+
+	xgene_msi->num_cpus = num_possible_cpus();
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		virt_msir = platform_get_irq(pdev, irq_index);
+		if (virt_msir < 0) {
+			dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+				irq_index);
+			rc = -EINVAL;
+			goto error;
+		}
+		xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
+		xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
+		xgene_msi->msi_groups[irq_index].msi = xgene_msi;
+	}
+
+	/*
+	 * MSInIRx registers are read-to-clear, before registering interrupt
+	 * handlers, reading all of them to clear all spurious interrupts
+	 * that may occur before the driver is probed.
+	 */
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++) {
+			msi_reg = (irq_index << 19) + (msi_idx << 16);
+			msi_val = readl(xgene_msi->msi_regs +
+					 MSI_IR0 + msi_reg);
+		}
+		/* Read MSIINTn to confirm */
+		msi_val = readl(xgene_msi->msi_regs +
+				MSI_INT0 + (irq_index << 16));
+		if (msi_val) {
+			dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
+			rc = -EINVAL;
+			goto error;
+		}
+	}
+
+	cpu_notifier_register_begin();
+
+	for_each_online_cpu(cpu)
+		if (xgene_msi_hwirq_alloc(cpu)) {
+			dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
+			cpu_notifier_register_done();
+			goto error;
+		}
+
+	rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+		cpu_notifier_register_done();
+		goto error;
+	}
+
+	cpu_notifier_register_done();
+
+	xgene_msi->mchip.of_node = pdev->dev.of_node;
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error_notifier;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+
+error_notifier:
+	unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 22751ed..3e6faa1 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1


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

* [PATCH v7 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-18  9:55                                                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-18  9:55 UTC (permalink / raw)
  To: linux-arm-kernel

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
is moved around these HW IRQs lines. With this approach, the total MSI vectors this
driver supports is reduced to 256.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 drivers/pci/host/Kconfig         |  10 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 550 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 582 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9..0932118 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,21 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+	help
+	  Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
+	  This MSI driver will provide MSI support for 5 PCIe ports of
+	  APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c..f39bde3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..648bc8f
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,550 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0			0x000000
+#define MSI_INT0		0x800000
+#define IDX_PER_GROUP		8
+#define IRQS_PER_IDX		16
+#define NR_HW_IRQS		16
+#define NR_MSI_VEC		(IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi_group {
+	struct xgene_msi		*msi;
+	int				gic_irq;
+	u32				msi_grp;
+};
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	u64				msi_addr;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	struct xgene_msi_group		*msi_groups;
+	int				num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0			base_addr
+ * MSI0IR1			base_addr +  0x10000
+ * ...				...
+ * MSI0IR6			base_addr +  0x60000
+ * MSI0IR7			base_addr +  0x70000
+ * MSI1IR0			base_addr +  0x80000
+ * MSI1IR1			base_addr +  0x90000
+ * ...				...
+ * MSI1IR7			base_addr +  0xF0000
+ * MSI2IR0			base_addr + 0x100000
+ * ...				...
+ * MSIFIR0			base_addr + 0x780000
+ * MSIFIR1			base_addr + 0x790000
+ * ...				...
+ * MSIFIR7			base_addr + 0x7F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *		Group 0: 0-255
+ *		Group 1: 256-511
+ *		Group 2: 512-767
+ *		...
+ *		Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *	As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *		Group 0: 0-15
+ *		Group 1: 16-32
+ *		...
+ *		Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+	u32 group = data->hwirq % NR_HW_IRQS;
+
+	msg->address_hi = upper_32_bits(msi->msi_addr);
+	msg->address_lo = lower_32_bits(msi->msi_addr) +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+				  const struct cpumask *mask, bool force)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+	int target_cpu = cpumask_first(mask);
+	int curr_cpu;
+
+	curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
+	if (curr_cpu == target_cpu)
+		return IRQ_SET_MASK_OK_DONE;
+
+	/* Update MSI number to target the new CPU */
+	irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+
+	return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+	struct xgene_msi *msi = domain->host_data;
+	int msi_irq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+					     msi->num_cpus, 0);
+	if (msi_irq < NR_MSI_VEC)
+		bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+	else
+		msi_irq = -ENOSPC;
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq,
+			    &xgene_msi_bottom_irq_chip, domain->host_data,
+			    handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+	u32 hwirq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
+	bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+					    &msi_domain_ops, msi);
+	if (!msi->domain)
+		return -ENOMEM;
+
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
+					sizeof(struct xgene_msi_group),
+					GFP_KERNEL);
+	if (!xgene_msi->msi_groups)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct xgene_msi_group *msi_groups;
+	struct xgene_msi *xgene_msi;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp;
+
+	chained_irq_enter(chip, desc);
+
+	msi_groups = irq_desc_get_handler_data(desc);
+	xgene_msi = msi_groups->msi;
+	msi_grp = msi_groups->msi_grp;
+
+	/*
+	 * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+	 * If bit x of this register is set (x is 0..7), one or more interupts
+	 * corresponding to MSInIRx is set.
+	 */
+	grp_select = readl_relaxed(xgene_msi->msi_regs +
+				   MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		/*
+		 * Calculate MSInIRx address to read to check for interrupts
+		 * (refer to termination address and data assignment
+		 * described in xgene_compose_msi_msg function)
+		 */
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl_relaxed(xgene_msi->msi_regs +
+					 MSI_IR0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			/*
+			 * Calculate MSI vector number (refer to the termination
+			 * address and data assignment described in
+			 * xgene_compose_msi_msg function)
+			 */
+			hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+				 NR_HW_IRQS) + msi_grp;
+			/*
+			 * As we have multiple hw_irq that maps to single MSI,
+			 * always look up the virq using the hw_irq as seen from
+			 * CPU0
+			 */
+			hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			WARN_ON(!virq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		virq = msi->msi_groups[i].gic_irq;
+		if (virq != 0) {
+			irq_set_chained_handler(virq, NULL);
+			irq_set_handler_data(virq, NULL);
+		}
+	}
+	kfree(msi->msi_groups);
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static int xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	cpumask_var_t mask;
+	int i;
+	int err;
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		if (i % msi->num_cpus != cpu)
+			continue;
+
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq,
+					xgene_msi_isr);
+		err = irq_set_handler_data(msi_group->gic_irq, msi_group);
+		if (err) {
+			pr_err("failed to register GIC IRQ handler\n");
+			return -EINVAL;
+		}
+		/*
+		 * Statically allocate MSI GIC IRQs to each CPU core
+		 * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+		 * to each core.
+		 */
+		if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+			cpumask_clear(mask);
+			cpumask_set_cpu(cpu, mask);
+			err = irq_set_affinity(msi_group->gic_irq, mask);
+			if (err) {
+				irq_set_chained_handler(msi_group->gic_irq,
+							NULL);
+				irq_set_handler_data(msi_group->gic_irq, NULL);
+				free_cpumask_var(mask);
+				pr_err("failed to set affinity for GIC IRQ");
+				return -EINVAL;
+			}
+			free_cpumask_var(mask);
+		}
+	}
+
+	return 0;
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	int i;
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		if (i % msi->num_cpus != cpu)
+			continue;
+
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq, NULL);
+		irq_set_handler_data(msi_group->gic_irq, NULL);
+	}
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+				  unsigned long action, void *hcpu)
+{
+	unsigned cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		xgene_msi_hwirq_alloc(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		xgene_msi_hwirq_free(cpu);
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+	.notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi"},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct xgene_msi *xgene_msi;
+	unsigned int cpu;
+	int virt_msir;
+	u32 msi_reg, msi_val, msi_idx;
+
+	xgene_msi = &xgene_msi_ctrl;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	xgene_msi->msi_addr = res->start;
+
+	xgene_msi->num_cpus = num_possible_cpus();
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		virt_msir = platform_get_irq(pdev, irq_index);
+		if (virt_msir < 0) {
+			dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+				irq_index);
+			rc = -EINVAL;
+			goto error;
+		}
+		xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
+		xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
+		xgene_msi->msi_groups[irq_index].msi = xgene_msi;
+	}
+
+	/*
+	 * MSInIRx registers are read-to-clear, before registering interrupt
+	 * handlers, reading all of them to clear all spurious interrupts
+	 * that may occur before the driver is probed.
+	 */
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++) {
+			msi_reg = (irq_index << 19) + (msi_idx << 16);
+			msi_val = readl(xgene_msi->msi_regs +
+					 MSI_IR0 + msi_reg);
+		}
+		/* Read MSIINTn to confirm */
+		msi_val = readl(xgene_msi->msi_regs +
+				MSI_INT0 + (irq_index << 16));
+		if (msi_val) {
+			dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
+			rc = -EINVAL;
+			goto error;
+		}
+	}
+
+	cpu_notifier_register_begin();
+
+	for_each_online_cpu(cpu)
+		if (xgene_msi_hwirq_alloc(cpu)) {
+			dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
+			cpu_notifier_register_done();
+			goto error;
+		}
+
+	rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+		cpu_notifier_register_done();
+		goto error;
+	}
+
+	cpu_notifier_register_done();
+
+	xgene_msi->mchip.of_node = pdev->dev.of_node;
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error_notifier;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+
+error_notifier:
+	unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 22751ed..3e6faa1 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1

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

* [PATCH v7 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
  2015-05-18  9:55                                                   ` Duc Dang
@ 2015-05-18  9:55                                                   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-18  9:55 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..4b719c9 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi@79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie@1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie@1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie@1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie@1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie@1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial@1c020000 {
-- 
1.9.1


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

* [PATCH v7 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
@ 2015-05-18  9:55                                                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-18  9:55 UTC (permalink / raw)
  To: linux-arm-kernel

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..4b719c9 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
-- 
1.9.1

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

* [PATCH v7 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
  2015-05-18  9:55                                                   ` Duc Dang
@ 2015-05-18  9:55                                                   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-18  9:55 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddc5a8c..a1b119b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7490,6 +7490,14 @@ L:	linux-pci@vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci@vger.kernel.org
+L:	linux-arm-kernel@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia@lists.infradead.org
-- 
1.9.1


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

* [PATCH v7 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
@ 2015-05-18  9:55                                                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-18  9:55 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddc5a8c..a1b119b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7490,6 +7490,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
-- 
1.9.1

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

* Re: [PATCH v6 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-04-22 12:50                                                 ` Marc Zyngier
  (?)
@ 2015-05-18 10:12                                                   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-18 10:12 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Wed, Apr 22, 2015 at 5:50 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>
> On Wed, 22 Apr 2015 07:15:09 +0100
> Duc Dang <dhdang@apm.com> wrote:
>
> > APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> > to GIC V2M specification for MSI Termination.
> >
> > There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> > block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> > and shared across all 5 PCIe ports.
> >
> > As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
> > correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
> > v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
> > is moved around these HW IRQs lines. With this approach, the total MSI vectors this
> > driver supports is reduced to 256.
> >
> > Signed-off-by: Duc Dang <dhdang@apm.com>
> > Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> > ---
> >  drivers/pci/host/Kconfig         |   8 +
> >  drivers/pci/host/Makefile        |   1 +
> >  drivers/pci/host/pci-xgene-msi.c | 504 +++++++++++++++++++++++++++++++++++++++
> >  drivers/pci/host/pci-xgene.c     |  21 ++
> >  4 files changed, 534 insertions(+)
> >  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> >
> > diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> > index 7b892a9..9f1e2b5 100644
> > --- a/drivers/pci/host/Kconfig
> > +++ b/drivers/pci/host/Kconfig
> > @@ -89,11 +89,19 @@ config PCI_XGENE
> >         depends on ARCH_XGENE
> >         depends on OF
> >         select PCIEPORTBUS
> > +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> > +       select PCI_XGENE_MSI if PCI_MSI
> >         help
> >           Say Y here if you want internal PCI support on APM X-Gene SoC.
> >           There are 5 internal PCIe ports available. Each port is GEN3 capable
> >           and have varied lanes from x1 to x8.
> >
> > +config PCI_XGENE_MSI
> > +       bool "X-Gene v1 PCIe MSI feature"
> > +       depends on PCI_XGENE && PCI_MSI
> > +       help
> > +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC
> > +
> >  config PCI_LAYERSCAPE
> >         bool "Freescale Layerscape PCIe controller"
> >         depends on OF && ARM
> > diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> > index e61d91c..f39bde3 100644
> > --- a/drivers/pci/host/Makefile
> > +++ b/drivers/pci/host/Makefile
> > @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> > +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
> >  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
> >  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> > diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> > new file mode 100644
> > index 0000000..8bbf925
> > --- /dev/null
> > +++ b/drivers/pci/host/pci-xgene-msi.c
> > @@ -0,0 +1,504 @@
> > +/*
> > + * APM X-Gene MSI Driver
> > + *
> > + * Copyright (c) 2014, Applied Micro Circuits Corporation
> > + * Author: Tanmay Inamdar <tinamdar@apm.com>
> > + *        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 as published by the
> > + * Free Software Foundation;  either version 2 of the  License, or (at your
> > + * option) any later version.
> > + *
> > + * 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.
> > + */
> > +#include <linux/cpu.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/module.h>
> > +#include <linux/msi.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/pci.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/of_pci.h>
> > +
> > +#define MSI_IR0                        0x000000
> > +#define MSI_INT0               0x800000
> > +#define IDX_PER_GROUP          8
> > +#define IRQS_PER_IDX           16
> > +#define NR_HW_IRQS             16
> > +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> > +
> > +struct xgene_msi {
> > +       struct device_node              *node;
> > +       struct msi_controller           mchip;
> > +       struct irq_domain               *domain;
> > +       u64                             msi_addr;
> > +       void __iomem                    *msi_regs;
> > +       unsigned long                   *bitmap;
> > +       struct mutex                    bitmap_lock;
> > +       int                             *msi_virqs;
> > +       int                             num_cpus;
> > +};
> > +
> > +/* Global data */
> > +static struct xgene_msi xgene_msi_ctrl;
> > +
> > +static struct irq_chip xgene_msi_top_irq_chip = {
> > +       .name           = "X-Gene1 MSI",
> > +       .irq_enable     = pci_msi_unmask_irq,
> > +       .irq_disable    = pci_msi_mask_irq,
> > +       .irq_mask       = pci_msi_mask_irq,
> > +       .irq_unmask     = pci_msi_unmask_irq,
> > +};
> > +
> > +static struct  msi_domain_info xgene_msi_domain_info = {
> > +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> > +                 MSI_FLAG_PCI_MSIX),
> > +       .chip   = &xgene_msi_top_irq_chip,
> > +};
> > +
> > +/*
> > + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> > + * n is group number (0..F), x is index of registers in each group (0..7)
> > + * The registers layout is like following:
> > + * MSI0IR0                     base_addr
> > + * MSI0IR1                     base_addr +  0x10000
> > + * ...                         ...
> > + * MSI0IR6                     base_addr +  0x60000
> > + * MSI0IR7                     base_addr +  0x70000
> > + * MSI1IR0                     base_addr +  0x80000
> > + * MSI1IR1                     base_addr +  0x90000
> > + * ...                         ...
> > + * MSI1IR7                     base_addr +  0xF0000
> > + * MSI2IR0                     base_addr + 0x100000
> > + * ...                         ...
> > + * MSIFIR0                     base_addr + 0x780000
> > + * MSIFIR1                     base_addr + 0x790000
> > + * ...                         ...
> > + * MSIFIR7                     base_addr + 0x7F0000
> > + *
> > + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> > + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> > + * registers.
> > + *
> > + * With 2048 MSI vectors supported, the MSI message can be construct using
> > + * following scheme:
> > + * - Divide into 8 256-vector groups
> > + *             Group 0: 0-255
> > + *             Group 1: 256-511
> > + *             Group 2: 512-767
> > + *             ...
> > + *             Group 7: 1792-2047
> > + * - Each 256-vector group is divided into 16 16-vector groups
> > + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> > + *             Group 0: 0-15
> > + *             Group 1: 16-32
> > + *             ...
> > + *             Group 15: 240-255
> > + * - The termination address of MSI vector in 256-vector group n and 16-vector
> > + * group x is the address of MSIxIRn
> > + * - The data for MSI vector in 16-vector group x is x
> > + */
> > +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> > +{
> > +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> > +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> > +       u32 group = data->hwirq % NR_HW_IRQS;
> > +
> > +       msg->address_hi = upper_32_bits(msi->msi_addr);
> > +       msg->address_lo = lower_32_bits(msi->msi_addr) +
> > +                         (((8 * group) + reg_set) << 16);
> > +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> > +}
> > +
> > +/*
> > + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> > + * To maintain the expected behaviour of .set_affinity for each MSI
> > + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> > + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> > + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> > + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> > + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> > + */
> > +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> > +                                 const struct cpumask *mask, bool force)
> > +{
> > +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> > +       struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
> > +       int target_cpu = cpumask_first(mask);
> > +       int curr_cpu;
> > +
> > +       if (!desc)
> > +               return -EINVAL;
>
> I still don't understand under which circumstances this could ever be
> NULL. You got here because someone has created this interrupt, and
> inserted it in the domain. Someone also found a way to reference it,
> and call this code. It must have been initialized the first place.
>
> Also, nothing uses this variable in what follows, so please drop it
> unless you can prove that desc can be NULL is that it is unsafe to
> proceed further.
>

I drop this in v7 patch.

> > +
> > +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
> > +       if (curr_cpu == target_cpu)
> > +               return IRQ_SET_MASK_OK_DONE;
> > +
> > +       /* Update MSI number to target the new CPU */
> > +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> > +
> > +       return IRQ_SET_MASK_OK;
> > +}
> > +
> > +static struct irq_chip xgene_msi_bottom_irq_chip = {
> > +       .name                   = "MSI",
> > +       .irq_set_affinity       = xgene_msi_set_affinity,
> > +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> > +};
> > +
> > +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> > +                                 unsigned int nr_irqs, void *args)
> > +{
> > +       struct xgene_msi *msi = domain->host_data;
> > +       int msi_irq;
> > +
> > +       mutex_lock(&msi->bitmap_lock);
> > +
> > +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
> > +                                            msi->num_cpus, 0);
> > +       if (msi_irq < NR_MSI_VEC)
> > +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
> > +       else
> > +               msi_irq = -ENOSPC;
> > +
> > +       mutex_unlock(&msi->bitmap_lock);
> > +
> > +       if (msi_irq < 0)
> > +               return msi_irq;
> > +
> > +       irq_domain_set_info(domain, virq, msi_irq,
> > +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> > +                           handle_simple_irq, NULL, NULL);
> > +       set_irq_flags(virq, IRQF_VALID);
> > +
> > +       return 0;
> > +}
> > +
> > +static void xgene_irq_domain_free(struct irq_domain *domain,
> > +                                 unsigned int virq, unsigned int nr_irqs)
> > +{
> > +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> > +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> > +       u32 hwirq;
> > +
> > +       mutex_lock(&msi->bitmap_lock);
> > +
> > +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
> > +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
> > +
> > +       mutex_unlock(&msi->bitmap_lock);
> > +
> > +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> > +}
> > +
> > +static const struct irq_domain_ops msi_domain_ops = {
> > +       .alloc  = xgene_irq_domain_alloc,
> > +       .free   = xgene_irq_domain_free,
> > +};
> > +
> > +static int xgene_allocate_domains(struct xgene_msi *msi)
> > +{
> > +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> > +                                           &msi_domain_ops, msi);
> > +       if (!msi->domain)
> > +               return -ENOMEM;
> > +
> > +       msi->mchip.of_node = msi->node;
> > +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> > +                                                     &xgene_msi_domain_info,
> > +                                                     msi->domain);
> > +
> > +       if (!msi->mchip.domain) {
> > +               irq_domain_remove(msi->domain);
> > +               return -ENOMEM;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static void xgene_free_domains(struct xgene_msi *msi)
> > +{
> > +       if (msi->mchip.domain)
> > +               irq_domain_remove(msi->mchip.domain);
> > +       if (msi->domain)
> > +               irq_domain_remove(msi->domain);
> > +}
> > +
> > +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> > +{
> > +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> > +
> > +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> > +       if (!xgene_msi->bitmap)
> > +               return -ENOMEM;
> > +
> > +       mutex_init(&xgene_msi->bitmap_lock);
> > +
> > +       xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
> > +       if (!xgene_msi->msi_virqs)
> > +               return -ENOMEM;
> > +
> > +       return 0;
> > +}
> > +
> > +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> > +{
> > +       struct irq_chip *chip = irq_desc_get_chip(desc);
> > +       struct xgene_msi *xgene_msi;
> > +       unsigned int virq;
> > +       int msir_index, msir_reg, msir_val, hw_irq;
> > +       u32 intr_index, grp_select, msi_grp;
> > +       int i;
> > +
> > +       chained_irq_enter(chip, desc);
> > +
> > +       xgene_msi = irq_desc_get_handler_data(desc);
> > +
> > +       msi_grp = NR_HW_IRQS;
> > +       for (i = 0; i < NR_HW_IRQS; i++)
> > +               if (irq == xgene_msi->msi_virqs[i]) {
> > +                       msi_grp = i;
> > +                       break;
> > +               }
>
> Oh, please!!! In v3, you were giving silly reasons to shave one cycle
> off a slow path, but you now seem pretty happy to run this loop on the
> bloody hot path!
>
> I already suggested that you actually save the group in handler_data,
> and reference the global xgene_msi, since it is guaranteed to be
> unique. If you really want to pointlessly follow pointers, you could
> replace your msi_virqs array with an array of:
>
> struct xgene_msi_group {
>         struct xgene_msi        *xgene_msi;
>         int                     gic_irq;
>         u32                     msi_grp;
> };
>

I changed this part as you suggested.

> and make the relevant structure pointer to by desc->handler_data. That
> will give you the information you need once and for all, without any
> silly, cycle wasting loop.
>
> > +       if (msi_grp >= NR_HW_IRQS) {
> > +               chained_irq_exit(chip, desc);
> > +               return;
> > +       }
>
> And I really can't see how this can ever be valid.

This is removed with the use of msi_grp above.
>
> > +
> > +       /*
> > +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> > +        * If bit x of this register is set (x is 0..7), one or more interupts
> > +        * corresponding to MSInIRx is set.
> > +        */
> > +       grp_select = readl_relaxed(xgene_msi->msi_regs +
> > +                                  MSI_INT0 + (msi_grp << 16));
> > +       while (grp_select) {
> > +               msir_index = ffs(grp_select) - 1;
> > +               /*
> > +                * Calculate MSInIRx address to read to check for interrupts
> > +                * (refer to termination address and data assignment
> > +                * described in xgene_compose_msi_msg function)
> > +                */
> > +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> > +               msir_val = readl_relaxed(xgene_msi->msi_regs +
> > +                                        MSI_IR0 + msir_reg);
> > +               while (msir_val) {
> > +                       intr_index = ffs(msir_val) - 1;
> > +                       /*
> > +                        * Calculate MSI vector number (refer to the termination
> > +                        * address and data assignment described in
> > +                        * xgene_compose_msi_msg function)
> > +                        */
> > +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> > +                                NR_HW_IRQS) + msi_grp;
> > +                       /*
> > +                        * As we have multiple hw_irq that maps to single MSI,
> > +                        * always look up the virq using the hw_irq as seen from
> > +                        * CPU0
> > +                        */
> > +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
> > +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> > +                       if (virq != 0)
> > +                               generic_handle_irq(virq);
>
> The (virq == 0) case certainly deserves a warning, because it indicates
> something went horribly wrong somewhere.
>
A WARN_ON() is added in v7 patch.

> > +                       msir_val &= ~(1 << intr_index);
> > +               }
> > +               grp_select &= ~(1 << msir_index);
> > +       }
> > +
> > +       chained_irq_exit(chip, desc);
> > +}
> > +
> > +static int xgene_msi_remove(struct platform_device *pdev)
> > +{
> > +       int virq, i;
> > +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> > +
> > +       for (i = 0; i < NR_HW_IRQS; i++) {
> > +               virq = msi->msi_virqs[i];
> > +               if (virq != 0)
> > +                       free_irq(virq, msi);
> > +       }
> > +       kfree(msi->msi_virqs);
> > +
> > +       kfree(msi->bitmap);
> > +       msi->bitmap = NULL;
> > +
> > +       xgene_free_domains(msi);
> > +
> > +       return 0;
> > +}
> > +
> > +static void xgene_msi_hwirq_alloc(unsigned int cpu)
> > +{
> > +       struct xgene_msi *msi = &xgene_msi_ctrl;
> > +       cpumask_var_t mask;
> > +       int i;
> > +       int err;
> > +
> > +       for (i = 0; i < NR_HW_IRQS; i++) {
> > +               if (i % msi->num_cpus != cpu)
> > +                       continue;
> > +               if (!msi->msi_virqs[i])
> > +                       continue;
> > +
> > +               irq_set_chained_handler(msi->msi_virqs[i], xgene_msi_isr);
> > +               err = irq_set_handler_data(msi->msi_virqs[i], msi);
>
> See? msi is *always* set to &xgene_msi_ctrl. So just encode the group
> or use a structure similar to the one I've outlined above.
>
> > +               if (err)
> > +                       continue;
>
> So we got a critical error that just makes the driver unusable, and yet
> we silently continue? Sounds like a plan...
>

I added additional error checks in v7 patch.

> > +
> > +               /*
> > +                * Statically allocate MSI GIC IRQs to each CPU core
> > +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> > +                * to each core.
> > +                */
> > +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> > +                       cpumask_clear(mask);
> > +                       cpumask_set_cpu(cpu, mask);
> > +                       err = irq_set_affinity(msi->msi_virqs[i], mask);
> > +                       if (err) {
> > +                               free_irq(msi->msi_virqs[i], msi);
> > +                               free_cpumask_var(mask);
> > +                               continue;
>
> Same here...
>
> > +                       }
> > +                       free_cpumask_var(mask);
> > +               }
> > +       }
> > +}
> > +
> > +static void xgene_msi_hwirq_free(unsigned int cpu)
> > +{
> > +       /*
> > +        * Kernel takes care of moving MSI interrupts that
> > +        * are steered to this CPU to another online CPU
> > +        * and freeing the interrupt handlers of GIC IRQs
> > +        * allocated for this CPU, so simply return here.
> > +        */
>
> Two things:
> - I couldn't find the code that backs this statement. MSIs (actually
>   all interrupts) will indeed be moved, but I can't see how the kernel
>   is going to free GIC interrupt. Care to point me to it?
> - Assuming this statement is true, why do we need this function at all?
>
I was wrong here. I was using free_irq for chained irq and got an
compain about freeing already freed irq.

I changed to use irq_set_chained_handler(virq, NULL) to mask chained
irq in v7 patch.

>
> > +       return;
> > +}
> > +
> > +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
> > +                                 unsigned long action, void *hcpu)
> > +{
> > +       unsigned cpu = (unsigned long)hcpu;
> > +
> > +       switch (action) {
> > +       case CPU_ONLINE:
> > +       case CPU_ONLINE_FROZEN:
> > +               xgene_msi_hwirq_alloc(cpu);
> > +               break;
> > +       case CPU_DEAD:
> > +       case CPU_DEAD_FROZEN:
> > +               xgene_msi_hwirq_free(cpu);
> > +               break;
> > +       default:
> > +               break;
> > +       }
> > +
> > +       return NOTIFY_OK;
> > +}
> > +
> > +static struct notifier_block xgene_msi_cpu_notifier = {
> > +       .notifier_call = xgene_msi_cpu_callback,
> > +};
> > +
> > +static const struct of_device_id xgene_msi_match_table[] = {
> > +       {.compatible = "apm,xgene1-msi"},
> > +       {},
> > +};
> > +
> > +static int xgene_msi_probe(struct platform_device *pdev)
> > +{
> > +       struct resource *res;
> > +       int rc, irq_index;
> > +       struct xgene_msi *xgene_msi;
> > +       unsigned int cpu;
> > +       int virt_msir;
> > +
> > +       xgene_msi = &xgene_msi_ctrl;
> > +       xgene_msi->node = pdev->dev.of_node;
>
> The only purpose the xgene_msi->node seems to be an intermediate
> storage for set mchip.of_node. Why don't you set the latter directly,
> and drop the former?
>
This assignment is dropped in v7 patch.
> > +
> > +       platform_set_drvdata(pdev, xgene_msi);
> > +
> > +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> > +       if (IS_ERR(xgene_msi->msi_regs)) {
> > +               dev_err(&pdev->dev, "no reg space\n");
> > +               rc = -EINVAL;
> > +               goto error;
> > +       }
> > +       xgene_msi->msi_addr = res->start;
> > +
> > +       xgene_msi->num_cpus = num_possible_cpus();
> > +
> > +       rc = xgene_msi_init_allocator(xgene_msi);
> > +       if (rc) {
> > +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> > +               goto error;
> > +       }
> > +
> > +       rc = xgene_allocate_domains(xgene_msi);
> > +       if (rc) {
> > +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> > +               goto error;
> > +       }
> > +
> > +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> > +               virt_msir = platform_get_irq(pdev, irq_index);
> > +               if (virt_msir < 0) {
> > +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> > +                               irq_index);
> > +                       rc = -EINVAL;
> > +                       goto error;
> > +               }
> > +               xgene_msi->msi_virqs[irq_index] = virt_msir;
> > +       }
> > +
> > +       cpu_notifier_register_begin();
> > +
> > +       for_each_online_cpu(cpu)
> > +               xgene_msi_hwirq_alloc(cpu);
>
> Why does this need to be in the cpu_notifier critical section? What
> will you do when xgene_msi_hwirq_alloc() fails?
>
I followed the instruction in Documentation/cpu-hotplug.txt to
register a hotplug callback, as well as perform initialization for
CPUs that are already online. xgene_msi_hwirq_alloc registers and
assigns IRQs to each CPU separately, so it is also safe to move it out
of the cpu_notifier critical section.

I add the error check if xgene_msi_hwirq_alloc fails in v7 patch.

> > +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
> > +       if (rc) {
> > +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
> > +               cpu_notifier_register_done();
> > +               goto error;
> > +       }
> > +
> > +       cpu_notifier_register_done();
> > +
> > +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> > +       if (rc) {
>
> Assuming we fail here...
>
> > +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> > +               goto error;
> > +       }
> > +
> > +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> > +
> > +       return 0;
> > +error:
> > +       xgene_msi_remove(pdev);
>
> ... we end-up here. But xgene_msi_remove doesn't unregister the
> notifier...

Code to unregister the notifier is added in v7 patch.
>
> > +       return rc;
> > +}
> > +
> > +static struct platform_driver xgene_msi_driver = {
> > +       .driver = {
> > +               .name = "xgene-msi",
> > +               .owner = THIS_MODULE,
> > +               .of_match_table = xgene_msi_match_table,
> > +       },
> > +       .probe = xgene_msi_probe,
> > +       .remove = xgene_msi_remove,
> > +};
> > +
> > +static int __init xgene_pcie_msi_init(void)
> > +{
> > +       return platform_driver_register(&xgene_msi_driver);
> > +}
> > +subsys_initcall(xgene_pcie_msi_init);
> > +
> > diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> > index 22751ed..3e6faa1 100644
> > --- a/drivers/pci/host/pci-xgene.c
> > +++ b/drivers/pci/host/pci-xgene.c
> > @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
> >         return 0;
> >  }
> >
> > +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> > +{
> > +       struct device_node *msi_node;
> > +
> > +       msi_node = of_parse_phandle(bus->dev.of_node,
> > +                                       "msi-parent", 0);
> > +       if (!msi_node)
> > +               return -ENODEV;
> > +
> > +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> > +       if (bus->msi)
> > +               bus->msi->dev = &bus->dev;
> > +       else
> > +               return -ENODEV;
> > +       return 0;
> > +}
> > +
> >  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
> >  {
> >         struct device_node *dn = pdev->dev.of_node;
> > @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
> >         if (!bus)
> >                 return -ENOMEM;
> >
> > +       if (IS_ENABLED(CONFIG_PCI_MSI))
> > +               if (xgene_pcie_msi_enable(bus))
> > +                       dev_info(port->dev, "failed to enable MSI\n");
> > +
> >         pci_scan_child_bus(bus);
> >         pci_assign_unassigned_bus_resources(bus);
> >         pci_bus_add_devices(bus);
> > --
> > 1.9.1
> >
>
> In somehow related news, this is my last review on this code for
> quite some time. You've posted it 4 times in less than two weeks, right
> in the middle of the merge window, I've given it a lot of attention,
> and you've just run out of credits on the reviewing arcade game.
>
> I suggest you spend some quality time with it, polish it as much as you
> can. and post it again in about three weeks. Don't just make it work.
> Make it beautiful. Make it something I become madly in love with. By
> that time, I'll have forgotten about it and maybe I'll be swept off my
> feet when I come back from holiday.
>
> Thanks,
>
>         M.
> --
> Without deviation from the norm, progress is not possible.

Regards,
Duc Dang.

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

* Re: [PATCH v6 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-18 10:12                                                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-18 10:12 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Wed, Apr 22, 2015 at 5:50 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>
> On Wed, 22 Apr 2015 07:15:09 +0100
> Duc Dang <dhdang@apm.com> wrote:
>
> > APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> > to GIC V2M specification for MSI Termination.
> >
> > There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> > block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> > and shared across all 5 PCIe ports.
> >
> > As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
> > correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
> > v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
> > is moved around these HW IRQs lines. With this approach, the total MSI vectors this
> > driver supports is reduced to 256.
> >
> > Signed-off-by: Duc Dang <dhdang@apm.com>
> > Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> > ---
> >  drivers/pci/host/Kconfig         |   8 +
> >  drivers/pci/host/Makefile        |   1 +
> >  drivers/pci/host/pci-xgene-msi.c | 504 +++++++++++++++++++++++++++++++++++++++
> >  drivers/pci/host/pci-xgene.c     |  21 ++
> >  4 files changed, 534 insertions(+)
> >  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> >
> > diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> > index 7b892a9..9f1e2b5 100644
> > --- a/drivers/pci/host/Kconfig
> > +++ b/drivers/pci/host/Kconfig
> > @@ -89,11 +89,19 @@ config PCI_XGENE
> >         depends on ARCH_XGENE
> >         depends on OF
> >         select PCIEPORTBUS
> > +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> > +       select PCI_XGENE_MSI if PCI_MSI
> >         help
> >           Say Y here if you want internal PCI support on APM X-Gene SoC.
> >           There are 5 internal PCIe ports available. Each port is GEN3 capable
> >           and have varied lanes from x1 to x8.
> >
> > +config PCI_XGENE_MSI
> > +       bool "X-Gene v1 PCIe MSI feature"
> > +       depends on PCI_XGENE && PCI_MSI
> > +       help
> > +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC
> > +
> >  config PCI_LAYERSCAPE
> >         bool "Freescale Layerscape PCIe controller"
> >         depends on OF && ARM
> > diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> > index e61d91c..f39bde3 100644
> > --- a/drivers/pci/host/Makefile
> > +++ b/drivers/pci/host/Makefile
> > @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> > +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
> >  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
> >  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> > diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> > new file mode 100644
> > index 0000000..8bbf925
> > --- /dev/null
> > +++ b/drivers/pci/host/pci-xgene-msi.c
> > @@ -0,0 +1,504 @@
> > +/*
> > + * APM X-Gene MSI Driver
> > + *
> > + * Copyright (c) 2014, Applied Micro Circuits Corporation
> > + * Author: Tanmay Inamdar <tinamdar@apm.com>
> > + *        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 as published by the
> > + * Free Software Foundation;  either version 2 of the  License, or (at your
> > + * option) any later version.
> > + *
> > + * 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.
> > + */
> > +#include <linux/cpu.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/module.h>
> > +#include <linux/msi.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/pci.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/of_pci.h>
> > +
> > +#define MSI_IR0                        0x000000
> > +#define MSI_INT0               0x800000
> > +#define IDX_PER_GROUP          8
> > +#define IRQS_PER_IDX           16
> > +#define NR_HW_IRQS             16
> > +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> > +
> > +struct xgene_msi {
> > +       struct device_node              *node;
> > +       struct msi_controller           mchip;
> > +       struct irq_domain               *domain;
> > +       u64                             msi_addr;
> > +       void __iomem                    *msi_regs;
> > +       unsigned long                   *bitmap;
> > +       struct mutex                    bitmap_lock;
> > +       int                             *msi_virqs;
> > +       int                             num_cpus;
> > +};
> > +
> > +/* Global data */
> > +static struct xgene_msi xgene_msi_ctrl;
> > +
> > +static struct irq_chip xgene_msi_top_irq_chip = {
> > +       .name           = "X-Gene1 MSI",
> > +       .irq_enable     = pci_msi_unmask_irq,
> > +       .irq_disable    = pci_msi_mask_irq,
> > +       .irq_mask       = pci_msi_mask_irq,
> > +       .irq_unmask     = pci_msi_unmask_irq,
> > +};
> > +
> > +static struct  msi_domain_info xgene_msi_domain_info = {
> > +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> > +                 MSI_FLAG_PCI_MSIX),
> > +       .chip   = &xgene_msi_top_irq_chip,
> > +};
> > +
> > +/*
> > + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> > + * n is group number (0..F), x is index of registers in each group (0..7)
> > + * The registers layout is like following:
> > + * MSI0IR0                     base_addr
> > + * MSI0IR1                     base_addr +  0x10000
> > + * ...                         ...
> > + * MSI0IR6                     base_addr +  0x60000
> > + * MSI0IR7                     base_addr +  0x70000
> > + * MSI1IR0                     base_addr +  0x80000
> > + * MSI1IR1                     base_addr +  0x90000
> > + * ...                         ...
> > + * MSI1IR7                     base_addr +  0xF0000
> > + * MSI2IR0                     base_addr + 0x100000
> > + * ...                         ...
> > + * MSIFIR0                     base_addr + 0x780000
> > + * MSIFIR1                     base_addr + 0x790000
> > + * ...                         ...
> > + * MSIFIR7                     base_addr + 0x7F0000
> > + *
> > + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> > + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> > + * registers.
> > + *
> > + * With 2048 MSI vectors supported, the MSI message can be construct using
> > + * following scheme:
> > + * - Divide into 8 256-vector groups
> > + *             Group 0: 0-255
> > + *             Group 1: 256-511
> > + *             Group 2: 512-767
> > + *             ...
> > + *             Group 7: 1792-2047
> > + * - Each 256-vector group is divided into 16 16-vector groups
> > + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> > + *             Group 0: 0-15
> > + *             Group 1: 16-32
> > + *             ...
> > + *             Group 15: 240-255
> > + * - The termination address of MSI vector in 256-vector group n and 16-vector
> > + * group x is the address of MSIxIRn
> > + * - The data for MSI vector in 16-vector group x is x
> > + */
> > +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> > +{
> > +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> > +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> > +       u32 group = data->hwirq % NR_HW_IRQS;
> > +
> > +       msg->address_hi = upper_32_bits(msi->msi_addr);
> > +       msg->address_lo = lower_32_bits(msi->msi_addr) +
> > +                         (((8 * group) + reg_set) << 16);
> > +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> > +}
> > +
> > +/*
> > + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> > + * To maintain the expected behaviour of .set_affinity for each MSI
> > + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> > + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> > + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> > + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> > + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> > + */
> > +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> > +                                 const struct cpumask *mask, bool force)
> > +{
> > +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> > +       struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
> > +       int target_cpu = cpumask_first(mask);
> > +       int curr_cpu;
> > +
> > +       if (!desc)
> > +               return -EINVAL;
>
> I still don't understand under which circumstances this could ever be
> NULL. You got here because someone has created this interrupt, and
> inserted it in the domain. Someone also found a way to reference it,
> and call this code. It must have been initialized the first place.
>
> Also, nothing uses this variable in what follows, so please drop it
> unless you can prove that desc can be NULL is that it is unsafe to
> proceed further.
>

I drop this in v7 patch.

> > +
> > +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
> > +       if (curr_cpu == target_cpu)
> > +               return IRQ_SET_MASK_OK_DONE;
> > +
> > +       /* Update MSI number to target the new CPU */
> > +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> > +
> > +       return IRQ_SET_MASK_OK;
> > +}
> > +
> > +static struct irq_chip xgene_msi_bottom_irq_chip = {
> > +       .name                   = "MSI",
> > +       .irq_set_affinity       = xgene_msi_set_affinity,
> > +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> > +};
> > +
> > +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> > +                                 unsigned int nr_irqs, void *args)
> > +{
> > +       struct xgene_msi *msi = domain->host_data;
> > +       int msi_irq;
> > +
> > +       mutex_lock(&msi->bitmap_lock);
> > +
> > +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
> > +                                            msi->num_cpus, 0);
> > +       if (msi_irq < NR_MSI_VEC)
> > +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
> > +       else
> > +               msi_irq = -ENOSPC;
> > +
> > +       mutex_unlock(&msi->bitmap_lock);
> > +
> > +       if (msi_irq < 0)
> > +               return msi_irq;
> > +
> > +       irq_domain_set_info(domain, virq, msi_irq,
> > +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> > +                           handle_simple_irq, NULL, NULL);
> > +       set_irq_flags(virq, IRQF_VALID);
> > +
> > +       return 0;
> > +}
> > +
> > +static void xgene_irq_domain_free(struct irq_domain *domain,
> > +                                 unsigned int virq, unsigned int nr_irqs)
> > +{
> > +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> > +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> > +       u32 hwirq;
> > +
> > +       mutex_lock(&msi->bitmap_lock);
> > +
> > +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
> > +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
> > +
> > +       mutex_unlock(&msi->bitmap_lock);
> > +
> > +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> > +}
> > +
> > +static const struct irq_domain_ops msi_domain_ops = {
> > +       .alloc  = xgene_irq_domain_alloc,
> > +       .free   = xgene_irq_domain_free,
> > +};
> > +
> > +static int xgene_allocate_domains(struct xgene_msi *msi)
> > +{
> > +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> > +                                           &msi_domain_ops, msi);
> > +       if (!msi->domain)
> > +               return -ENOMEM;
> > +
> > +       msi->mchip.of_node = msi->node;
> > +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> > +                                                     &xgene_msi_domain_info,
> > +                                                     msi->domain);
> > +
> > +       if (!msi->mchip.domain) {
> > +               irq_domain_remove(msi->domain);
> > +               return -ENOMEM;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static void xgene_free_domains(struct xgene_msi *msi)
> > +{
> > +       if (msi->mchip.domain)
> > +               irq_domain_remove(msi->mchip.domain);
> > +       if (msi->domain)
> > +               irq_domain_remove(msi->domain);
> > +}
> > +
> > +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> > +{
> > +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> > +
> > +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> > +       if (!xgene_msi->bitmap)
> > +               return -ENOMEM;
> > +
> > +       mutex_init(&xgene_msi->bitmap_lock);
> > +
> > +       xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
> > +       if (!xgene_msi->msi_virqs)
> > +               return -ENOMEM;
> > +
> > +       return 0;
> > +}
> > +
> > +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> > +{
> > +       struct irq_chip *chip = irq_desc_get_chip(desc);
> > +       struct xgene_msi *xgene_msi;
> > +       unsigned int virq;
> > +       int msir_index, msir_reg, msir_val, hw_irq;
> > +       u32 intr_index, grp_select, msi_grp;
> > +       int i;
> > +
> > +       chained_irq_enter(chip, desc);
> > +
> > +       xgene_msi = irq_desc_get_handler_data(desc);
> > +
> > +       msi_grp = NR_HW_IRQS;
> > +       for (i = 0; i < NR_HW_IRQS; i++)
> > +               if (irq == xgene_msi->msi_virqs[i]) {
> > +                       msi_grp = i;
> > +                       break;
> > +               }
>
> Oh, please!!! In v3, you were giving silly reasons to shave one cycle
> off a slow path, but you now seem pretty happy to run this loop on the
> bloody hot path!
>
> I already suggested that you actually save the group in handler_data,
> and reference the global xgene_msi, since it is guaranteed to be
> unique. If you really want to pointlessly follow pointers, you could
> replace your msi_virqs array with an array of:
>
> struct xgene_msi_group {
>         struct xgene_msi        *xgene_msi;
>         int                     gic_irq;
>         u32                     msi_grp;
> };
>

I changed this part as you suggested.

> and make the relevant structure pointer to by desc->handler_data. That
> will give you the information you need once and for all, without any
> silly, cycle wasting loop.
>
> > +       if (msi_grp >= NR_HW_IRQS) {
> > +               chained_irq_exit(chip, desc);
> > +               return;
> > +       }
>
> And I really can't see how this can ever be valid.

This is removed with the use of msi_grp above.
>
> > +
> > +       /*
> > +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> > +        * If bit x of this register is set (x is 0..7), one or more interupts
> > +        * corresponding to MSInIRx is set.
> > +        */
> > +       grp_select = readl_relaxed(xgene_msi->msi_regs +
> > +                                  MSI_INT0 + (msi_grp << 16));
> > +       while (grp_select) {
> > +               msir_index = ffs(grp_select) - 1;
> > +               /*
> > +                * Calculate MSInIRx address to read to check for interrupts
> > +                * (refer to termination address and data assignment
> > +                * described in xgene_compose_msi_msg function)
> > +                */
> > +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> > +               msir_val = readl_relaxed(xgene_msi->msi_regs +
> > +                                        MSI_IR0 + msir_reg);
> > +               while (msir_val) {
> > +                       intr_index = ffs(msir_val) - 1;
> > +                       /*
> > +                        * Calculate MSI vector number (refer to the termination
> > +                        * address and data assignment described in
> > +                        * xgene_compose_msi_msg function)
> > +                        */
> > +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> > +                                NR_HW_IRQS) + msi_grp;
> > +                       /*
> > +                        * As we have multiple hw_irq that maps to single MSI,
> > +                        * always look up the virq using the hw_irq as seen from
> > +                        * CPU0
> > +                        */
> > +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
> > +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> > +                       if (virq != 0)
> > +                               generic_handle_irq(virq);
>
> The (virq == 0) case certainly deserves a warning, because it indicates
> something went horribly wrong somewhere.
>
A WARN_ON() is added in v7 patch.

> > +                       msir_val &= ~(1 << intr_index);
> > +               }
> > +               grp_select &= ~(1 << msir_index);
> > +       }
> > +
> > +       chained_irq_exit(chip, desc);
> > +}
> > +
> > +static int xgene_msi_remove(struct platform_device *pdev)
> > +{
> > +       int virq, i;
> > +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> > +
> > +       for (i = 0; i < NR_HW_IRQS; i++) {
> > +               virq = msi->msi_virqs[i];
> > +               if (virq != 0)
> > +                       free_irq(virq, msi);
> > +       }
> > +       kfree(msi->msi_virqs);
> > +
> > +       kfree(msi->bitmap);
> > +       msi->bitmap = NULL;
> > +
> > +       xgene_free_domains(msi);
> > +
> > +       return 0;
> > +}
> > +
> > +static void xgene_msi_hwirq_alloc(unsigned int cpu)
> > +{
> > +       struct xgene_msi *msi = &xgene_msi_ctrl;
> > +       cpumask_var_t mask;
> > +       int i;
> > +       int err;
> > +
> > +       for (i = 0; i < NR_HW_IRQS; i++) {
> > +               if (i % msi->num_cpus != cpu)
> > +                       continue;
> > +               if (!msi->msi_virqs[i])
> > +                       continue;
> > +
> > +               irq_set_chained_handler(msi->msi_virqs[i], xgene_msi_isr);
> > +               err = irq_set_handler_data(msi->msi_virqs[i], msi);
>
> See? msi is *always* set to &xgene_msi_ctrl. So just encode the group
> or use a structure similar to the one I've outlined above.
>
> > +               if (err)
> > +                       continue;
>
> So we got a critical error that just makes the driver unusable, and yet
> we silently continue? Sounds like a plan...
>

I added additional error checks in v7 patch.

> > +
> > +               /*
> > +                * Statically allocate MSI GIC IRQs to each CPU core
> > +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> > +                * to each core.
> > +                */
> > +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> > +                       cpumask_clear(mask);
> > +                       cpumask_set_cpu(cpu, mask);
> > +                       err = irq_set_affinity(msi->msi_virqs[i], mask);
> > +                       if (err) {
> > +                               free_irq(msi->msi_virqs[i], msi);
> > +                               free_cpumask_var(mask);
> > +                               continue;
>
> Same here...
>
> > +                       }
> > +                       free_cpumask_var(mask);
> > +               }
> > +       }
> > +}
> > +
> > +static void xgene_msi_hwirq_free(unsigned int cpu)
> > +{
> > +       /*
> > +        * Kernel takes care of moving MSI interrupts that
> > +        * are steered to this CPU to another online CPU
> > +        * and freeing the interrupt handlers of GIC IRQs
> > +        * allocated for this CPU, so simply return here.
> > +        */
>
> Two things:
> - I couldn't find the code that backs this statement. MSIs (actually
>   all interrupts) will indeed be moved, but I can't see how the kernel
>   is going to free GIC interrupt. Care to point me to it?
> - Assuming this statement is true, why do we need this function at all?
>
I was wrong here. I was using free_irq for chained irq and got an
compain about freeing already freed irq.

I changed to use irq_set_chained_handler(virq, NULL) to mask chained
irq in v7 patch.

>
> > +       return;
> > +}
> > +
> > +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
> > +                                 unsigned long action, void *hcpu)
> > +{
> > +       unsigned cpu = (unsigned long)hcpu;
> > +
> > +       switch (action) {
> > +       case CPU_ONLINE:
> > +       case CPU_ONLINE_FROZEN:
> > +               xgene_msi_hwirq_alloc(cpu);
> > +               break;
> > +       case CPU_DEAD:
> > +       case CPU_DEAD_FROZEN:
> > +               xgene_msi_hwirq_free(cpu);
> > +               break;
> > +       default:
> > +               break;
> > +       }
> > +
> > +       return NOTIFY_OK;
> > +}
> > +
> > +static struct notifier_block xgene_msi_cpu_notifier = {
> > +       .notifier_call = xgene_msi_cpu_callback,
> > +};
> > +
> > +static const struct of_device_id xgene_msi_match_table[] = {
> > +       {.compatible = "apm,xgene1-msi"},
> > +       {},
> > +};
> > +
> > +static int xgene_msi_probe(struct platform_device *pdev)
> > +{
> > +       struct resource *res;
> > +       int rc, irq_index;
> > +       struct xgene_msi *xgene_msi;
> > +       unsigned int cpu;
> > +       int virt_msir;
> > +
> > +       xgene_msi = &xgene_msi_ctrl;
> > +       xgene_msi->node = pdev->dev.of_node;
>
> The only purpose the xgene_msi->node seems to be an intermediate
> storage for set mchip.of_node. Why don't you set the latter directly,
> and drop the former?
>
This assignment is dropped in v7 patch.
> > +
> > +       platform_set_drvdata(pdev, xgene_msi);
> > +
> > +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> > +       if (IS_ERR(xgene_msi->msi_regs)) {
> > +               dev_err(&pdev->dev, "no reg space\n");
> > +               rc = -EINVAL;
> > +               goto error;
> > +       }
> > +       xgene_msi->msi_addr = res->start;
> > +
> > +       xgene_msi->num_cpus = num_possible_cpus();
> > +
> > +       rc = xgene_msi_init_allocator(xgene_msi);
> > +       if (rc) {
> > +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> > +               goto error;
> > +       }
> > +
> > +       rc = xgene_allocate_domains(xgene_msi);
> > +       if (rc) {
> > +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> > +               goto error;
> > +       }
> > +
> > +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> > +               virt_msir = platform_get_irq(pdev, irq_index);
> > +               if (virt_msir < 0) {
> > +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> > +                               irq_index);
> > +                       rc = -EINVAL;
> > +                       goto error;
> > +               }
> > +               xgene_msi->msi_virqs[irq_index] = virt_msir;
> > +       }
> > +
> > +       cpu_notifier_register_begin();
> > +
> > +       for_each_online_cpu(cpu)
> > +               xgene_msi_hwirq_alloc(cpu);
>
> Why does this need to be in the cpu_notifier critical section? What
> will you do when xgene_msi_hwirq_alloc() fails?
>
I followed the instruction in Documentation/cpu-hotplug.txt to
register a hotplug callback, as well as perform initialization for
CPUs that are already online. xgene_msi_hwirq_alloc registers and
assigns IRQs to each CPU separately, so it is also safe to move it out
of the cpu_notifier critical section.

I add the error check if xgene_msi_hwirq_alloc fails in v7 patch.

> > +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
> > +       if (rc) {
> > +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
> > +               cpu_notifier_register_done();
> > +               goto error;
> > +       }
> > +
> > +       cpu_notifier_register_done();
> > +
> > +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> > +       if (rc) {
>
> Assuming we fail here...
>
> > +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> > +               goto error;
> > +       }
> > +
> > +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> > +
> > +       return 0;
> > +error:
> > +       xgene_msi_remove(pdev);
>
> ... we end-up here. But xgene_msi_remove doesn't unregister the
> notifier...

Code to unregister the notifier is added in v7 patch.
>
> > +       return rc;
> > +}
> > +
> > +static struct platform_driver xgene_msi_driver = {
> > +       .driver = {
> > +               .name = "xgene-msi",
> > +               .owner = THIS_MODULE,
> > +               .of_match_table = xgene_msi_match_table,
> > +       },
> > +       .probe = xgene_msi_probe,
> > +       .remove = xgene_msi_remove,
> > +};
> > +
> > +static int __init xgene_pcie_msi_init(void)
> > +{
> > +       return platform_driver_register(&xgene_msi_driver);
> > +}
> > +subsys_initcall(xgene_pcie_msi_init);
> > +
> > diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> > index 22751ed..3e6faa1 100644
> > --- a/drivers/pci/host/pci-xgene.c
> > +++ b/drivers/pci/host/pci-xgene.c
> > @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
> >         return 0;
> >  }
> >
> > +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> > +{
> > +       struct device_node *msi_node;
> > +
> > +       msi_node = of_parse_phandle(bus->dev.of_node,
> > +                                       "msi-parent", 0);
> > +       if (!msi_node)
> > +               return -ENODEV;
> > +
> > +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> > +       if (bus->msi)
> > +               bus->msi->dev = &bus->dev;
> > +       else
> > +               return -ENODEV;
> > +       return 0;
> > +}
> > +
> >  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
> >  {
> >         struct device_node *dn = pdev->dev.of_node;
> > @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
> >         if (!bus)
> >                 return -ENOMEM;
> >
> > +       if (IS_ENABLED(CONFIG_PCI_MSI))
> > +               if (xgene_pcie_msi_enable(bus))
> > +                       dev_info(port->dev, "failed to enable MSI\n");
> > +
> >         pci_scan_child_bus(bus);
> >         pci_assign_unassigned_bus_resources(bus);
> >         pci_bus_add_devices(bus);
> > --
> > 1.9.1
> >
>
> In somehow related news, this is my last review on this code for
> quite some time. You've posted it 4 times in less than two weeks, right
> in the middle of the merge window, I've given it a lot of attention,
> and you've just run out of credits on the reviewing arcade game.
>
> I suggest you spend some quality time with it, polish it as much as you
> can. and post it again in about three weeks. Don't just make it work.
> Make it beautiful. Make it something I become madly in love with. By
> that time, I'll have forgotten about it and maybe I'll be swept off my
> feet when I come back from holiday.
>
> Thanks,
>
>         M.
> --
> Without deviation from the norm, progress is not possible.

Regards,
Duc Dang.

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

* [PATCH v6 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-18 10:12                                                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-18 10:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 22, 2015 at 5:50 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>
> On Wed, 22 Apr 2015 07:15:09 +0100
> Duc Dang <dhdang@apm.com> wrote:
>
> > APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> > to GIC V2M specification for MSI Termination.
> >
> > There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> > block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> > and shared across all 5 PCIe ports.
> >
> > As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
> > correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
> > v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
> > is moved around these HW IRQs lines. With this approach, the total MSI vectors this
> > driver supports is reduced to 256.
> >
> > Signed-off-by: Duc Dang <dhdang@apm.com>
> > Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> > ---
> >  drivers/pci/host/Kconfig         |   8 +
> >  drivers/pci/host/Makefile        |   1 +
> >  drivers/pci/host/pci-xgene-msi.c | 504 +++++++++++++++++++++++++++++++++++++++
> >  drivers/pci/host/pci-xgene.c     |  21 ++
> >  4 files changed, 534 insertions(+)
> >  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> >
> > diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> > index 7b892a9..9f1e2b5 100644
> > --- a/drivers/pci/host/Kconfig
> > +++ b/drivers/pci/host/Kconfig
> > @@ -89,11 +89,19 @@ config PCI_XGENE
> >         depends on ARCH_XGENE
> >         depends on OF
> >         select PCIEPORTBUS
> > +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> > +       select PCI_XGENE_MSI if PCI_MSI
> >         help
> >           Say Y here if you want internal PCI support on APM X-Gene SoC.
> >           There are 5 internal PCIe ports available. Each port is GEN3 capable
> >           and have varied lanes from x1 to x8.
> >
> > +config PCI_XGENE_MSI
> > +       bool "X-Gene v1 PCIe MSI feature"
> > +       depends on PCI_XGENE && PCI_MSI
> > +       help
> > +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC
> > +
> >  config PCI_LAYERSCAPE
> >         bool "Freescale Layerscape PCIe controller"
> >         depends on OF && ARM
> > diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> > index e61d91c..f39bde3 100644
> > --- a/drivers/pci/host/Makefile
> > +++ b/drivers/pci/host/Makefile
> > @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> > +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
> >  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
> >  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> > diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> > new file mode 100644
> > index 0000000..8bbf925
> > --- /dev/null
> > +++ b/drivers/pci/host/pci-xgene-msi.c
> > @@ -0,0 +1,504 @@
> > +/*
> > + * APM X-Gene MSI Driver
> > + *
> > + * Copyright (c) 2014, Applied Micro Circuits Corporation
> > + * Author: Tanmay Inamdar <tinamdar@apm.com>
> > + *        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 as published by the
> > + * Free Software Foundation;  either version 2 of the  License, or (at your
> > + * option) any later version.
> > + *
> > + * 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.
> > + */
> > +#include <linux/cpu.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/module.h>
> > +#include <linux/msi.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/pci.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/of_pci.h>
> > +
> > +#define MSI_IR0                        0x000000
> > +#define MSI_INT0               0x800000
> > +#define IDX_PER_GROUP          8
> > +#define IRQS_PER_IDX           16
> > +#define NR_HW_IRQS             16
> > +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> > +
> > +struct xgene_msi {
> > +       struct device_node              *node;
> > +       struct msi_controller           mchip;
> > +       struct irq_domain               *domain;
> > +       u64                             msi_addr;
> > +       void __iomem                    *msi_regs;
> > +       unsigned long                   *bitmap;
> > +       struct mutex                    bitmap_lock;
> > +       int                             *msi_virqs;
> > +       int                             num_cpus;
> > +};
> > +
> > +/* Global data */
> > +static struct xgene_msi xgene_msi_ctrl;
> > +
> > +static struct irq_chip xgene_msi_top_irq_chip = {
> > +       .name           = "X-Gene1 MSI",
> > +       .irq_enable     = pci_msi_unmask_irq,
> > +       .irq_disable    = pci_msi_mask_irq,
> > +       .irq_mask       = pci_msi_mask_irq,
> > +       .irq_unmask     = pci_msi_unmask_irq,
> > +};
> > +
> > +static struct  msi_domain_info xgene_msi_domain_info = {
> > +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> > +                 MSI_FLAG_PCI_MSIX),
> > +       .chip   = &xgene_msi_top_irq_chip,
> > +};
> > +
> > +/*
> > + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> > + * n is group number (0..F), x is index of registers in each group (0..7)
> > + * The registers layout is like following:
> > + * MSI0IR0                     base_addr
> > + * MSI0IR1                     base_addr +  0x10000
> > + * ...                         ...
> > + * MSI0IR6                     base_addr +  0x60000
> > + * MSI0IR7                     base_addr +  0x70000
> > + * MSI1IR0                     base_addr +  0x80000
> > + * MSI1IR1                     base_addr +  0x90000
> > + * ...                         ...
> > + * MSI1IR7                     base_addr +  0xF0000
> > + * MSI2IR0                     base_addr + 0x100000
> > + * ...                         ...
> > + * MSIFIR0                     base_addr + 0x780000
> > + * MSIFIR1                     base_addr + 0x790000
> > + * ...                         ...
> > + * MSIFIR7                     base_addr + 0x7F0000
> > + *
> > + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> > + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> > + * registers.
> > + *
> > + * With 2048 MSI vectors supported, the MSI message can be construct using
> > + * following scheme:
> > + * - Divide into 8 256-vector groups
> > + *             Group 0: 0-255
> > + *             Group 1: 256-511
> > + *             Group 2: 512-767
> > + *             ...
> > + *             Group 7: 1792-2047
> > + * - Each 256-vector group is divided into 16 16-vector groups
> > + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> > + *             Group 0: 0-15
> > + *             Group 1: 16-32
> > + *             ...
> > + *             Group 15: 240-255
> > + * - The termination address of MSI vector in 256-vector group n and 16-vector
> > + * group x is the address of MSIxIRn
> > + * - The data for MSI vector in 16-vector group x is x
> > + */
> > +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> > +{
> > +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> > +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> > +       u32 group = data->hwirq % NR_HW_IRQS;
> > +
> > +       msg->address_hi = upper_32_bits(msi->msi_addr);
> > +       msg->address_lo = lower_32_bits(msi->msi_addr) +
> > +                         (((8 * group) + reg_set) << 16);
> > +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> > +}
> > +
> > +/*
> > + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> > + * To maintain the expected behaviour of .set_affinity for each MSI
> > + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> > + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> > + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> > + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> > + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> > + */
> > +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> > +                                 const struct cpumask *mask, bool force)
> > +{
> > +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> > +       struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
> > +       int target_cpu = cpumask_first(mask);
> > +       int curr_cpu;
> > +
> > +       if (!desc)
> > +               return -EINVAL;
>
> I still don't understand under which circumstances this could ever be
> NULL. You got here because someone has created this interrupt, and
> inserted it in the domain. Someone also found a way to reference it,
> and call this code. It must have been initialized the first place.
>
> Also, nothing uses this variable in what follows, so please drop it
> unless you can prove that desc can be NULL is that it is unsafe to
> proceed further.
>

I drop this in v7 patch.

> > +
> > +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
> > +       if (curr_cpu == target_cpu)
> > +               return IRQ_SET_MASK_OK_DONE;
> > +
> > +       /* Update MSI number to target the new CPU */
> > +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> > +
> > +       return IRQ_SET_MASK_OK;
> > +}
> > +
> > +static struct irq_chip xgene_msi_bottom_irq_chip = {
> > +       .name                   = "MSI",
> > +       .irq_set_affinity       = xgene_msi_set_affinity,
> > +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> > +};
> > +
> > +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> > +                                 unsigned int nr_irqs, void *args)
> > +{
> > +       struct xgene_msi *msi = domain->host_data;
> > +       int msi_irq;
> > +
> > +       mutex_lock(&msi->bitmap_lock);
> > +
> > +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
> > +                                            msi->num_cpus, 0);
> > +       if (msi_irq < NR_MSI_VEC)
> > +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
> > +       else
> > +               msi_irq = -ENOSPC;
> > +
> > +       mutex_unlock(&msi->bitmap_lock);
> > +
> > +       if (msi_irq < 0)
> > +               return msi_irq;
> > +
> > +       irq_domain_set_info(domain, virq, msi_irq,
> > +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> > +                           handle_simple_irq, NULL, NULL);
> > +       set_irq_flags(virq, IRQF_VALID);
> > +
> > +       return 0;
> > +}
> > +
> > +static void xgene_irq_domain_free(struct irq_domain *domain,
> > +                                 unsigned int virq, unsigned int nr_irqs)
> > +{
> > +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> > +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> > +       u32 hwirq;
> > +
> > +       mutex_lock(&msi->bitmap_lock);
> > +
> > +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
> > +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
> > +
> > +       mutex_unlock(&msi->bitmap_lock);
> > +
> > +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> > +}
> > +
> > +static const struct irq_domain_ops msi_domain_ops = {
> > +       .alloc  = xgene_irq_domain_alloc,
> > +       .free   = xgene_irq_domain_free,
> > +};
> > +
> > +static int xgene_allocate_domains(struct xgene_msi *msi)
> > +{
> > +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> > +                                           &msi_domain_ops, msi);
> > +       if (!msi->domain)
> > +               return -ENOMEM;
> > +
> > +       msi->mchip.of_node = msi->node;
> > +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> > +                                                     &xgene_msi_domain_info,
> > +                                                     msi->domain);
> > +
> > +       if (!msi->mchip.domain) {
> > +               irq_domain_remove(msi->domain);
> > +               return -ENOMEM;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static void xgene_free_domains(struct xgene_msi *msi)
> > +{
> > +       if (msi->mchip.domain)
> > +               irq_domain_remove(msi->mchip.domain);
> > +       if (msi->domain)
> > +               irq_domain_remove(msi->domain);
> > +}
> > +
> > +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> > +{
> > +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> > +
> > +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> > +       if (!xgene_msi->bitmap)
> > +               return -ENOMEM;
> > +
> > +       mutex_init(&xgene_msi->bitmap_lock);
> > +
> > +       xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
> > +       if (!xgene_msi->msi_virqs)
> > +               return -ENOMEM;
> > +
> > +       return 0;
> > +}
> > +
> > +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> > +{
> > +       struct irq_chip *chip = irq_desc_get_chip(desc);
> > +       struct xgene_msi *xgene_msi;
> > +       unsigned int virq;
> > +       int msir_index, msir_reg, msir_val, hw_irq;
> > +       u32 intr_index, grp_select, msi_grp;
> > +       int i;
> > +
> > +       chained_irq_enter(chip, desc);
> > +
> > +       xgene_msi = irq_desc_get_handler_data(desc);
> > +
> > +       msi_grp = NR_HW_IRQS;
> > +       for (i = 0; i < NR_HW_IRQS; i++)
> > +               if (irq == xgene_msi->msi_virqs[i]) {
> > +                       msi_grp = i;
> > +                       break;
> > +               }
>
> Oh, please!!! In v3, you were giving silly reasons to shave one cycle
> off a slow path, but you now seem pretty happy to run this loop on the
> bloody hot path!
>
> I already suggested that you actually save the group in handler_data,
> and reference the global xgene_msi, since it is guaranteed to be
> unique. If you really want to pointlessly follow pointers, you could
> replace your msi_virqs array with an array of:
>
> struct xgene_msi_group {
>         struct xgene_msi        *xgene_msi;
>         int                     gic_irq;
>         u32                     msi_grp;
> };
>

I changed this part as you suggested.

> and make the relevant structure pointer to by desc->handler_data. That
> will give you the information you need once and for all, without any
> silly, cycle wasting loop.
>
> > +       if (msi_grp >= NR_HW_IRQS) {
> > +               chained_irq_exit(chip, desc);
> > +               return;
> > +       }
>
> And I really can't see how this can ever be valid.

This is removed with the use of msi_grp above.
>
> > +
> > +       /*
> > +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> > +        * If bit x of this register is set (x is 0..7), one or more interupts
> > +        * corresponding to MSInIRx is set.
> > +        */
> > +       grp_select = readl_relaxed(xgene_msi->msi_regs +
> > +                                  MSI_INT0 + (msi_grp << 16));
> > +       while (grp_select) {
> > +               msir_index = ffs(grp_select) - 1;
> > +               /*
> > +                * Calculate MSInIRx address to read to check for interrupts
> > +                * (refer to termination address and data assignment
> > +                * described in xgene_compose_msi_msg function)
> > +                */
> > +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> > +               msir_val = readl_relaxed(xgene_msi->msi_regs +
> > +                                        MSI_IR0 + msir_reg);
> > +               while (msir_val) {
> > +                       intr_index = ffs(msir_val) - 1;
> > +                       /*
> > +                        * Calculate MSI vector number (refer to the termination
> > +                        * address and data assignment described in
> > +                        * xgene_compose_msi_msg function)
> > +                        */
> > +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> > +                                NR_HW_IRQS) + msi_grp;
> > +                       /*
> > +                        * As we have multiple hw_irq that maps to single MSI,
> > +                        * always look up the virq using the hw_irq as seen from
> > +                        * CPU0
> > +                        */
> > +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
> > +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> > +                       if (virq != 0)
> > +                               generic_handle_irq(virq);
>
> The (virq == 0) case certainly deserves a warning, because it indicates
> something went horribly wrong somewhere.
>
A WARN_ON() is added in v7 patch.

> > +                       msir_val &= ~(1 << intr_index);
> > +               }
> > +               grp_select &= ~(1 << msir_index);
> > +       }
> > +
> > +       chained_irq_exit(chip, desc);
> > +}
> > +
> > +static int xgene_msi_remove(struct platform_device *pdev)
> > +{
> > +       int virq, i;
> > +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> > +
> > +       for (i = 0; i < NR_HW_IRQS; i++) {
> > +               virq = msi->msi_virqs[i];
> > +               if (virq != 0)
> > +                       free_irq(virq, msi);
> > +       }
> > +       kfree(msi->msi_virqs);
> > +
> > +       kfree(msi->bitmap);
> > +       msi->bitmap = NULL;
> > +
> > +       xgene_free_domains(msi);
> > +
> > +       return 0;
> > +}
> > +
> > +static void xgene_msi_hwirq_alloc(unsigned int cpu)
> > +{
> > +       struct xgene_msi *msi = &xgene_msi_ctrl;
> > +       cpumask_var_t mask;
> > +       int i;
> > +       int err;
> > +
> > +       for (i = 0; i < NR_HW_IRQS; i++) {
> > +               if (i % msi->num_cpus != cpu)
> > +                       continue;
> > +               if (!msi->msi_virqs[i])
> > +                       continue;
> > +
> > +               irq_set_chained_handler(msi->msi_virqs[i], xgene_msi_isr);
> > +               err = irq_set_handler_data(msi->msi_virqs[i], msi);
>
> See? msi is *always* set to &xgene_msi_ctrl. So just encode the group
> or use a structure similar to the one I've outlined above.
>
> > +               if (err)
> > +                       continue;
>
> So we got a critical error that just makes the driver unusable, and yet
> we silently continue? Sounds like a plan...
>

I added additional error checks in v7 patch.

> > +
> > +               /*
> > +                * Statically allocate MSI GIC IRQs to each CPU core
> > +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> > +                * to each core.
> > +                */
> > +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> > +                       cpumask_clear(mask);
> > +                       cpumask_set_cpu(cpu, mask);
> > +                       err = irq_set_affinity(msi->msi_virqs[i], mask);
> > +                       if (err) {
> > +                               free_irq(msi->msi_virqs[i], msi);
> > +                               free_cpumask_var(mask);
> > +                               continue;
>
> Same here...
>
> > +                       }
> > +                       free_cpumask_var(mask);
> > +               }
> > +       }
> > +}
> > +
> > +static void xgene_msi_hwirq_free(unsigned int cpu)
> > +{
> > +       /*
> > +        * Kernel takes care of moving MSI interrupts that
> > +        * are steered to this CPU to another online CPU
> > +        * and freeing the interrupt handlers of GIC IRQs
> > +        * allocated for this CPU, so simply return here.
> > +        */
>
> Two things:
> - I couldn't find the code that backs this statement. MSIs (actually
>   all interrupts) will indeed be moved, but I can't see how the kernel
>   is going to free GIC interrupt. Care to point me to it?
> - Assuming this statement is true, why do we need this function at all?
>
I was wrong here. I was using free_irq for chained irq and got an
compain about freeing already freed irq.

I changed to use irq_set_chained_handler(virq, NULL) to mask chained
irq in v7 patch.

>
> > +       return;
> > +}
> > +
> > +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
> > +                                 unsigned long action, void *hcpu)
> > +{
> > +       unsigned cpu = (unsigned long)hcpu;
> > +
> > +       switch (action) {
> > +       case CPU_ONLINE:
> > +       case CPU_ONLINE_FROZEN:
> > +               xgene_msi_hwirq_alloc(cpu);
> > +               break;
> > +       case CPU_DEAD:
> > +       case CPU_DEAD_FROZEN:
> > +               xgene_msi_hwirq_free(cpu);
> > +               break;
> > +       default:
> > +               break;
> > +       }
> > +
> > +       return NOTIFY_OK;
> > +}
> > +
> > +static struct notifier_block xgene_msi_cpu_notifier = {
> > +       .notifier_call = xgene_msi_cpu_callback,
> > +};
> > +
> > +static const struct of_device_id xgene_msi_match_table[] = {
> > +       {.compatible = "apm,xgene1-msi"},
> > +       {},
> > +};
> > +
> > +static int xgene_msi_probe(struct platform_device *pdev)
> > +{
> > +       struct resource *res;
> > +       int rc, irq_index;
> > +       struct xgene_msi *xgene_msi;
> > +       unsigned int cpu;
> > +       int virt_msir;
> > +
> > +       xgene_msi = &xgene_msi_ctrl;
> > +       xgene_msi->node = pdev->dev.of_node;
>
> The only purpose the xgene_msi->node seems to be an intermediate
> storage for set mchip.of_node. Why don't you set the latter directly,
> and drop the former?
>
This assignment is dropped in v7 patch.
> > +
> > +       platform_set_drvdata(pdev, xgene_msi);
> > +
> > +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> > +       if (IS_ERR(xgene_msi->msi_regs)) {
> > +               dev_err(&pdev->dev, "no reg space\n");
> > +               rc = -EINVAL;
> > +               goto error;
> > +       }
> > +       xgene_msi->msi_addr = res->start;
> > +
> > +       xgene_msi->num_cpus = num_possible_cpus();
> > +
> > +       rc = xgene_msi_init_allocator(xgene_msi);
> > +       if (rc) {
> > +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> > +               goto error;
> > +       }
> > +
> > +       rc = xgene_allocate_domains(xgene_msi);
> > +       if (rc) {
> > +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> > +               goto error;
> > +       }
> > +
> > +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> > +               virt_msir = platform_get_irq(pdev, irq_index);
> > +               if (virt_msir < 0) {
> > +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> > +                               irq_index);
> > +                       rc = -EINVAL;
> > +                       goto error;
> > +               }
> > +               xgene_msi->msi_virqs[irq_index] = virt_msir;
> > +       }
> > +
> > +       cpu_notifier_register_begin();
> > +
> > +       for_each_online_cpu(cpu)
> > +               xgene_msi_hwirq_alloc(cpu);
>
> Why does this need to be in the cpu_notifier critical section? What
> will you do when xgene_msi_hwirq_alloc() fails?
>
I followed the instruction in Documentation/cpu-hotplug.txt to
register a hotplug callback, as well as perform initialization for
CPUs that are already online. xgene_msi_hwirq_alloc registers and
assigns IRQs to each CPU separately, so it is also safe to move it out
of the cpu_notifier critical section.

I add the error check if xgene_msi_hwirq_alloc fails in v7 patch.

> > +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
> > +       if (rc) {
> > +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
> > +               cpu_notifier_register_done();
> > +               goto error;
> > +       }
> > +
> > +       cpu_notifier_register_done();
> > +
> > +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> > +       if (rc) {
>
> Assuming we fail here...
>
> > +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> > +               goto error;
> > +       }
> > +
> > +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> > +
> > +       return 0;
> > +error:
> > +       xgene_msi_remove(pdev);
>
> ... we end-up here. But xgene_msi_remove doesn't unregister the
> notifier...

Code to unregister the notifier is added in v7 patch.
>
> > +       return rc;
> > +}
> > +
> > +static struct platform_driver xgene_msi_driver = {
> > +       .driver = {
> > +               .name = "xgene-msi",
> > +               .owner = THIS_MODULE,
> > +               .of_match_table = xgene_msi_match_table,
> > +       },
> > +       .probe = xgene_msi_probe,
> > +       .remove = xgene_msi_remove,
> > +};
> > +
> > +static int __init xgene_pcie_msi_init(void)
> > +{
> > +       return platform_driver_register(&xgene_msi_driver);
> > +}
> > +subsys_initcall(xgene_pcie_msi_init);
> > +
> > diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> > index 22751ed..3e6faa1 100644
> > --- a/drivers/pci/host/pci-xgene.c
> > +++ b/drivers/pci/host/pci-xgene.c
> > @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
> >         return 0;
> >  }
> >
> > +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> > +{
> > +       struct device_node *msi_node;
> > +
> > +       msi_node = of_parse_phandle(bus->dev.of_node,
> > +                                       "msi-parent", 0);
> > +       if (!msi_node)
> > +               return -ENODEV;
> > +
> > +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> > +       if (bus->msi)
> > +               bus->msi->dev = &bus->dev;
> > +       else
> > +               return -ENODEV;
> > +       return 0;
> > +}
> > +
> >  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
> >  {
> >         struct device_node *dn = pdev->dev.of_node;
> > @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
> >         if (!bus)
> >                 return -ENOMEM;
> >
> > +       if (IS_ENABLED(CONFIG_PCI_MSI))
> > +               if (xgene_pcie_msi_enable(bus))
> > +                       dev_info(port->dev, "failed to enable MSI\n");
> > +
> >         pci_scan_child_bus(bus);
> >         pci_assign_unassigned_bus_resources(bus);
> >         pci_bus_add_devices(bus);
> > --
> > 1.9.1
> >
>
> In somehow related news, this is my last review on this code for
> quite some time. You've posted it 4 times in less than two weeks, right
> in the middle of the merge window, I've given it a lot of attention,
> and you've just run out of credits on the reviewing arcade game.
>
> I suggest you spend some quality time with it, polish it as much as you
> can. and post it again in about three weeks. Don't just make it work.
> Make it beautiful. Make it something I become madly in love with. By
> that time, I'll have forgotten about it and maybe I'll be swept off my
> feet when I come back from holiday.
>
> Thanks,
>
>         M.
> --
> Without deviation from the norm, progress is not possible.

Regards,
Duc Dang.

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

* Re: [PATCH v7 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-05-18  9:55                                                   ` Duc Dang
  (?)
@ 2015-05-20  9:16                                                     ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-05-20  9:16 UTC (permalink / raw)
  To: Duc Dang
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Mon, 18 May 2015 10:55:19 +0100
Duc Dang <dhdang@apm.com> wrote:

> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports.
> 
> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
> driver supports is reduced to 256.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |  10 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 550 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 582 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..0932118 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,21 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +       help
> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
> +         This MSI driver will provide MSI support for 5 PCIe ports of
> +         APM X-Gene v1 SoC
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..648bc8f
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,550 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi_group {
> +       struct xgene_msi                *msi;
> +       int                             gic_irq;
> +       u32                             msi_grp;
> +};
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       struct xgene_msi_group          *msi_groups;
> +       int                             num_cpus;
> +};
> +
> +/* Global data */
> +static struct xgene_msi xgene_msi_ctrl;
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> +       u32 group = data->hwirq % NR_HW_IRQS;
> +
> +       msg->address_hi = upper_32_bits(msi->msi_addr);
> +       msg->address_lo = lower_32_bits(msi->msi_addr) +
> +                         (((8 * group) + reg_set) << 16);

It would be safer to compute the offset from msi_addr, and apply
{upper,lower}_32_bits() to the resulting value. Otherwise, you end-up
with the wrong upper bits if you cross a 4GB boundary. I know this is
not the case here, but it doesn't hurt to be correct.

> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +
> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       /* Update MSI number to target the new CPU */
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> +
> +       return IRQ_SET_MASK_OK;
> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +       struct xgene_msi *msi = domain->host_data;
> +       int msi_irq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
> +                                            msi->num_cpus, 0);
> +       if (msi_irq < NR_MSI_VEC)
> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq,
> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> +                           handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +       u32 hwirq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> +                                           &msi_domain_ops, msi);
> +       if (!msi->domain)
> +               return -ENOMEM;
> +
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
> +                                       sizeof(struct xgene_msi_group),
> +                                       GFP_KERNEL);
> +       if (!xgene_msi->msi_groups)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> +{
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       struct xgene_msi_group *msi_groups;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int virq;
> +       int msir_index, msir_reg, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp;
> +
> +       chained_irq_enter(chip, desc);
> +
> +       msi_groups = irq_desc_get_handler_data(desc);
> +       xgene_msi = msi_groups->msi;
> +       msi_grp = msi_groups->msi_grp;
> +
> +       /*
> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> +        * If bit x of this register is set (x is 0..7), one or more interupts
> +        * corresponding to MSInIRx is set.
> +        */
> +       grp_select = readl_relaxed(xgene_msi->msi_regs +
> +                                  MSI_INT0 + (msi_grp << 16));
> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               /*
> +                * Calculate MSInIRx address to read to check for interrupts
> +                * (refer to termination address and data assignment
> +                * described in xgene_compose_msi_msg function)
> +                */
> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> +               msir_val = readl_relaxed(xgene_msi->msi_regs +
> +                                        MSI_IR0 + msir_reg);
> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       /*
> +                        * Calculate MSI vector number (refer to the termination
> +                        * address and data assignment described in
> +                        * xgene_compose_msi_msg function)
> +                        */
> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> +                                NR_HW_IRQS) + msi_grp;
> +                       /*
> +                        * As we have multiple hw_irq that maps to single MSI,
> +                        * always look up the virq using the hw_irq as seen from
> +                        * CPU0
> +                        */
> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> +                       WARN_ON(!virq);
> +                       if (virq != 0)
> +                               generic_handle_irq(virq);
> +                       msir_val &= ~(1 << intr_index);
> +               }
> +               grp_select &= ~(1 << msir_index);
> +       }

I wonder if you wouldn't be better off resampling MSIINTn here, just in
case something else has been made pending in the meantime.

> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               virq = msi->msi_groups[i].gic_irq;
> +               if (virq != 0) {
> +                       irq_set_chained_handler(virq, NULL);
> +                       irq_set_handler_data(virq, NULL);
> +               }
> +       }
> +       kfree(msi->msi_groups);
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);
> +
> +       return 0;
> +}
> +
> +static int xgene_msi_hwirq_alloc(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       struct xgene_msi_group *msi_group;
> +       cpumask_var_t mask;
> +       int i;
> +       int err;
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               if (i % msi->num_cpus != cpu)
> +                       continue;

Oh please...

	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {

does the same thing.

> +
> +               msi_group = &msi->msi_groups[i];
> +               if (!msi_group->gic_irq)
> +                       continue;
> +
> +               irq_set_chained_handler(msi_group->gic_irq,
> +                                       xgene_msi_isr);
> +               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
> +               if (err) {
> +                       pr_err("failed to register GIC IRQ handler\n");
> +                       return -EINVAL;
> +               }
> +               /*
> +                * Statically allocate MSI GIC IRQs to each CPU core
> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> +                * to each core.
> +                */
> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +                       cpumask_clear(mask);
> +                       cpumask_set_cpu(cpu, mask);
> +                       err = irq_set_affinity(msi_group->gic_irq, mask);
> +                       if (err) {
> +                               irq_set_chained_handler(msi_group->gic_irq,
> +                                                       NULL);
> +                               irq_set_handler_data(msi_group->gic_irq, NULL);
> +                               free_cpumask_var(mask);
> +                               pr_err("failed to set affinity for GIC IRQ");
> +                               return -EINVAL;
> +                       }
> +                       free_cpumask_var(mask);
> +               }

And what happens if alloc_cpumask_var fails?

> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_hwirq_free(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       struct xgene_msi_group *msi_group;
> +       int i;
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               if (i % msi->num_cpus != cpu)
> +                       continue;

Same remark as above.

> +
> +               msi_group = &msi->msi_groups[i];
> +               if (!msi_group->gic_irq)
> +                       continue;
> +
> +               irq_set_chained_handler(msi_group->gic_irq, NULL);
> +               irq_set_handler_data(msi_group->gic_irq, NULL);
> +       }
> +}
> +
> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
> +                                 unsigned long action, void *hcpu)
> +{
> +       unsigned cpu = (unsigned long)hcpu;
> +
> +       switch (action) {
> +       case CPU_ONLINE:
> +       case CPU_ONLINE_FROZEN:
> +               xgene_msi_hwirq_alloc(cpu);
> +               break;
> +       case CPU_DEAD:
> +       case CPU_DEAD_FROZEN:
> +               xgene_msi_hwirq_free(cpu);
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       return NOTIFY_OK;
> +}
> +
> +static struct notifier_block xgene_msi_cpu_notifier = {
> +       .notifier_call = xgene_msi_cpu_callback,
> +};
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi"},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int cpu;
> +       int virt_msir;
> +       u32 msi_reg, msi_val, msi_idx;
> +
> +       xgene_msi = &xgene_msi_ctrl;
> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +       xgene_msi->msi_addr = res->start;
> +
> +       xgene_msi->num_cpus = num_possible_cpus();
> +
> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               virt_msir = platform_get_irq(pdev, irq_index);
> +               if (virt_msir < 0) {
> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                               irq_index);
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
> +               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
> +               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
> +       }
> +
> +       /*
> +        * MSInIRx registers are read-to-clear, before registering interrupt
> +        * handlers, reading all of them to clear all spurious interrupts
> +        * that may occur before the driver is probed.
> +        */
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++) {
> +                       msi_reg = (irq_index << 19) + (msi_idx << 16);
> +                       msi_val = readl(xgene_msi->msi_regs +
> +                                        MSI_IR0 + msi_reg);

Feels like the above two line could be turned into a small helper (you
have the same in your handling code).

> +               }
> +               /* Read MSIINTn to confirm */
> +               msi_val = readl(xgene_msi->msi_regs +
> +                               MSI_INT0 + (irq_index << 16));
> +               if (msi_val) {
> +                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +       }
> +
> +       cpu_notifier_register_begin();
> +
> +       for_each_online_cpu(cpu)
> +               if (xgene_msi_hwirq_alloc(cpu)) {
> +                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
> +                       cpu_notifier_register_done();
> +                       goto error;
> +               }
> +
> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
> +               cpu_notifier_register_done();
> +               goto error;
> +       }
> +
> +       cpu_notifier_register_done();
> +
> +       xgene_msi->mchip.of_node = pdev->dev.of_node;
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error_notifier;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +
> +error_notifier:
> +       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +error:
> +       xgene_msi_remove(pdev);
> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

This is finally starting to look better (that, or my pink-tainted
holiday sunglasses are keeping me in a good mood). Please fix the few
nits above, and hopefully this can make it into 4.2 (unless someone
else spots something nasty here).

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny.

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

* Re: [PATCH v7 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-20  9:16                                                     ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-05-20  9:16 UTC (permalink / raw)
  To: Duc Dang
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Mon, 18 May 2015 10:55:19 +0100
Duc Dang <dhdang@apm.com> wrote:

> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports.
> 
> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
> driver supports is reduced to 256.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |  10 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 550 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 582 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..0932118 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,21 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +       help
> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
> +         This MSI driver will provide MSI support for 5 PCIe ports of
> +         APM X-Gene v1 SoC
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..648bc8f
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,550 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi_group {
> +       struct xgene_msi                *msi;
> +       int                             gic_irq;
> +       u32                             msi_grp;
> +};
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       struct xgene_msi_group          *msi_groups;
> +       int                             num_cpus;
> +};
> +
> +/* Global data */
> +static struct xgene_msi xgene_msi_ctrl;
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> +       u32 group = data->hwirq % NR_HW_IRQS;
> +
> +       msg->address_hi = upper_32_bits(msi->msi_addr);
> +       msg->address_lo = lower_32_bits(msi->msi_addr) +
> +                         (((8 * group) + reg_set) << 16);

It would be safer to compute the offset from msi_addr, and apply
{upper,lower}_32_bits() to the resulting value. Otherwise, you end-up
with the wrong upper bits if you cross a 4GB boundary. I know this is
not the case here, but it doesn't hurt to be correct.

> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +
> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       /* Update MSI number to target the new CPU */
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> +
> +       return IRQ_SET_MASK_OK;
> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +       struct xgene_msi *msi = domain->host_data;
> +       int msi_irq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
> +                                            msi->num_cpus, 0);
> +       if (msi_irq < NR_MSI_VEC)
> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq,
> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> +                           handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +       u32 hwirq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> +                                           &msi_domain_ops, msi);
> +       if (!msi->domain)
> +               return -ENOMEM;
> +
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
> +                                       sizeof(struct xgene_msi_group),
> +                                       GFP_KERNEL);
> +       if (!xgene_msi->msi_groups)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> +{
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       struct xgene_msi_group *msi_groups;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int virq;
> +       int msir_index, msir_reg, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp;
> +
> +       chained_irq_enter(chip, desc);
> +
> +       msi_groups = irq_desc_get_handler_data(desc);
> +       xgene_msi = msi_groups->msi;
> +       msi_grp = msi_groups->msi_grp;
> +
> +       /*
> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> +        * If bit x of this register is set (x is 0..7), one or more interupts
> +        * corresponding to MSInIRx is set.
> +        */
> +       grp_select = readl_relaxed(xgene_msi->msi_regs +
> +                                  MSI_INT0 + (msi_grp << 16));
> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               /*
> +                * Calculate MSInIRx address to read to check for interrupts
> +                * (refer to termination address and data assignment
> +                * described in xgene_compose_msi_msg function)
> +                */
> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> +               msir_val = readl_relaxed(xgene_msi->msi_regs +
> +                                        MSI_IR0 + msir_reg);
> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       /*
> +                        * Calculate MSI vector number (refer to the termination
> +                        * address and data assignment described in
> +                        * xgene_compose_msi_msg function)
> +                        */
> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> +                                NR_HW_IRQS) + msi_grp;
> +                       /*
> +                        * As we have multiple hw_irq that maps to single MSI,
> +                        * always look up the virq using the hw_irq as seen from
> +                        * CPU0
> +                        */
> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> +                       WARN_ON(!virq);
> +                       if (virq != 0)
> +                               generic_handle_irq(virq);
> +                       msir_val &= ~(1 << intr_index);
> +               }
> +               grp_select &= ~(1 << msir_index);
> +       }

I wonder if you wouldn't be better off resampling MSIINTn here, just in
case something else has been made pending in the meantime.

> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               virq = msi->msi_groups[i].gic_irq;
> +               if (virq != 0) {
> +                       irq_set_chained_handler(virq, NULL);
> +                       irq_set_handler_data(virq, NULL);
> +               }
> +       }
> +       kfree(msi->msi_groups);
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);
> +
> +       return 0;
> +}
> +
> +static int xgene_msi_hwirq_alloc(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       struct xgene_msi_group *msi_group;
> +       cpumask_var_t mask;
> +       int i;
> +       int err;
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               if (i % msi->num_cpus != cpu)
> +                       continue;

Oh please...

	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {

does the same thing.

> +
> +               msi_group = &msi->msi_groups[i];
> +               if (!msi_group->gic_irq)
> +                       continue;
> +
> +               irq_set_chained_handler(msi_group->gic_irq,
> +                                       xgene_msi_isr);
> +               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
> +               if (err) {
> +                       pr_err("failed to register GIC IRQ handler\n");
> +                       return -EINVAL;
> +               }
> +               /*
> +                * Statically allocate MSI GIC IRQs to each CPU core
> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> +                * to each core.
> +                */
> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +                       cpumask_clear(mask);
> +                       cpumask_set_cpu(cpu, mask);
> +                       err = irq_set_affinity(msi_group->gic_irq, mask);
> +                       if (err) {
> +                               irq_set_chained_handler(msi_group->gic_irq,
> +                                                       NULL);
> +                               irq_set_handler_data(msi_group->gic_irq, NULL);
> +                               free_cpumask_var(mask);
> +                               pr_err("failed to set affinity for GIC IRQ");
> +                               return -EINVAL;
> +                       }
> +                       free_cpumask_var(mask);
> +               }

And what happens if alloc_cpumask_var fails?

> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_hwirq_free(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       struct xgene_msi_group *msi_group;
> +       int i;
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               if (i % msi->num_cpus != cpu)
> +                       continue;

Same remark as above.

> +
> +               msi_group = &msi->msi_groups[i];
> +               if (!msi_group->gic_irq)
> +                       continue;
> +
> +               irq_set_chained_handler(msi_group->gic_irq, NULL);
> +               irq_set_handler_data(msi_group->gic_irq, NULL);
> +       }
> +}
> +
> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
> +                                 unsigned long action, void *hcpu)
> +{
> +       unsigned cpu = (unsigned long)hcpu;
> +
> +       switch (action) {
> +       case CPU_ONLINE:
> +       case CPU_ONLINE_FROZEN:
> +               xgene_msi_hwirq_alloc(cpu);
> +               break;
> +       case CPU_DEAD:
> +       case CPU_DEAD_FROZEN:
> +               xgene_msi_hwirq_free(cpu);
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       return NOTIFY_OK;
> +}
> +
> +static struct notifier_block xgene_msi_cpu_notifier = {
> +       .notifier_call = xgene_msi_cpu_callback,
> +};
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi"},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int cpu;
> +       int virt_msir;
> +       u32 msi_reg, msi_val, msi_idx;
> +
> +       xgene_msi = &xgene_msi_ctrl;
> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +       xgene_msi->msi_addr = res->start;
> +
> +       xgene_msi->num_cpus = num_possible_cpus();
> +
> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               virt_msir = platform_get_irq(pdev, irq_index);
> +               if (virt_msir < 0) {
> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                               irq_index);
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
> +               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
> +               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
> +       }
> +
> +       /*
> +        * MSInIRx registers are read-to-clear, before registering interrupt
> +        * handlers, reading all of them to clear all spurious interrupts
> +        * that may occur before the driver is probed.
> +        */
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++) {
> +                       msi_reg = (irq_index << 19) + (msi_idx << 16);
> +                       msi_val = readl(xgene_msi->msi_regs +
> +                                        MSI_IR0 + msi_reg);

Feels like the above two line could be turned into a small helper (you
have the same in your handling code).

> +               }
> +               /* Read MSIINTn to confirm */
> +               msi_val = readl(xgene_msi->msi_regs +
> +                               MSI_INT0 + (irq_index << 16));
> +               if (msi_val) {
> +                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +       }
> +
> +       cpu_notifier_register_begin();
> +
> +       for_each_online_cpu(cpu)
> +               if (xgene_msi_hwirq_alloc(cpu)) {
> +                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
> +                       cpu_notifier_register_done();
> +                       goto error;
> +               }
> +
> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
> +               cpu_notifier_register_done();
> +               goto error;
> +       }
> +
> +       cpu_notifier_register_done();
> +
> +       xgene_msi->mchip.of_node = pdev->dev.of_node;
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error_notifier;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +
> +error_notifier:
> +       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +error:
> +       xgene_msi_remove(pdev);
> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

This is finally starting to look better (that, or my pink-tainted
holiday sunglasses are keeping me in a good mood). Please fix the few
nits above, and hopefully this can make it into 4.2 (unless someone
else spots something nasty here).

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny.

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

* [PATCH v7 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-20  9:16                                                     ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-05-20  9:16 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, 18 May 2015 10:55:19 +0100
Duc Dang <dhdang@apm.com> wrote:

> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports.
> 
> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
> driver supports is reduced to 256.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |  10 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 550 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 582 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..0932118 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,21 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +       help
> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
> +         This MSI driver will provide MSI support for 5 PCIe ports of
> +         APM X-Gene v1 SoC
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..648bc8f
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,550 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi_group {
> +       struct xgene_msi                *msi;
> +       int                             gic_irq;
> +       u32                             msi_grp;
> +};
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       struct xgene_msi_group          *msi_groups;
> +       int                             num_cpus;
> +};
> +
> +/* Global data */
> +static struct xgene_msi xgene_msi_ctrl;
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
> +       u32 group = data->hwirq % NR_HW_IRQS;
> +
> +       msg->address_hi = upper_32_bits(msi->msi_addr);
> +       msg->address_lo = lower_32_bits(msi->msi_addr) +
> +                         (((8 * group) + reg_set) << 16);

It would be safer to compute the offset from msi_addr, and apply
{upper,lower}_32_bits() to the resulting value. Otherwise, you end-up
with the wrong upper bits if you cross a 4GB boundary. I know this is
not the case here, but it doesn't hurt to be correct.

> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +
> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       /* Update MSI number to target the new CPU */
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> +
> +       return IRQ_SET_MASK_OK;
> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +       struct xgene_msi *msi = domain->host_data;
> +       int msi_irq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
> +                                            msi->num_cpus, 0);
> +       if (msi_irq < NR_MSI_VEC)
> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq,
> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> +                           handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +       u32 hwirq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> +                                           &msi_domain_ops, msi);
> +       if (!msi->domain)
> +               return -ENOMEM;
> +
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
> +                                       sizeof(struct xgene_msi_group),
> +                                       GFP_KERNEL);
> +       if (!xgene_msi->msi_groups)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> +{
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       struct xgene_msi_group *msi_groups;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int virq;
> +       int msir_index, msir_reg, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp;
> +
> +       chained_irq_enter(chip, desc);
> +
> +       msi_groups = irq_desc_get_handler_data(desc);
> +       xgene_msi = msi_groups->msi;
> +       msi_grp = msi_groups->msi_grp;
> +
> +       /*
> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> +        * If bit x of this register is set (x is 0..7), one or more interupts
> +        * corresponding to MSInIRx is set.
> +        */
> +       grp_select = readl_relaxed(xgene_msi->msi_regs +
> +                                  MSI_INT0 + (msi_grp << 16));
> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               /*
> +                * Calculate MSInIRx address to read to check for interrupts
> +                * (refer to termination address and data assignment
> +                * described in xgene_compose_msi_msg function)
> +                */
> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
> +               msir_val = readl_relaxed(xgene_msi->msi_regs +
> +                                        MSI_IR0 + msir_reg);
> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       /*
> +                        * Calculate MSI vector number (refer to the termination
> +                        * address and data assignment described in
> +                        * xgene_compose_msi_msg function)
> +                        */
> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> +                                NR_HW_IRQS) + msi_grp;
> +                       /*
> +                        * As we have multiple hw_irq that maps to single MSI,
> +                        * always look up the virq using the hw_irq as seen from
> +                        * CPU0
> +                        */
> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> +                       WARN_ON(!virq);
> +                       if (virq != 0)
> +                               generic_handle_irq(virq);
> +                       msir_val &= ~(1 << intr_index);
> +               }
> +               grp_select &= ~(1 << msir_index);
> +       }

I wonder if you wouldn't be better off resampling MSIINTn here, just in
case something else has been made pending in the meantime.

> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               virq = msi->msi_groups[i].gic_irq;
> +               if (virq != 0) {
> +                       irq_set_chained_handler(virq, NULL);
> +                       irq_set_handler_data(virq, NULL);
> +               }
> +       }
> +       kfree(msi->msi_groups);
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);
> +
> +       return 0;
> +}
> +
> +static int xgene_msi_hwirq_alloc(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       struct xgene_msi_group *msi_group;
> +       cpumask_var_t mask;
> +       int i;
> +       int err;
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               if (i % msi->num_cpus != cpu)
> +                       continue;

Oh please...

	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {

does the same thing.

> +
> +               msi_group = &msi->msi_groups[i];
> +               if (!msi_group->gic_irq)
> +                       continue;
> +
> +               irq_set_chained_handler(msi_group->gic_irq,
> +                                       xgene_msi_isr);
> +               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
> +               if (err) {
> +                       pr_err("failed to register GIC IRQ handler\n");
> +                       return -EINVAL;
> +               }
> +               /*
> +                * Statically allocate MSI GIC IRQs to each CPU core
> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> +                * to each core.
> +                */
> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +                       cpumask_clear(mask);
> +                       cpumask_set_cpu(cpu, mask);
> +                       err = irq_set_affinity(msi_group->gic_irq, mask);
> +                       if (err) {
> +                               irq_set_chained_handler(msi_group->gic_irq,
> +                                                       NULL);
> +                               irq_set_handler_data(msi_group->gic_irq, NULL);
> +                               free_cpumask_var(mask);
> +                               pr_err("failed to set affinity for GIC IRQ");
> +                               return -EINVAL;
> +                       }
> +                       free_cpumask_var(mask);
> +               }

And what happens if alloc_cpumask_var fails?

> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_hwirq_free(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       struct xgene_msi_group *msi_group;
> +       int i;
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               if (i % msi->num_cpus != cpu)
> +                       continue;

Same remark as above.

> +
> +               msi_group = &msi->msi_groups[i];
> +               if (!msi_group->gic_irq)
> +                       continue;
> +
> +               irq_set_chained_handler(msi_group->gic_irq, NULL);
> +               irq_set_handler_data(msi_group->gic_irq, NULL);
> +       }
> +}
> +
> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
> +                                 unsigned long action, void *hcpu)
> +{
> +       unsigned cpu = (unsigned long)hcpu;
> +
> +       switch (action) {
> +       case CPU_ONLINE:
> +       case CPU_ONLINE_FROZEN:
> +               xgene_msi_hwirq_alloc(cpu);
> +               break;
> +       case CPU_DEAD:
> +       case CPU_DEAD_FROZEN:
> +               xgene_msi_hwirq_free(cpu);
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       return NOTIFY_OK;
> +}
> +
> +static struct notifier_block xgene_msi_cpu_notifier = {
> +       .notifier_call = xgene_msi_cpu_callback,
> +};
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi"},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int cpu;
> +       int virt_msir;
> +       u32 msi_reg, msi_val, msi_idx;
> +
> +       xgene_msi = &xgene_msi_ctrl;
> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +       xgene_msi->msi_addr = res->start;
> +
> +       xgene_msi->num_cpus = num_possible_cpus();
> +
> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               virt_msir = platform_get_irq(pdev, irq_index);
> +               if (virt_msir < 0) {
> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                               irq_index);
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
> +               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
> +               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
> +       }
> +
> +       /*
> +        * MSInIRx registers are read-to-clear, before registering interrupt
> +        * handlers, reading all of them to clear all spurious interrupts
> +        * that may occur before the driver is probed.
> +        */
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++) {
> +                       msi_reg = (irq_index << 19) + (msi_idx << 16);
> +                       msi_val = readl(xgene_msi->msi_regs +
> +                                        MSI_IR0 + msi_reg);

Feels like the above two line could be turned into a small helper (you
have the same in your handling code).

> +               }
> +               /* Read MSIINTn to confirm */
> +               msi_val = readl(xgene_msi->msi_regs +
> +                               MSI_INT0 + (irq_index << 16));
> +               if (msi_val) {
> +                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +       }
> +
> +       cpu_notifier_register_begin();
> +
> +       for_each_online_cpu(cpu)
> +               if (xgene_msi_hwirq_alloc(cpu)) {
> +                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
> +                       cpu_notifier_register_done();
> +                       goto error;
> +               }
> +
> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
> +               cpu_notifier_register_done();
> +               goto error;
> +       }
> +
> +       cpu_notifier_register_done();
> +
> +       xgene_msi->mchip.of_node = pdev->dev.of_node;
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error_notifier;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +
> +error_notifier:
> +       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +error:
> +       xgene_msi_remove(pdev);
> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

This is finally starting to look better (that, or my pink-tainted
holiday sunglasses are keeping me in a good mood). Please fix the few
nits above, and hopefully this can make it into 4.2 (unless someone
else spots something nasty here).

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny.

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

* [PATCH v8 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-05-20  9:16                                                     ` Marc Zyngier
@ 2015-05-22 18:41                                                       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-22 18:41 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI
vectors this driver supports is reduced to 256 to maintain the correct set_affinity
behavior for each MSI.

v8 changes:
	1. Add helper to read MSI registers
	2. Resample interrupt status of the same group after handling interrupts
	   in that group
	3. Error handling in case fail to allocate CPU mask for affinity setting
	4. Calculate MSI message address_hi in safer way

v7 changes:
        1. Add more error handling cases
        2. Clear spurious interrupts that may happen during driver probe
        3. Not using free_irq for chained irqs
        4. Improve GIC IRQ number look up in chained handler

v6 changes:
        1. Correctly allocate MSI with bitmap_find_next_zero_area
        2. Add notifier for CPU hotplug case
        3. Driver clean up
        4. Add more detailed information into device tree binding document
           and move the device tree binding patch before the driver/dts patch

v5 changes:
        1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs
        for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer
        them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1
        supports is reduced to 256.

v4 changes:
        1. Remove affinity setting for each MSI
        2. Add description about register layout, MSI termination address and data
        3. Correct total number of MSI vectors to 2048
        4. Clean up error messages
        5. Remove unused module code

v3 changes:
        1. Implement MSI support using PCI MSI IRQ domain
        2. Only use msi_controller to store IRQ domain

v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
 drivers/pci/host/Kconfig                           |  10 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 573 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 708 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1


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

* [PATCH v8 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-05-22 18:41                                                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-22 18:41 UTC (permalink / raw)
  To: linux-arm-kernel

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI
vectors this driver supports is reduced to 256 to maintain the correct set_affinity
behavior for each MSI.

v8 changes:
	1. Add helper to read MSI registers
	2. Resample interrupt status of the same group after handling interrupts
	   in that group
	3. Error handling in case fail to allocate CPU mask for affinity setting
	4. Calculate MSI message address_hi in safer way

v7 changes:
        1. Add more error handling cases
        2. Clear spurious interrupts that may happen during driver probe
        3. Not using free_irq for chained irqs
        4. Improve GIC IRQ number look up in chained handler

v6 changes:
        1. Correctly allocate MSI with bitmap_find_next_zero_area
        2. Add notifier for CPU hotplug case
        3. Driver clean up
        4. Add more detailed information into device tree binding document
           and move the device tree binding patch before the driver/dts patch

v5 changes:
        1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs
        for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer
        them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1
        supports is reduced to 256.

v4 changes:
        1. Remove affinity setting for each MSI
        2. Add description about register layout, MSI termination address and data
        3. Correct total number of MSI vectors to 2048
        4. Clean up error messages
        5. Remove unused module code

v3 changes:
        1. Implement MSI support using PCI MSI IRQ domain
        2. Only use msi_controller to store IRQ domain

v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
 drivers/pci/host/Kconfig                           |  10 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 573 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 708 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1

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

* [PATCH v8 1/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
  2015-05-22 18:41                                                       ` Duc Dang
@ 2015-05-22 18:41                                                       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-22 18:41 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

X-Gene v1 PCIe MSI controller block provides PCIE MSI functionality for 5 X-Gene v1
PCIE ports

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..36d881c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+	      X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+	      interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi@79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = 	<0x0 0x10 0x4>
+				<0x0 0x11 0x4>
+				<0x0 0x12 0x4>
+				<0x0 0x13 0x4>
+				<0x0 0x14 0x4>
+				<0x0 0x15 0x4>
+				<0x0 0x16 0x4>
+				<0x0 0x17 0x4>
+				<0x0 0x18 0x4>
+				<0x0 0x19 0x4>
+				<0x0 0x1a 0x4>
+				<0x0 0x1b 0x4>
+				<0x0 0x1c 0x4>
+				<0x0 0x1d 0x4>
+				<0x0 0x1e 0x4>
+				<0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie@1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1


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

* [PATCH v8 1/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
@ 2015-05-22 18:41                                                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-22 18:41 UTC (permalink / raw)
  To: linux-arm-kernel

X-Gene v1 PCIe MSI controller block provides PCIE MSI functionality for 5 X-Gene v1
PCIE ports

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..36d881c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+	      X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+	      interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = 	<0x0 0x10 0x4>
+				<0x0 0x11 0x4>
+				<0x0 0x12 0x4>
+				<0x0 0x13 0x4>
+				<0x0 0x14 0x4>
+				<0x0 0x15 0x4>
+				<0x0 0x16 0x4>
+				<0x0 0x17 0x4>
+				<0x0 0x18 0x4>
+				<0x0 0x19 0x4>
+				<0x0 0x1a 0x4>
+				<0x0 0x1b 0x4>
+				<0x0 0x1c 0x4>
+				<0x0 0x1d 0x4>
+				<0x0 0x1e 0x4>
+				<0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1

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

* [PATCH v8 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-05-22 18:41                                                       ` Duc Dang
@ 2015-05-22 18:41                                                       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-22 18:41 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
is moved around these HW IRQs lines. With this approach, the total MSI vectors this
driver supports is reduced to 256.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 drivers/pci/host/Kconfig         |  10 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 573 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 605 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9..0932118 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,21 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+	help
+	  Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
+	  This MSI driver will provide MSI support for 5 PCIe ports of
+	  APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c..f39bde3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..0c6823d
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,573 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0			0x000000
+#define MSI_INT0		0x800000
+#define IDX_PER_GROUP		8
+#define IRQS_PER_IDX		16
+#define NR_HW_IRQS		16
+#define NR_MSI_VEC		(IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi_group {
+	struct xgene_msi		*msi;
+	int				gic_irq;
+	u32				msi_grp;
+};
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	u64				msi_addr;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	struct xgene_msi_group		*msi_groups;
+	int				num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0			base_addr
+ * MSI0IR1			base_addr +  0x10000
+ * ...				...
+ * MSI0IR6			base_addr +  0x60000
+ * MSI0IR7			base_addr +  0x70000
+ * MSI1IR0			base_addr +  0x80000
+ * MSI1IR1			base_addr +  0x90000
+ * ...				...
+ * MSI1IR7			base_addr +  0xF0000
+ * MSI2IR0			base_addr + 0x100000
+ * ...				...
+ * MSIFIR0			base_addr + 0x780000
+ * MSIFIR1			base_addr + 0x790000
+ * ...				...
+ * MSIFIR7			base_addr + 0x7F0000
+ * MSIINT0			base_addr + 0x800000
+ * MSIINT1			base_addr + 0x810000
+ * ...				...
+ * MSIINTF			base_addr + 0x8F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
+ * the MSI pending status caused by 1 of its 8 index registers.
+ */
+
+/* MSInIRx read helper */
+static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
+				    u32 msi_grp, u32 msir_idx)
+{
+	return readl_relaxed(msi->msi_regs + MSI_IR0 +
+			      (msi_grp << 19) + (msir_idx << 16));
+}
+
+/* MSIINTn read helper */
+static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
+{
+	return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+}
+
+/* With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *		Group 0: 0-255
+ *		Group 1: 256-511
+ *		Group 2: 512-767
+ *		...
+ *		Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *	As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *		Group 0: 0-15
+ *		Group 1: 16-32
+ *		...
+ *		Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+	u32 group = data->hwirq % NR_HW_IRQS;
+
+	msg->address_hi = upper_32_bits(msi->msi_addr +
+					(((8 * group) + reg_set) << 16));
+	msg->address_lo = lower_32_bits(msi->msi_addr +
+					(((8 * group) + reg_set) << 16));
+	msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+				  const struct cpumask *mask, bool force)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+	int target_cpu = cpumask_first(mask);
+	int curr_cpu;
+
+	curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
+	if (curr_cpu == target_cpu)
+		return IRQ_SET_MASK_OK_DONE;
+
+	/* Update MSI number to target the new CPU */
+	irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+
+	return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+	struct xgene_msi *msi = domain->host_data;
+	int msi_irq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+					     msi->num_cpus, 0);
+	if (msi_irq < NR_MSI_VEC)
+		bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+	else
+		msi_irq = -ENOSPC;
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq,
+			    &xgene_msi_bottom_irq_chip, domain->host_data,
+			    handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+	u32 hwirq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
+	bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+					    &msi_domain_ops, msi);
+	if (!msi->domain)
+		return -ENOMEM;
+
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
+					sizeof(struct xgene_msi_group),
+					GFP_KERNEL);
+	if (!xgene_msi->msi_groups)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct xgene_msi_group *msi_groups;
+	struct xgene_msi *xgene_msi;
+	unsigned int virq;
+	int msir_index, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp;
+
+	chained_irq_enter(chip, desc);
+
+	msi_groups = irq_desc_get_handler_data(desc);
+	xgene_msi = msi_groups->msi;
+	msi_grp = msi_groups->msi_grp;
+
+	/*
+	 * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+	 * If bit x of this register is set (x is 0..7), one or more interupts
+	 * corresponding to MSInIRx is set.
+	 */
+	grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		/*
+		 * Calculate MSInIRx address to read to check for interrupts
+		 * (refer to termination address and data assignment
+		 * described in xgene_compose_msi_msg function)
+		 */
+		msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			/*
+			 * Calculate MSI vector number (refer to the termination
+			 * address and data assignment described in
+			 * xgene_compose_msi_msg function)
+			 */
+			hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+				 NR_HW_IRQS) + msi_grp;
+			/*
+			 * As we have multiple hw_irq that maps to single MSI,
+			 * always look up the virq using the hw_irq as seen from
+			 * CPU0
+			 */
+			hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			WARN_ON(!virq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+		}
+		grp_select &= ~(1 << msir_index);
+
+		if (!grp_select) {
+			/*
+			 * We handled all interrupts happened in this group,
+			 * resample this group MSI_INTx register in case
+			 * something else has been made pending in the meantime
+			 */
+			grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+		}
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		virq = msi->msi_groups[i].gic_irq;
+		if (virq != 0) {
+			irq_set_chained_handler(virq, NULL);
+			irq_set_handler_data(virq, NULL);
+		}
+	}
+	kfree(msi->msi_groups);
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static int xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	cpumask_var_t mask;
+	int i;
+	int err;
+
+	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq,
+					xgene_msi_isr);
+		err = irq_set_handler_data(msi_group->gic_irq, msi_group);
+		if (err) {
+			pr_err("failed to register GIC IRQ handler\n");
+			return -EINVAL;
+		}
+		/*
+		 * Statically allocate MSI GIC IRQs to each CPU core
+		 * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+		 * to each core.
+		 */
+		if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+			cpumask_clear(mask);
+			cpumask_set_cpu(cpu, mask);
+			err = irq_set_affinity(msi_group->gic_irq, mask);
+			if (err)
+				pr_err("failed to set affinity for GIC IRQ");
+			free_cpumask_var(mask);
+		} else {
+			pr_err("failed to alloc CPU mask for affinity\n");
+			err = -EINVAL;
+		}
+
+		if (err) {
+			irq_set_chained_handler(msi_group->gic_irq, NULL);
+			irq_set_handler_data(msi_group->gic_irq, NULL);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	int i;
+
+	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq, NULL);
+		irq_set_handler_data(msi_group->gic_irq, NULL);
+	}
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+				  unsigned long action, void *hcpu)
+{
+	unsigned cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		xgene_msi_hwirq_alloc(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		xgene_msi_hwirq_free(cpu);
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+	.notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi"},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct xgene_msi *xgene_msi;
+	unsigned int cpu;
+	int virt_msir;
+	u32 msi_val, msi_idx;
+
+	xgene_msi = &xgene_msi_ctrl;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	xgene_msi->msi_addr = res->start;
+
+	xgene_msi->num_cpus = num_possible_cpus();
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		virt_msir = platform_get_irq(pdev, irq_index);
+		if (virt_msir < 0) {
+			dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+				irq_index);
+			rc = -EINVAL;
+			goto error;
+		}
+		xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
+		xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
+		xgene_msi->msi_groups[irq_index].msi = xgene_msi;
+	}
+
+	/*
+	 * MSInIRx registers are read-to-clear, before registering interrupt
+	 * handlers, reading all of them to clear all spurious interrupts
+	 * that may occur before the driver is probed.
+	 */
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
+			msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
+						    msi_idx);
+		/* Read MSIINTn to confirm */
+		msi_val = xgene_msi_int_read(xgene_msi, irq_index);
+		if (msi_val) {
+			dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
+			rc = -EINVAL;
+			goto error;
+		}
+	}
+
+	cpu_notifier_register_begin();
+
+	for_each_online_cpu(cpu)
+		if (xgene_msi_hwirq_alloc(cpu)) {
+			dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
+			cpu_notifier_register_done();
+			goto error;
+		}
+
+	rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+		cpu_notifier_register_done();
+		goto error;
+	}
+
+	cpu_notifier_register_done();
+
+	xgene_msi->mchip.of_node = pdev->dev.of_node;
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error_notifier;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+
+error_notifier:
+	unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 22751ed..3e6faa1 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1


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

* [PATCH v8 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-22 18:41                                                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-22 18:41 UTC (permalink / raw)
  To: linux-arm-kernel

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
is moved around these HW IRQs lines. With this approach, the total MSI vectors this
driver supports is reduced to 256.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 drivers/pci/host/Kconfig         |  10 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 573 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 605 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9..0932118 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,21 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+	help
+	  Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
+	  This MSI driver will provide MSI support for 5 PCIe ports of
+	  APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c..f39bde3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..0c6823d
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,573 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0			0x000000
+#define MSI_INT0		0x800000
+#define IDX_PER_GROUP		8
+#define IRQS_PER_IDX		16
+#define NR_HW_IRQS		16
+#define NR_MSI_VEC		(IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi_group {
+	struct xgene_msi		*msi;
+	int				gic_irq;
+	u32				msi_grp;
+};
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	u64				msi_addr;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	struct xgene_msi_group		*msi_groups;
+	int				num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0			base_addr
+ * MSI0IR1			base_addr +  0x10000
+ * ...				...
+ * MSI0IR6			base_addr +  0x60000
+ * MSI0IR7			base_addr +  0x70000
+ * MSI1IR0			base_addr +  0x80000
+ * MSI1IR1			base_addr +  0x90000
+ * ...				...
+ * MSI1IR7			base_addr +  0xF0000
+ * MSI2IR0			base_addr + 0x100000
+ * ...				...
+ * MSIFIR0			base_addr + 0x780000
+ * MSIFIR1			base_addr + 0x790000
+ * ...				...
+ * MSIFIR7			base_addr + 0x7F0000
+ * MSIINT0			base_addr + 0x800000
+ * MSIINT1			base_addr + 0x810000
+ * ...				...
+ * MSIINTF			base_addr + 0x8F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
+ * the MSI pending status caused by 1 of its 8 index registers.
+ */
+
+/* MSInIRx read helper */
+static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
+				    u32 msi_grp, u32 msir_idx)
+{
+	return readl_relaxed(msi->msi_regs + MSI_IR0 +
+			      (msi_grp << 19) + (msir_idx << 16));
+}
+
+/* MSIINTn read helper */
+static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
+{
+	return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+}
+
+/* With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *		Group 0: 0-255
+ *		Group 1: 256-511
+ *		Group 2: 512-767
+ *		...
+ *		Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *	As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *		Group 0: 0-15
+ *		Group 1: 16-32
+ *		...
+ *		Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+	u32 group = data->hwirq % NR_HW_IRQS;
+
+	msg->address_hi = upper_32_bits(msi->msi_addr +
+					(((8 * group) + reg_set) << 16));
+	msg->address_lo = lower_32_bits(msi->msi_addr +
+					(((8 * group) + reg_set) << 16));
+	msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+				  const struct cpumask *mask, bool force)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+	int target_cpu = cpumask_first(mask);
+	int curr_cpu;
+
+	curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
+	if (curr_cpu == target_cpu)
+		return IRQ_SET_MASK_OK_DONE;
+
+	/* Update MSI number to target the new CPU */
+	irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+
+	return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+	struct xgene_msi *msi = domain->host_data;
+	int msi_irq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+					     msi->num_cpus, 0);
+	if (msi_irq < NR_MSI_VEC)
+		bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+	else
+		msi_irq = -ENOSPC;
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq,
+			    &xgene_msi_bottom_irq_chip, domain->host_data,
+			    handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+	u32 hwirq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
+	bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+					    &msi_domain_ops, msi);
+	if (!msi->domain)
+		return -ENOMEM;
+
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
+					sizeof(struct xgene_msi_group),
+					GFP_KERNEL);
+	if (!xgene_msi->msi_groups)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct xgene_msi_group *msi_groups;
+	struct xgene_msi *xgene_msi;
+	unsigned int virq;
+	int msir_index, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp;
+
+	chained_irq_enter(chip, desc);
+
+	msi_groups = irq_desc_get_handler_data(desc);
+	xgene_msi = msi_groups->msi;
+	msi_grp = msi_groups->msi_grp;
+
+	/*
+	 * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+	 * If bit x of this register is set (x is 0..7), one or more interupts
+	 * corresponding to MSInIRx is set.
+	 */
+	grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		/*
+		 * Calculate MSInIRx address to read to check for interrupts
+		 * (refer to termination address and data assignment
+		 * described in xgene_compose_msi_msg function)
+		 */
+		msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			/*
+			 * Calculate MSI vector number (refer to the termination
+			 * address and data assignment described in
+			 * xgene_compose_msi_msg function)
+			 */
+			hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+				 NR_HW_IRQS) + msi_grp;
+			/*
+			 * As we have multiple hw_irq that maps to single MSI,
+			 * always look up the virq using the hw_irq as seen from
+			 * CPU0
+			 */
+			hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			WARN_ON(!virq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+		}
+		grp_select &= ~(1 << msir_index);
+
+		if (!grp_select) {
+			/*
+			 * We handled all interrupts happened in this group,
+			 * resample this group MSI_INTx register in case
+			 * something else has been made pending in the meantime
+			 */
+			grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+		}
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		virq = msi->msi_groups[i].gic_irq;
+		if (virq != 0) {
+			irq_set_chained_handler(virq, NULL);
+			irq_set_handler_data(virq, NULL);
+		}
+	}
+	kfree(msi->msi_groups);
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static int xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	cpumask_var_t mask;
+	int i;
+	int err;
+
+	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq,
+					xgene_msi_isr);
+		err = irq_set_handler_data(msi_group->gic_irq, msi_group);
+		if (err) {
+			pr_err("failed to register GIC IRQ handler\n");
+			return -EINVAL;
+		}
+		/*
+		 * Statically allocate MSI GIC IRQs to each CPU core
+		 * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+		 * to each core.
+		 */
+		if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+			cpumask_clear(mask);
+			cpumask_set_cpu(cpu, mask);
+			err = irq_set_affinity(msi_group->gic_irq, mask);
+			if (err)
+				pr_err("failed to set affinity for GIC IRQ");
+			free_cpumask_var(mask);
+		} else {
+			pr_err("failed to alloc CPU mask for affinity\n");
+			err = -EINVAL;
+		}
+
+		if (err) {
+			irq_set_chained_handler(msi_group->gic_irq, NULL);
+			irq_set_handler_data(msi_group->gic_irq, NULL);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	int i;
+
+	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq, NULL);
+		irq_set_handler_data(msi_group->gic_irq, NULL);
+	}
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+				  unsigned long action, void *hcpu)
+{
+	unsigned cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		xgene_msi_hwirq_alloc(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		xgene_msi_hwirq_free(cpu);
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+	.notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi"},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct xgene_msi *xgene_msi;
+	unsigned int cpu;
+	int virt_msir;
+	u32 msi_val, msi_idx;
+
+	xgene_msi = &xgene_msi_ctrl;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	xgene_msi->msi_addr = res->start;
+
+	xgene_msi->num_cpus = num_possible_cpus();
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		virt_msir = platform_get_irq(pdev, irq_index);
+		if (virt_msir < 0) {
+			dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+				irq_index);
+			rc = -EINVAL;
+			goto error;
+		}
+		xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
+		xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
+		xgene_msi->msi_groups[irq_index].msi = xgene_msi;
+	}
+
+	/*
+	 * MSInIRx registers are read-to-clear, before registering interrupt
+	 * handlers, reading all of them to clear all spurious interrupts
+	 * that may occur before the driver is probed.
+	 */
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
+			msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
+						    msi_idx);
+		/* Read MSIINTn to confirm */
+		msi_val = xgene_msi_int_read(xgene_msi, irq_index);
+		if (msi_val) {
+			dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
+			rc = -EINVAL;
+			goto error;
+		}
+	}
+
+	cpu_notifier_register_begin();
+
+	for_each_online_cpu(cpu)
+		if (xgene_msi_hwirq_alloc(cpu)) {
+			dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
+			cpu_notifier_register_done();
+			goto error;
+		}
+
+	rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+		cpu_notifier_register_done();
+		goto error;
+	}
+
+	cpu_notifier_register_done();
+
+	xgene_msi->mchip.of_node = pdev->dev.of_node;
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error_notifier;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+
+error_notifier:
+	unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 22751ed..3e6faa1 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1

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

* [PATCH v8 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
  2015-05-22 18:41                                                       ` Duc Dang
@ 2015-05-22 18:41                                                       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-22 18:41 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..4b719c9 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi@79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie@1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie@1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie@1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie@1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie@1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial@1c020000 {
-- 
1.9.1


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

* [PATCH v8 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
@ 2015-05-22 18:41                                                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-22 18:41 UTC (permalink / raw)
  To: linux-arm-kernel

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index f1ad9c2..4b719c9 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
-- 
1.9.1

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

* [PATCH v8 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
  2015-05-22 18:41                                                       ` Duc Dang
@ 2015-05-22 18:41                                                       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-22 18:41 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddc5a8c..a1b119b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7490,6 +7490,14 @@ L:	linux-pci@vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci@vger.kernel.org
+L:	linux-arm-kernel@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia@lists.infradead.org
-- 
1.9.1


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

* [PATCH v8 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
@ 2015-05-22 18:41                                                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-22 18:41 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ddc5a8c..a1b119b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7490,6 +7490,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
-- 
1.9.1

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

* Re: [PATCH v7 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-05-20  9:16                                                     ` Marc Zyngier
  (?)
@ 2015-05-22 18:43                                                       ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-22 18:43 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Wed, May 20, 2015 at 2:16 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On Mon, 18 May 2015 10:55:19 +0100
> Duc Dang <dhdang@apm.com> wrote:
>
>> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
>> to GIC V2M specification for MSI Termination.
>>
>> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
>> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
>> and shared across all 5 PCIe ports.
>>
>> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
>> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
>> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
>> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
>> driver supports is reduced to 256.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  drivers/pci/host/Kconfig         |  10 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 550 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 582 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 7b892a9..0932118 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,21 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>> +       select PCI_XGENE_MSI if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +       help
>> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
>> +         This MSI driver will provide MSI support for 5 PCIe ports of
>> +         APM X-Gene v1 SoC
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index e61d91c..f39bde3 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..648bc8f
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,550 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/cpu.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_IR0                        0x000000
>> +#define MSI_INT0               0x800000
>> +#define IDX_PER_GROUP          8
>> +#define IRQS_PER_IDX           16
>> +#define NR_HW_IRQS             16
>> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
>> +
>> +struct xgene_msi_group {
>> +       struct xgene_msi                *msi;
>> +       int                             gic_irq;
>> +       u32                             msi_grp;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       u64                             msi_addr;
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       struct xgene_msi_group          *msi_groups;
>> +       int                             num_cpus;
>> +};
>> +
>> +/* Global data */
>> +static struct xgene_msi xgene_msi_ctrl;
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +/*
>> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
>> + * n is group number (0..F), x is index of registers in each group (0..7)
>> + * The registers layout is like following:
>> + * MSI0IR0                     base_addr
>> + * MSI0IR1                     base_addr +  0x10000
>> + * ...                         ...
>> + * MSI0IR6                     base_addr +  0x60000
>> + * MSI0IR7                     base_addr +  0x70000
>> + * MSI1IR0                     base_addr +  0x80000
>> + * MSI1IR1                     base_addr +  0x90000
>> + * ...                         ...
>> + * MSI1IR7                     base_addr +  0xF0000
>> + * MSI2IR0                     base_addr + 0x100000
>> + * ...                         ...
>> + * MSIFIR0                     base_addr + 0x780000
>> + * MSIFIR1                     base_addr + 0x790000
>> + * ...                         ...
>> + * MSIFIR7                     base_addr + 0x7F0000
>> + *
>> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
>> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
>> + * registers.
>> + *
>> + * With 2048 MSI vectors supported, the MSI message can be construct using
>> + * following scheme:
>> + * - Divide into 8 256-vector groups
>> + *             Group 0: 0-255
>> + *             Group 1: 256-511
>> + *             Group 2: 512-767
>> + *             ...
>> + *             Group 7: 1792-2047
>> + * - Each 256-vector group is divided into 16 16-vector groups
>> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
>> + *             Group 0: 0-15
>> + *             Group 1: 16-32
>> + *             ...
>> + *             Group 15: 240-255
>> + * - The termination address of MSI vector in 256-vector group n and 16-vector
>> + * group x is the address of MSIxIRn
>> + * - The data for MSI vector in 16-vector group x is x
>> + */
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
>> +       u32 group = data->hwirq % NR_HW_IRQS;
>> +
>> +       msg->address_hi = upper_32_bits(msi->msi_addr);
>> +       msg->address_lo = lower_32_bits(msi->msi_addr) +
>> +                         (((8 * group) + reg_set) << 16);
>
> It would be safer to compute the offset from msi_addr, and apply
> {upper,lower}_32_bits() to the resulting value. Otherwise, you end-up
> with the wrong upper bits if you cross a 4GB boundary. I know this is
> not the case here, but it doesn't hurt to be correct.
>
Yes, I changed this in v8 patch.

>> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
>> +}
>> +
>> +/*
>> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
>> + * To maintain the expected behaviour of .set_affinity for each MSI
>> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
>> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
>> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
>> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
>> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
>> + */
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
>> +       int target_cpu = cpumask_first(mask);
>> +       int curr_cpu;
>> +
>> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
>> +       if (curr_cpu == target_cpu)
>> +               return IRQ_SET_MASK_OK_DONE;
>> +
>> +       /* Update MSI number to target the new CPU */
>> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
>> +
>> +       return IRQ_SET_MASK_OK;
>> +}
>> +
>> +static struct irq_chip xgene_msi_bottom_irq_chip = {
>> +       .name                   = "MSI",
>> +       .irq_set_affinity       = xgene_msi_set_affinity,
>> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
>> +};
>> +
>> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
>> +                                 unsigned int nr_irqs, void *args)
>> +{
>> +       struct xgene_msi *msi = domain->host_data;
>> +       int msi_irq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
>> +                                            msi->num_cpus, 0);
>> +       if (msi_irq < NR_MSI_VEC)
>> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
>> +       else
>> +               msi_irq = -ENOSPC;
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       if (msi_irq < 0)
>> +               return msi_irq;
>> +
>> +       irq_domain_set_info(domain, virq, msi_irq,
>> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
>> +                           handle_simple_irq, NULL, NULL);
>> +       set_irq_flags(virq, IRQF_VALID);
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>> +                                 unsigned int virq, unsigned int nr_irqs)
>> +{
>> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>> +       u32 hwirq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
>> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops msi_domain_ops = {
>> +       .alloc  = xgene_irq_domain_alloc,
>> +       .free   = xgene_irq_domain_free,
>> +};
>> +
>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>> +{
>> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
>> +                                           &msi_domain_ops, msi);
>> +       if (!msi->domain)
>> +               return -ENOMEM;
>> +
>> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>> +                                                     &xgene_msi_domain_info,
>> +                                                     msi->domain);
>> +
>> +       if (!msi->mchip.domain) {
>> +               irq_domain_remove(msi->domain);
>> +               return -ENOMEM;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_free_domains(struct xgene_msi *msi)
>> +{
>> +       if (msi->mchip.domain)
>> +               irq_domain_remove(msi->mchip.domain);
>> +       if (msi->domain)
>> +               irq_domain_remove(msi->domain);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
>> +
>> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +       if (!xgene_msi->bitmap)
>> +               return -ENOMEM;
>> +
>> +       mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
>> +                                       sizeof(struct xgene_msi_group),
>> +                                       GFP_KERNEL);
>> +       if (!xgene_msi->msi_groups)
>> +               return -ENOMEM;
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
>> +{
>> +       struct irq_chip *chip = irq_desc_get_chip(desc);
>> +       struct xgene_msi_group *msi_groups;
>> +       struct xgene_msi *xgene_msi;
>> +       unsigned int virq;
>> +       int msir_index, msir_reg, msir_val, hw_irq;
>> +       u32 intr_index, grp_select, msi_grp;
>> +
>> +       chained_irq_enter(chip, desc);
>> +
>> +       msi_groups = irq_desc_get_handler_data(desc);
>> +       xgene_msi = msi_groups->msi;
>> +       msi_grp = msi_groups->msi_grp;
>> +
>> +       /*
>> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
>> +        * If bit x of this register is set (x is 0..7), one or more interupts
>> +        * corresponding to MSInIRx is set.
>> +        */
>> +       grp_select = readl_relaxed(xgene_msi->msi_regs +
>> +                                  MSI_INT0 + (msi_grp << 16));
>> +       while (grp_select) {
>> +               msir_index = ffs(grp_select) - 1;
>> +               /*
>> +                * Calculate MSInIRx address to read to check for interrupts
>> +                * (refer to termination address and data assignment
>> +                * described in xgene_compose_msi_msg function)
>> +                */
>> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
>> +               msir_val = readl_relaxed(xgene_msi->msi_regs +
>> +                                        MSI_IR0 + msir_reg);
>> +               while (msir_val) {
>> +                       intr_index = ffs(msir_val) - 1;
>> +                       /*
>> +                        * Calculate MSI vector number (refer to the termination
>> +                        * address and data assignment described in
>> +                        * xgene_compose_msi_msg function)
>> +                        */
>> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
>> +                                NR_HW_IRQS) + msi_grp;
>> +                       /*
>> +                        * As we have multiple hw_irq that maps to single MSI,
>> +                        * always look up the virq using the hw_irq as seen from
>> +                        * CPU0
>> +                        */
>> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
>> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> +                       WARN_ON(!virq);
>> +                       if (virq != 0)
>> +                               generic_handle_irq(virq);
>> +                       msir_val &= ~(1 << intr_index);
>> +               }
>> +               grp_select &= ~(1 << msir_index);
>> +       }
>
> I wonder if you wouldn't be better off resampling MSIINTn here, just in
> case something else has been made pending in the meantime.

I added code to read MSI_INTn of the same group again when grp_select
becomes 0 (when we handle all the interrupts).

>
>> +
>> +       chained_irq_exit(chip, desc);
>> +}
>> +
>> +static int xgene_msi_remove(struct platform_device *pdev)
>> +{
>> +       int virq, i;
>> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
>> +
>> +       for (i = 0; i < NR_HW_IRQS; i++) {
>> +               virq = msi->msi_groups[i].gic_irq;
>> +               if (virq != 0) {
>> +                       irq_set_chained_handler(virq, NULL);
>> +                       irq_set_handler_data(virq, NULL);
>> +               }
>> +       }
>> +       kfree(msi->msi_groups);
>> +
>> +       kfree(msi->bitmap);
>> +       msi->bitmap = NULL;
>> +
>> +       xgene_free_domains(msi);
>> +
>> +       return 0;
>> +}
>> +
>> +static int xgene_msi_hwirq_alloc(unsigned int cpu)
>> +{
>> +       struct xgene_msi *msi = &xgene_msi_ctrl;
>> +       struct xgene_msi_group *msi_group;
>> +       cpumask_var_t mask;
>> +       int i;
>> +       int err;
>> +
>> +       for (i = 0; i < NR_HW_IRQS; i++) {
>> +               if (i % msi->num_cpus != cpu)
>> +                       continue;
>
> Oh please...
>
>         for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
>
> does the same thing.

Thanks, I changed it.
>
>> +
>> +               msi_group = &msi->msi_groups[i];
>> +               if (!msi_group->gic_irq)
>> +                       continue;
>> +
>> +               irq_set_chained_handler(msi_group->gic_irq,
>> +                                       xgene_msi_isr);
>> +               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
>> +               if (err) {
>> +                       pr_err("failed to register GIC IRQ handler\n");
>> +                       return -EINVAL;
>> +               }
>> +               /*
>> +                * Statically allocate MSI GIC IRQs to each CPU core
>> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
>> +                * to each core.
>> +                */
>> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
>> +                       cpumask_clear(mask);
>> +                       cpumask_set_cpu(cpu, mask);
>> +                       err = irq_set_affinity(msi_group->gic_irq, mask);
>> +                       if (err) {
>> +                               irq_set_chained_handler(msi_group->gic_irq,
>> +                                                       NULL);
>> +                               irq_set_handler_data(msi_group->gic_irq, NULL);
>> +                               free_cpumask_var(mask);
>> +                               pr_err("failed to set affinity for GIC IRQ");
>> +                               return -EINVAL;
>> +                       }
>> +                       free_cpumask_var(mask);
>> +               }
>
> And what happens if alloc_cpumask_var fails?

I add error handling code for this, if this happens, chained handler
will be freed and the function returns with error.
>

>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_msi_hwirq_free(unsigned int cpu)
>> +{
>> +       struct xgene_msi *msi = &xgene_msi_ctrl;
>> +       struct xgene_msi_group *msi_group;
>> +       int i;
>> +
>> +       for (i = 0; i < NR_HW_IRQS; i++) {
>> +               if (i % msi->num_cpus != cpu)
>> +                       continue;
>
> Same remark as above.
>
>> +
>> +               msi_group = &msi->msi_groups[i];
>> +               if (!msi_group->gic_irq)
>> +                       continue;
>> +
>> +               irq_set_chained_handler(msi_group->gic_irq, NULL);
>> +               irq_set_handler_data(msi_group->gic_irq, NULL);
>> +       }
>> +}
>> +
>> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
>> +                                 unsigned long action, void *hcpu)
>> +{
>> +       unsigned cpu = (unsigned long)hcpu;
>> +
>> +       switch (action) {
>> +       case CPU_ONLINE:
>> +       case CPU_ONLINE_FROZEN:
>> +               xgene_msi_hwirq_alloc(cpu);
>> +               break;
>> +       case CPU_DEAD:
>> +       case CPU_DEAD_FROZEN:
>> +               xgene_msi_hwirq_free(cpu);
>> +               break;
>> +       default:
>> +               break;
>> +       }
>> +
>> +       return NOTIFY_OK;
>> +}
>> +
>> +static struct notifier_block xgene_msi_cpu_notifier = {
>> +       .notifier_call = xgene_msi_cpu_callback,
>> +};
>> +
>> +static const struct of_device_id xgene_msi_match_table[] = {
>> +       {.compatible = "apm,xgene1-msi"},
>> +       {},
>> +};
>> +
>> +static int xgene_msi_probe(struct platform_device *pdev)
>> +{
>> +       struct resource *res;
>> +       int rc, irq_index;
>> +       struct xgene_msi *xgene_msi;
>> +       unsigned int cpu;
>> +       int virt_msir;
>> +       u32 msi_reg, msi_val, msi_idx;
>> +
>> +       xgene_msi = &xgene_msi_ctrl;
>> +
>> +       platform_set_drvdata(pdev, xgene_msi);
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(xgene_msi->msi_regs)) {
>> +               dev_err(&pdev->dev, "no reg space\n");
>> +               rc = -EINVAL;
>> +               goto error;
>> +       }
>> +       xgene_msi->msi_addr = res->start;
>> +
>> +       xgene_msi->num_cpus = num_possible_cpus();
>> +
>> +       rc = xgene_msi_init_allocator(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
>> +               goto error;
>> +       }
>> +
>> +       rc = xgene_allocate_domains(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
>> +               goto error;
>> +       }
>> +
>> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
>> +               virt_msir = platform_get_irq(pdev, irq_index);
>> +               if (virt_msir < 0) {
>> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
>> +                               irq_index);
>> +                       rc = -EINVAL;
>> +                       goto error;
>> +               }
>> +               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
>> +               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
>> +               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
>> +       }
>> +
>> +       /*
>> +        * MSInIRx registers are read-to-clear, before registering interrupt
>> +        * handlers, reading all of them to clear all spurious interrupts
>> +        * that may occur before the driver is probed.
>> +        */
>> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
>> +               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++) {
>> +                       msi_reg = (irq_index << 19) + (msi_idx << 16);
>> +                       msi_val = readl(xgene_msi->msi_regs +
>> +                                        MSI_IR0 + msi_reg);
>
> Feels like the above two line could be turned into a small helper (you
> have the same in your handling code).

I created 2 helpers to read MSInIRx and MSIINTn registers.
>
>> +               }
>> +               /* Read MSIINTn to confirm */
>> +               msi_val = readl(xgene_msi->msi_regs +
>> +                               MSI_INT0 + (irq_index << 16));
>> +               if (msi_val) {
>> +                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
>> +                       rc = -EINVAL;
>> +                       goto error;
>> +               }
>> +       }
>> +
>> +       cpu_notifier_register_begin();
>> +
>> +       for_each_online_cpu(cpu)
>> +               if (xgene_msi_hwirq_alloc(cpu)) {
>> +                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
>> +                       cpu_notifier_register_done();
>> +                       goto error;
>> +               }
>> +
>> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
>> +               cpu_notifier_register_done();
>> +               goto error;
>> +       }
>> +
>> +       cpu_notifier_register_done();
>> +
>> +       xgene_msi->mchip.of_node = pdev->dev.of_node;
>> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
>> +               goto error_notifier;
>> +       }
>> +
>> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
>> +
>> +       return 0;
>> +
>> +error_notifier:
>> +       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
>> +error:
>> +       xgene_msi_remove(pdev);
>> +       return rc;
>> +}
>> +
>> +static struct platform_driver xgene_msi_driver = {
>> +       .driver = {
>> +               .name = "xgene-msi",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = xgene_msi_match_table,
>> +       },
>> +       .probe = xgene_msi_probe,
>> +       .remove = xgene_msi_remove,
>> +};
>> +
>> +static int __init xgene_pcie_msi_init(void)
>> +{
>> +       return platform_driver_register(&xgene_msi_driver);
>> +}
>> +subsys_initcall(xgene_pcie_msi_init);
>> +
>> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
>> index 22751ed..3e6faa1 100644
>> --- a/drivers/pci/host/pci-xgene.c
>> +++ b/drivers/pci/host/pci-xgene.c
>> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>>         return 0;
>>  }
>>
>> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
>> +{
>> +       struct device_node *msi_node;
>> +
>> +       msi_node = of_parse_phandle(bus->dev.of_node,
>> +                                       "msi-parent", 0);
>> +       if (!msi_node)
>> +               return -ENODEV;
>> +
>> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
>> +       if (bus->msi)
>> +               bus->msi->dev = &bus->dev;
>> +       else
>> +               return -ENODEV;
>> +       return 0;
>> +}
>> +
>>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>  {
>>         struct device_node *dn = pdev->dev.of_node;
>> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>         if (!bus)
>>                 return -ENOMEM;
>>
>> +       if (IS_ENABLED(CONFIG_PCI_MSI))
>> +               if (xgene_pcie_msi_enable(bus))
>> +                       dev_info(port->dev, "failed to enable MSI\n");
>> +
>>         pci_scan_child_bus(bus);
>>         pci_assign_unassigned_bus_resources(bus);
>>         pci_bus_add_devices(bus);
>> --
>> 1.9.1
>>
>
> This is finally starting to look better (that, or my pink-tainted
> holiday sunglasses are keeping me in a good mood). Please fix the few
> nits above, and hopefully this can make it into 4.2 (unless someone
> else spots something nasty here).
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny.

Regards,
Duc Dang.

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

* Re: [PATCH v7 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-22 18:43                                                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-22 18:43 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On Wed, May 20, 2015 at 2:16 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On Mon, 18 May 2015 10:55:19 +0100
> Duc Dang <dhdang@apm.com> wrote:
>
>> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
>> to GIC V2M specification for MSI Termination.
>>
>> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
>> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
>> and shared across all 5 PCIe ports.
>>
>> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
>> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
>> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
>> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
>> driver supports is reduced to 256.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  drivers/pci/host/Kconfig         |  10 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 550 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 582 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 7b892a9..0932118 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,21 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>> +       select PCI_XGENE_MSI if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +       help
>> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
>> +         This MSI driver will provide MSI support for 5 PCIe ports of
>> +         APM X-Gene v1 SoC
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index e61d91c..f39bde3 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..648bc8f
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,550 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/cpu.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_IR0                        0x000000
>> +#define MSI_INT0               0x800000
>> +#define IDX_PER_GROUP          8
>> +#define IRQS_PER_IDX           16
>> +#define NR_HW_IRQS             16
>> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
>> +
>> +struct xgene_msi_group {
>> +       struct xgene_msi                *msi;
>> +       int                             gic_irq;
>> +       u32                             msi_grp;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       u64                             msi_addr;
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       struct xgene_msi_group          *msi_groups;
>> +       int                             num_cpus;
>> +};
>> +
>> +/* Global data */
>> +static struct xgene_msi xgene_msi_ctrl;
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +/*
>> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
>> + * n is group number (0..F), x is index of registers in each group (0..7)
>> + * The registers layout is like following:
>> + * MSI0IR0                     base_addr
>> + * MSI0IR1                     base_addr +  0x10000
>> + * ...                         ...
>> + * MSI0IR6                     base_addr +  0x60000
>> + * MSI0IR7                     base_addr +  0x70000
>> + * MSI1IR0                     base_addr +  0x80000
>> + * MSI1IR1                     base_addr +  0x90000
>> + * ...                         ...
>> + * MSI1IR7                     base_addr +  0xF0000
>> + * MSI2IR0                     base_addr + 0x100000
>> + * ...                         ...
>> + * MSIFIR0                     base_addr + 0x780000
>> + * MSIFIR1                     base_addr + 0x790000
>> + * ...                         ...
>> + * MSIFIR7                     base_addr + 0x7F0000
>> + *
>> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
>> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
>> + * registers.
>> + *
>> + * With 2048 MSI vectors supported, the MSI message can be construct using
>> + * following scheme:
>> + * - Divide into 8 256-vector groups
>> + *             Group 0: 0-255
>> + *             Group 1: 256-511
>> + *             Group 2: 512-767
>> + *             ...
>> + *             Group 7: 1792-2047
>> + * - Each 256-vector group is divided into 16 16-vector groups
>> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
>> + *             Group 0: 0-15
>> + *             Group 1: 16-32
>> + *             ...
>> + *             Group 15: 240-255
>> + * - The termination address of MSI vector in 256-vector group n and 16-vector
>> + * group x is the address of MSIxIRn
>> + * - The data for MSI vector in 16-vector group x is x
>> + */
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
>> +       u32 group = data->hwirq % NR_HW_IRQS;
>> +
>> +       msg->address_hi = upper_32_bits(msi->msi_addr);
>> +       msg->address_lo = lower_32_bits(msi->msi_addr) +
>> +                         (((8 * group) + reg_set) << 16);
>
> It would be safer to compute the offset from msi_addr, and apply
> {upper,lower}_32_bits() to the resulting value. Otherwise, you end-up
> with the wrong upper bits if you cross a 4GB boundary. I know this is
> not the case here, but it doesn't hurt to be correct.
>
Yes, I changed this in v8 patch.

>> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
>> +}
>> +
>> +/*
>> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
>> + * To maintain the expected behaviour of .set_affinity for each MSI
>> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
>> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
>> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
>> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
>> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
>> + */
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
>> +       int target_cpu = cpumask_first(mask);
>> +       int curr_cpu;
>> +
>> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
>> +       if (curr_cpu == target_cpu)
>> +               return IRQ_SET_MASK_OK_DONE;
>> +
>> +       /* Update MSI number to target the new CPU */
>> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
>> +
>> +       return IRQ_SET_MASK_OK;
>> +}
>> +
>> +static struct irq_chip xgene_msi_bottom_irq_chip = {
>> +       .name                   = "MSI",
>> +       .irq_set_affinity       = xgene_msi_set_affinity,
>> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
>> +};
>> +
>> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
>> +                                 unsigned int nr_irqs, void *args)
>> +{
>> +       struct xgene_msi *msi = domain->host_data;
>> +       int msi_irq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
>> +                                            msi->num_cpus, 0);
>> +       if (msi_irq < NR_MSI_VEC)
>> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
>> +       else
>> +               msi_irq = -ENOSPC;
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       if (msi_irq < 0)
>> +               return msi_irq;
>> +
>> +       irq_domain_set_info(domain, virq, msi_irq,
>> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
>> +                           handle_simple_irq, NULL, NULL);
>> +       set_irq_flags(virq, IRQF_VALID);
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>> +                                 unsigned int virq, unsigned int nr_irqs)
>> +{
>> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>> +       u32 hwirq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
>> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops msi_domain_ops = {
>> +       .alloc  = xgene_irq_domain_alloc,
>> +       .free   = xgene_irq_domain_free,
>> +};
>> +
>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>> +{
>> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
>> +                                           &msi_domain_ops, msi);
>> +       if (!msi->domain)
>> +               return -ENOMEM;
>> +
>> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>> +                                                     &xgene_msi_domain_info,
>> +                                                     msi->domain);
>> +
>> +       if (!msi->mchip.domain) {
>> +               irq_domain_remove(msi->domain);
>> +               return -ENOMEM;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_free_domains(struct xgene_msi *msi)
>> +{
>> +       if (msi->mchip.domain)
>> +               irq_domain_remove(msi->mchip.domain);
>> +       if (msi->domain)
>> +               irq_domain_remove(msi->domain);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
>> +
>> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +       if (!xgene_msi->bitmap)
>> +               return -ENOMEM;
>> +
>> +       mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
>> +                                       sizeof(struct xgene_msi_group),
>> +                                       GFP_KERNEL);
>> +       if (!xgene_msi->msi_groups)
>> +               return -ENOMEM;
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
>> +{
>> +       struct irq_chip *chip = irq_desc_get_chip(desc);
>> +       struct xgene_msi_group *msi_groups;
>> +       struct xgene_msi *xgene_msi;
>> +       unsigned int virq;
>> +       int msir_index, msir_reg, msir_val, hw_irq;
>> +       u32 intr_index, grp_select, msi_grp;
>> +
>> +       chained_irq_enter(chip, desc);
>> +
>> +       msi_groups = irq_desc_get_handler_data(desc);
>> +       xgene_msi = msi_groups->msi;
>> +       msi_grp = msi_groups->msi_grp;
>> +
>> +       /*
>> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
>> +        * If bit x of this register is set (x is 0..7), one or more interupts
>> +        * corresponding to MSInIRx is set.
>> +        */
>> +       grp_select = readl_relaxed(xgene_msi->msi_regs +
>> +                                  MSI_INT0 + (msi_grp << 16));
>> +       while (grp_select) {
>> +               msir_index = ffs(grp_select) - 1;
>> +               /*
>> +                * Calculate MSInIRx address to read to check for interrupts
>> +                * (refer to termination address and data assignment
>> +                * described in xgene_compose_msi_msg function)
>> +                */
>> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
>> +               msir_val = readl_relaxed(xgene_msi->msi_regs +
>> +                                        MSI_IR0 + msir_reg);
>> +               while (msir_val) {
>> +                       intr_index = ffs(msir_val) - 1;
>> +                       /*
>> +                        * Calculate MSI vector number (refer to the termination
>> +                        * address and data assignment described in
>> +                        * xgene_compose_msi_msg function)
>> +                        */
>> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
>> +                                NR_HW_IRQS) + msi_grp;
>> +                       /*
>> +                        * As we have multiple hw_irq that maps to single MSI,
>> +                        * always look up the virq using the hw_irq as seen from
>> +                        * CPU0
>> +                        */
>> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
>> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> +                       WARN_ON(!virq);
>> +                       if (virq != 0)
>> +                               generic_handle_irq(virq);
>> +                       msir_val &= ~(1 << intr_index);
>> +               }
>> +               grp_select &= ~(1 << msir_index);
>> +       }
>
> I wonder if you wouldn't be better off resampling MSIINTn here, just in
> case something else has been made pending in the meantime.

I added code to read MSI_INTn of the same group again when grp_select
becomes 0 (when we handle all the interrupts).

>
>> +
>> +       chained_irq_exit(chip, desc);
>> +}
>> +
>> +static int xgene_msi_remove(struct platform_device *pdev)
>> +{
>> +       int virq, i;
>> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
>> +
>> +       for (i = 0; i < NR_HW_IRQS; i++) {
>> +               virq = msi->msi_groups[i].gic_irq;
>> +               if (virq != 0) {
>> +                       irq_set_chained_handler(virq, NULL);
>> +                       irq_set_handler_data(virq, NULL);
>> +               }
>> +       }
>> +       kfree(msi->msi_groups);
>> +
>> +       kfree(msi->bitmap);
>> +       msi->bitmap = NULL;
>> +
>> +       xgene_free_domains(msi);
>> +
>> +       return 0;
>> +}
>> +
>> +static int xgene_msi_hwirq_alloc(unsigned int cpu)
>> +{
>> +       struct xgene_msi *msi = &xgene_msi_ctrl;
>> +       struct xgene_msi_group *msi_group;
>> +       cpumask_var_t mask;
>> +       int i;
>> +       int err;
>> +
>> +       for (i = 0; i < NR_HW_IRQS; i++) {
>> +               if (i % msi->num_cpus != cpu)
>> +                       continue;
>
> Oh please...
>
>         for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
>
> does the same thing.

Thanks, I changed it.
>
>> +
>> +               msi_group = &msi->msi_groups[i];
>> +               if (!msi_group->gic_irq)
>> +                       continue;
>> +
>> +               irq_set_chained_handler(msi_group->gic_irq,
>> +                                       xgene_msi_isr);
>> +               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
>> +               if (err) {
>> +                       pr_err("failed to register GIC IRQ handler\n");
>> +                       return -EINVAL;
>> +               }
>> +               /*
>> +                * Statically allocate MSI GIC IRQs to each CPU core
>> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
>> +                * to each core.
>> +                */
>> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
>> +                       cpumask_clear(mask);
>> +                       cpumask_set_cpu(cpu, mask);
>> +                       err = irq_set_affinity(msi_group->gic_irq, mask);
>> +                       if (err) {
>> +                               irq_set_chained_handler(msi_group->gic_irq,
>> +                                                       NULL);
>> +                               irq_set_handler_data(msi_group->gic_irq, NULL);
>> +                               free_cpumask_var(mask);
>> +                               pr_err("failed to set affinity for GIC IRQ");
>> +                               return -EINVAL;
>> +                       }
>> +                       free_cpumask_var(mask);
>> +               }
>
> And what happens if alloc_cpumask_var fails?

I add error handling code for this, if this happens, chained handler
will be freed and the function returns with error.
>

>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_msi_hwirq_free(unsigned int cpu)
>> +{
>> +       struct xgene_msi *msi = &xgene_msi_ctrl;
>> +       struct xgene_msi_group *msi_group;
>> +       int i;
>> +
>> +       for (i = 0; i < NR_HW_IRQS; i++) {
>> +               if (i % msi->num_cpus != cpu)
>> +                       continue;
>
> Same remark as above.
>
>> +
>> +               msi_group = &msi->msi_groups[i];
>> +               if (!msi_group->gic_irq)
>> +                       continue;
>> +
>> +               irq_set_chained_handler(msi_group->gic_irq, NULL);
>> +               irq_set_handler_data(msi_group->gic_irq, NULL);
>> +       }
>> +}
>> +
>> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
>> +                                 unsigned long action, void *hcpu)
>> +{
>> +       unsigned cpu = (unsigned long)hcpu;
>> +
>> +       switch (action) {
>> +       case CPU_ONLINE:
>> +       case CPU_ONLINE_FROZEN:
>> +               xgene_msi_hwirq_alloc(cpu);
>> +               break;
>> +       case CPU_DEAD:
>> +       case CPU_DEAD_FROZEN:
>> +               xgene_msi_hwirq_free(cpu);
>> +               break;
>> +       default:
>> +               break;
>> +       }
>> +
>> +       return NOTIFY_OK;
>> +}
>> +
>> +static struct notifier_block xgene_msi_cpu_notifier = {
>> +       .notifier_call = xgene_msi_cpu_callback,
>> +};
>> +
>> +static const struct of_device_id xgene_msi_match_table[] = {
>> +       {.compatible = "apm,xgene1-msi"},
>> +       {},
>> +};
>> +
>> +static int xgene_msi_probe(struct platform_device *pdev)
>> +{
>> +       struct resource *res;
>> +       int rc, irq_index;
>> +       struct xgene_msi *xgene_msi;
>> +       unsigned int cpu;
>> +       int virt_msir;
>> +       u32 msi_reg, msi_val, msi_idx;
>> +
>> +       xgene_msi = &xgene_msi_ctrl;
>> +
>> +       platform_set_drvdata(pdev, xgene_msi);
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(xgene_msi->msi_regs)) {
>> +               dev_err(&pdev->dev, "no reg space\n");
>> +               rc = -EINVAL;
>> +               goto error;
>> +       }
>> +       xgene_msi->msi_addr = res->start;
>> +
>> +       xgene_msi->num_cpus = num_possible_cpus();
>> +
>> +       rc = xgene_msi_init_allocator(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
>> +               goto error;
>> +       }
>> +
>> +       rc = xgene_allocate_domains(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
>> +               goto error;
>> +       }
>> +
>> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
>> +               virt_msir = platform_get_irq(pdev, irq_index);
>> +               if (virt_msir < 0) {
>> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
>> +                               irq_index);
>> +                       rc = -EINVAL;
>> +                       goto error;
>> +               }
>> +               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
>> +               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
>> +               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
>> +       }
>> +
>> +       /*
>> +        * MSInIRx registers are read-to-clear, before registering interrupt
>> +        * handlers, reading all of them to clear all spurious interrupts
>> +        * that may occur before the driver is probed.
>> +        */
>> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
>> +               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++) {
>> +                       msi_reg = (irq_index << 19) + (msi_idx << 16);
>> +                       msi_val = readl(xgene_msi->msi_regs +
>> +                                        MSI_IR0 + msi_reg);
>
> Feels like the above two line could be turned into a small helper (you
> have the same in your handling code).

I created 2 helpers to read MSInIRx and MSIINTn registers.
>
>> +               }
>> +               /* Read MSIINTn to confirm */
>> +               msi_val = readl(xgene_msi->msi_regs +
>> +                               MSI_INT0 + (irq_index << 16));
>> +               if (msi_val) {
>> +                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
>> +                       rc = -EINVAL;
>> +                       goto error;
>> +               }
>> +       }
>> +
>> +       cpu_notifier_register_begin();
>> +
>> +       for_each_online_cpu(cpu)
>> +               if (xgene_msi_hwirq_alloc(cpu)) {
>> +                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
>> +                       cpu_notifier_register_done();
>> +                       goto error;
>> +               }
>> +
>> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
>> +               cpu_notifier_register_done();
>> +               goto error;
>> +       }
>> +
>> +       cpu_notifier_register_done();
>> +
>> +       xgene_msi->mchip.of_node = pdev->dev.of_node;
>> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
>> +               goto error_notifier;
>> +       }
>> +
>> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
>> +
>> +       return 0;
>> +
>> +error_notifier:
>> +       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
>> +error:
>> +       xgene_msi_remove(pdev);
>> +       return rc;
>> +}
>> +
>> +static struct platform_driver xgene_msi_driver = {
>> +       .driver = {
>> +               .name = "xgene-msi",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = xgene_msi_match_table,
>> +       },
>> +       .probe = xgene_msi_probe,
>> +       .remove = xgene_msi_remove,
>> +};
>> +
>> +static int __init xgene_pcie_msi_init(void)
>> +{
>> +       return platform_driver_register(&xgene_msi_driver);
>> +}
>> +subsys_initcall(xgene_pcie_msi_init);
>> +
>> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
>> index 22751ed..3e6faa1 100644
>> --- a/drivers/pci/host/pci-xgene.c
>> +++ b/drivers/pci/host/pci-xgene.c
>> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>>         return 0;
>>  }
>>
>> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
>> +{
>> +       struct device_node *msi_node;
>> +
>> +       msi_node = of_parse_phandle(bus->dev.of_node,
>> +                                       "msi-parent", 0);
>> +       if (!msi_node)
>> +               return -ENODEV;
>> +
>> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
>> +       if (bus->msi)
>> +               bus->msi->dev = &bus->dev;
>> +       else
>> +               return -ENODEV;
>> +       return 0;
>> +}
>> +
>>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>  {
>>         struct device_node *dn = pdev->dev.of_node;
>> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>         if (!bus)
>>                 return -ENOMEM;
>>
>> +       if (IS_ENABLED(CONFIG_PCI_MSI))
>> +               if (xgene_pcie_msi_enable(bus))
>> +                       dev_info(port->dev, "failed to enable MSI\n");
>> +
>>         pci_scan_child_bus(bus);
>>         pci_assign_unassigned_bus_resources(bus);
>>         pci_bus_add_devices(bus);
>> --
>> 1.9.1
>>
>
> This is finally starting to look better (that, or my pink-tainted
> holiday sunglasses are keeping me in a good mood). Please fix the few
> nits above, and hopefully this can make it into 4.2 (unless someone
> else spots something nasty here).
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny.

Regards,
Duc Dang.

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

* [PATCH v7 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-22 18:43                                                       ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-22 18:43 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, May 20, 2015 at 2:16 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On Mon, 18 May 2015 10:55:19 +0100
> Duc Dang <dhdang@apm.com> wrote:
>
>> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
>> to GIC V2M specification for MSI Termination.
>>
>> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
>> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
>> and shared across all 5 PCIe ports.
>>
>> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
>> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
>> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
>> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
>> driver supports is reduced to 256.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  drivers/pci/host/Kconfig         |  10 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 550 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 582 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 7b892a9..0932118 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,21 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>> +       select PCI_XGENE_MSI if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +       help
>> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
>> +         This MSI driver will provide MSI support for 5 PCIe ports of
>> +         APM X-Gene v1 SoC
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index e61d91c..f39bde3 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..648bc8f
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,550 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/cpu.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_IR0                        0x000000
>> +#define MSI_INT0               0x800000
>> +#define IDX_PER_GROUP          8
>> +#define IRQS_PER_IDX           16
>> +#define NR_HW_IRQS             16
>> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
>> +
>> +struct xgene_msi_group {
>> +       struct xgene_msi                *msi;
>> +       int                             gic_irq;
>> +       u32                             msi_grp;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       u64                             msi_addr;
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       struct xgene_msi_group          *msi_groups;
>> +       int                             num_cpus;
>> +};
>> +
>> +/* Global data */
>> +static struct xgene_msi xgene_msi_ctrl;
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +/*
>> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
>> + * n is group number (0..F), x is index of registers in each group (0..7)
>> + * The registers layout is like following:
>> + * MSI0IR0                     base_addr
>> + * MSI0IR1                     base_addr +  0x10000
>> + * ...                         ...
>> + * MSI0IR6                     base_addr +  0x60000
>> + * MSI0IR7                     base_addr +  0x70000
>> + * MSI1IR0                     base_addr +  0x80000
>> + * MSI1IR1                     base_addr +  0x90000
>> + * ...                         ...
>> + * MSI1IR7                     base_addr +  0xF0000
>> + * MSI2IR0                     base_addr + 0x100000
>> + * ...                         ...
>> + * MSIFIR0                     base_addr + 0x780000
>> + * MSIFIR1                     base_addr + 0x790000
>> + * ...                         ...
>> + * MSIFIR7                     base_addr + 0x7F0000
>> + *
>> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
>> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
>> + * registers.
>> + *
>> + * With 2048 MSI vectors supported, the MSI message can be construct using
>> + * following scheme:
>> + * - Divide into 8 256-vector groups
>> + *             Group 0: 0-255
>> + *             Group 1: 256-511
>> + *             Group 2: 512-767
>> + *             ...
>> + *             Group 7: 1792-2047
>> + * - Each 256-vector group is divided into 16 16-vector groups
>> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
>> + *             Group 0: 0-15
>> + *             Group 1: 16-32
>> + *             ...
>> + *             Group 15: 240-255
>> + * - The termination address of MSI vector in 256-vector group n and 16-vector
>> + * group x is the address of MSIxIRn
>> + * - The data for MSI vector in 16-vector group x is x
>> + */
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
>> +       u32 group = data->hwirq % NR_HW_IRQS;
>> +
>> +       msg->address_hi = upper_32_bits(msi->msi_addr);
>> +       msg->address_lo = lower_32_bits(msi->msi_addr) +
>> +                         (((8 * group) + reg_set) << 16);
>
> It would be safer to compute the offset from msi_addr, and apply
> {upper,lower}_32_bits() to the resulting value. Otherwise, you end-up
> with the wrong upper bits if you cross a 4GB boundary. I know this is
> not the case here, but it doesn't hurt to be correct.
>
Yes, I changed this in v8 patch.

>> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
>> +}
>> +
>> +/*
>> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
>> + * To maintain the expected behaviour of .set_affinity for each MSI
>> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
>> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
>> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
>> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
>> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
>> + */
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
>> +       int target_cpu = cpumask_first(mask);
>> +       int curr_cpu;
>> +
>> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
>> +       if (curr_cpu == target_cpu)
>> +               return IRQ_SET_MASK_OK_DONE;
>> +
>> +       /* Update MSI number to target the new CPU */
>> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
>> +
>> +       return IRQ_SET_MASK_OK;
>> +}
>> +
>> +static struct irq_chip xgene_msi_bottom_irq_chip = {
>> +       .name                   = "MSI",
>> +       .irq_set_affinity       = xgene_msi_set_affinity,
>> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
>> +};
>> +
>> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
>> +                                 unsigned int nr_irqs, void *args)
>> +{
>> +       struct xgene_msi *msi = domain->host_data;
>> +       int msi_irq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
>> +                                            msi->num_cpus, 0);
>> +       if (msi_irq < NR_MSI_VEC)
>> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
>> +       else
>> +               msi_irq = -ENOSPC;
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       if (msi_irq < 0)
>> +               return msi_irq;
>> +
>> +       irq_domain_set_info(domain, virq, msi_irq,
>> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
>> +                           handle_simple_irq, NULL, NULL);
>> +       set_irq_flags(virq, IRQF_VALID);
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>> +                                 unsigned int virq, unsigned int nr_irqs)
>> +{
>> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>> +       u32 hwirq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
>> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops msi_domain_ops = {
>> +       .alloc  = xgene_irq_domain_alloc,
>> +       .free   = xgene_irq_domain_free,
>> +};
>> +
>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>> +{
>> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
>> +                                           &msi_domain_ops, msi);
>> +       if (!msi->domain)
>> +               return -ENOMEM;
>> +
>> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>> +                                                     &xgene_msi_domain_info,
>> +                                                     msi->domain);
>> +
>> +       if (!msi->mchip.domain) {
>> +               irq_domain_remove(msi->domain);
>> +               return -ENOMEM;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_free_domains(struct xgene_msi *msi)
>> +{
>> +       if (msi->mchip.domain)
>> +               irq_domain_remove(msi->mchip.domain);
>> +       if (msi->domain)
>> +               irq_domain_remove(msi->domain);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
>> +
>> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +       if (!xgene_msi->bitmap)
>> +               return -ENOMEM;
>> +
>> +       mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
>> +                                       sizeof(struct xgene_msi_group),
>> +                                       GFP_KERNEL);
>> +       if (!xgene_msi->msi_groups)
>> +               return -ENOMEM;
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
>> +{
>> +       struct irq_chip *chip = irq_desc_get_chip(desc);
>> +       struct xgene_msi_group *msi_groups;
>> +       struct xgene_msi *xgene_msi;
>> +       unsigned int virq;
>> +       int msir_index, msir_reg, msir_val, hw_irq;
>> +       u32 intr_index, grp_select, msi_grp;
>> +
>> +       chained_irq_enter(chip, desc);
>> +
>> +       msi_groups = irq_desc_get_handler_data(desc);
>> +       xgene_msi = msi_groups->msi;
>> +       msi_grp = msi_groups->msi_grp;
>> +
>> +       /*
>> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
>> +        * If bit x of this register is set (x is 0..7), one or more interupts
>> +        * corresponding to MSInIRx is set.
>> +        */
>> +       grp_select = readl_relaxed(xgene_msi->msi_regs +
>> +                                  MSI_INT0 + (msi_grp << 16));
>> +       while (grp_select) {
>> +               msir_index = ffs(grp_select) - 1;
>> +               /*
>> +                * Calculate MSInIRx address to read to check for interrupts
>> +                * (refer to termination address and data assignment
>> +                * described in xgene_compose_msi_msg function)
>> +                */
>> +               msir_reg = (msi_grp << 19) + (msir_index << 16);
>> +               msir_val = readl_relaxed(xgene_msi->msi_regs +
>> +                                        MSI_IR0 + msir_reg);
>> +               while (msir_val) {
>> +                       intr_index = ffs(msir_val) - 1;
>> +                       /*
>> +                        * Calculate MSI vector number (refer to the termination
>> +                        * address and data assignment described in
>> +                        * xgene_compose_msi_msg function)
>> +                        */
>> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
>> +                                NR_HW_IRQS) + msi_grp;
>> +                       /*
>> +                        * As we have multiple hw_irq that maps to single MSI,
>> +                        * always look up the virq using the hw_irq as seen from
>> +                        * CPU0
>> +                        */
>> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
>> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> +                       WARN_ON(!virq);
>> +                       if (virq != 0)
>> +                               generic_handle_irq(virq);
>> +                       msir_val &= ~(1 << intr_index);
>> +               }
>> +               grp_select &= ~(1 << msir_index);
>> +       }
>
> I wonder if you wouldn't be better off resampling MSIINTn here, just in
> case something else has been made pending in the meantime.

I added code to read MSI_INTn of the same group again when grp_select
becomes 0 (when we handle all the interrupts).

>
>> +
>> +       chained_irq_exit(chip, desc);
>> +}
>> +
>> +static int xgene_msi_remove(struct platform_device *pdev)
>> +{
>> +       int virq, i;
>> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
>> +
>> +       for (i = 0; i < NR_HW_IRQS; i++) {
>> +               virq = msi->msi_groups[i].gic_irq;
>> +               if (virq != 0) {
>> +                       irq_set_chained_handler(virq, NULL);
>> +                       irq_set_handler_data(virq, NULL);
>> +               }
>> +       }
>> +       kfree(msi->msi_groups);
>> +
>> +       kfree(msi->bitmap);
>> +       msi->bitmap = NULL;
>> +
>> +       xgene_free_domains(msi);
>> +
>> +       return 0;
>> +}
>> +
>> +static int xgene_msi_hwirq_alloc(unsigned int cpu)
>> +{
>> +       struct xgene_msi *msi = &xgene_msi_ctrl;
>> +       struct xgene_msi_group *msi_group;
>> +       cpumask_var_t mask;
>> +       int i;
>> +       int err;
>> +
>> +       for (i = 0; i < NR_HW_IRQS; i++) {
>> +               if (i % msi->num_cpus != cpu)
>> +                       continue;
>
> Oh please...
>
>         for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
>
> does the same thing.

Thanks, I changed it.
>
>> +
>> +               msi_group = &msi->msi_groups[i];
>> +               if (!msi_group->gic_irq)
>> +                       continue;
>> +
>> +               irq_set_chained_handler(msi_group->gic_irq,
>> +                                       xgene_msi_isr);
>> +               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
>> +               if (err) {
>> +                       pr_err("failed to register GIC IRQ handler\n");
>> +                       return -EINVAL;
>> +               }
>> +               /*
>> +                * Statically allocate MSI GIC IRQs to each CPU core
>> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
>> +                * to each core.
>> +                */
>> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
>> +                       cpumask_clear(mask);
>> +                       cpumask_set_cpu(cpu, mask);
>> +                       err = irq_set_affinity(msi_group->gic_irq, mask);
>> +                       if (err) {
>> +                               irq_set_chained_handler(msi_group->gic_irq,
>> +                                                       NULL);
>> +                               irq_set_handler_data(msi_group->gic_irq, NULL);
>> +                               free_cpumask_var(mask);
>> +                               pr_err("failed to set affinity for GIC IRQ");
>> +                               return -EINVAL;
>> +                       }
>> +                       free_cpumask_var(mask);
>> +               }
>
> And what happens if alloc_cpumask_var fails?

I add error handling code for this, if this happens, chained handler
will be freed and the function returns with error.
>

>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_msi_hwirq_free(unsigned int cpu)
>> +{
>> +       struct xgene_msi *msi = &xgene_msi_ctrl;
>> +       struct xgene_msi_group *msi_group;
>> +       int i;
>> +
>> +       for (i = 0; i < NR_HW_IRQS; i++) {
>> +               if (i % msi->num_cpus != cpu)
>> +                       continue;
>
> Same remark as above.
>
>> +
>> +               msi_group = &msi->msi_groups[i];
>> +               if (!msi_group->gic_irq)
>> +                       continue;
>> +
>> +               irq_set_chained_handler(msi_group->gic_irq, NULL);
>> +               irq_set_handler_data(msi_group->gic_irq, NULL);
>> +       }
>> +}
>> +
>> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
>> +                                 unsigned long action, void *hcpu)
>> +{
>> +       unsigned cpu = (unsigned long)hcpu;
>> +
>> +       switch (action) {
>> +       case CPU_ONLINE:
>> +       case CPU_ONLINE_FROZEN:
>> +               xgene_msi_hwirq_alloc(cpu);
>> +               break;
>> +       case CPU_DEAD:
>> +       case CPU_DEAD_FROZEN:
>> +               xgene_msi_hwirq_free(cpu);
>> +               break;
>> +       default:
>> +               break;
>> +       }
>> +
>> +       return NOTIFY_OK;
>> +}
>> +
>> +static struct notifier_block xgene_msi_cpu_notifier = {
>> +       .notifier_call = xgene_msi_cpu_callback,
>> +};
>> +
>> +static const struct of_device_id xgene_msi_match_table[] = {
>> +       {.compatible = "apm,xgene1-msi"},
>> +       {},
>> +};
>> +
>> +static int xgene_msi_probe(struct platform_device *pdev)
>> +{
>> +       struct resource *res;
>> +       int rc, irq_index;
>> +       struct xgene_msi *xgene_msi;
>> +       unsigned int cpu;
>> +       int virt_msir;
>> +       u32 msi_reg, msi_val, msi_idx;
>> +
>> +       xgene_msi = &xgene_msi_ctrl;
>> +
>> +       platform_set_drvdata(pdev, xgene_msi);
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(xgene_msi->msi_regs)) {
>> +               dev_err(&pdev->dev, "no reg space\n");
>> +               rc = -EINVAL;
>> +               goto error;
>> +       }
>> +       xgene_msi->msi_addr = res->start;
>> +
>> +       xgene_msi->num_cpus = num_possible_cpus();
>> +
>> +       rc = xgene_msi_init_allocator(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
>> +               goto error;
>> +       }
>> +
>> +       rc = xgene_allocate_domains(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
>> +               goto error;
>> +       }
>> +
>> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
>> +               virt_msir = platform_get_irq(pdev, irq_index);
>> +               if (virt_msir < 0) {
>> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
>> +                               irq_index);
>> +                       rc = -EINVAL;
>> +                       goto error;
>> +               }
>> +               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
>> +               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
>> +               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
>> +       }
>> +
>> +       /*
>> +        * MSInIRx registers are read-to-clear, before registering interrupt
>> +        * handlers, reading all of them to clear all spurious interrupts
>> +        * that may occur before the driver is probed.
>> +        */
>> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
>> +               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++) {
>> +                       msi_reg = (irq_index << 19) + (msi_idx << 16);
>> +                       msi_val = readl(xgene_msi->msi_regs +
>> +                                        MSI_IR0 + msi_reg);
>
> Feels like the above two line could be turned into a small helper (you
> have the same in your handling code).

I created 2 helpers to read MSInIRx and MSIINTn registers.
>
>> +               }
>> +               /* Read MSIINTn to confirm */
>> +               msi_val = readl(xgene_msi->msi_regs +
>> +                               MSI_INT0 + (irq_index << 16));
>> +               if (msi_val) {
>> +                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
>> +                       rc = -EINVAL;
>> +                       goto error;
>> +               }
>> +       }
>> +
>> +       cpu_notifier_register_begin();
>> +
>> +       for_each_online_cpu(cpu)
>> +               if (xgene_msi_hwirq_alloc(cpu)) {
>> +                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
>> +                       cpu_notifier_register_done();
>> +                       goto error;
>> +               }
>> +
>> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
>> +               cpu_notifier_register_done();
>> +               goto error;
>> +       }
>> +
>> +       cpu_notifier_register_done();
>> +
>> +       xgene_msi->mchip.of_node = pdev->dev.of_node;
>> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
>> +               goto error_notifier;
>> +       }
>> +
>> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
>> +
>> +       return 0;
>> +
>> +error_notifier:
>> +       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
>> +error:
>> +       xgene_msi_remove(pdev);
>> +       return rc;
>> +}
>> +
>> +static struct platform_driver xgene_msi_driver = {
>> +       .driver = {
>> +               .name = "xgene-msi",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = xgene_msi_match_table,
>> +       },
>> +       .probe = xgene_msi_probe,
>> +       .remove = xgene_msi_remove,
>> +};
>> +
>> +static int __init xgene_pcie_msi_init(void)
>> +{
>> +       return platform_driver_register(&xgene_msi_driver);
>> +}
>> +subsys_initcall(xgene_pcie_msi_init);
>> +
>> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
>> index 22751ed..3e6faa1 100644
>> --- a/drivers/pci/host/pci-xgene.c
>> +++ b/drivers/pci/host/pci-xgene.c
>> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>>         return 0;
>>  }
>>
>> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
>> +{
>> +       struct device_node *msi_node;
>> +
>> +       msi_node = of_parse_phandle(bus->dev.of_node,
>> +                                       "msi-parent", 0);
>> +       if (!msi_node)
>> +               return -ENODEV;
>> +
>> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
>> +       if (bus->msi)
>> +               bus->msi->dev = &bus->dev;
>> +       else
>> +               return -ENODEV;
>> +       return 0;
>> +}
>> +
>>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>  {
>>         struct device_node *dn = pdev->dev.of_node;
>> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>         if (!bus)
>>                 return -ENOMEM;
>>
>> +       if (IS_ENABLED(CONFIG_PCI_MSI))
>> +               if (xgene_pcie_msi_enable(bus))
>> +                       dev_info(port->dev, "failed to enable MSI\n");
>> +
>>         pci_scan_child_bus(bus);
>>         pci_assign_unassigned_bus_resources(bus);
>>         pci_bus_add_devices(bus);
>> --
>> 1.9.1
>>
>
> This is finally starting to look better (that, or my pink-tainted
> holiday sunglasses are keeping me in a good mood). Please fix the few
> nits above, and hopefully this can make it into 4.2 (unless someone
> else spots something nasty here).
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny.

Regards,
Duc Dang.

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

* Re: [PATCH v8 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-05-22 18:41                                                       ` Duc Dang
  (?)
@ 2015-05-25 11:52                                                         ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-05-25 11:52 UTC (permalink / raw)
  To: Duc Dang
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Thomas Gleixner, Jason Cooper

On Fri, 22 May 2015 19:41:10 +0100
Duc Dang <dhdang@apm.com> wrote:

> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports.
> 
> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
> driver supports is reduced to 256.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |  10 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 573 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 605 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..0932118 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,21 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +       help
> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
> +         This MSI driver will provide MSI support for 5 PCIe ports of
> +         APM X-Gene v1 SoC
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..0c6823d
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,573 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi_group {
> +       struct xgene_msi                *msi;
> +       int                             gic_irq;
> +       u32                             msi_grp;
> +};
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       struct xgene_msi_group          *msi_groups;
> +       int                             num_cpus;
> +};
> +
> +/* Global data */
> +static struct xgene_msi xgene_msi_ctrl;
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + * MSIINT0                     base_addr + 0x800000
> + * MSIINT1                     base_addr + 0x810000
> + * ...                         ...
> + * MSIINTF                     base_addr + 0x8F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
> + * the MSI pending status caused by 1 of its 8 index registers.
> + */
> +
> +/* MSInIRx read helper */
> +static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
> +                                   u32 msi_grp, u32 msir_idx)
> +{
> +       return readl_relaxed(msi->msi_regs + MSI_IR0 +
> +                             (msi_grp << 19) + (msir_idx << 16));
> +}
> +
> +/* MSIINTn read helper */
> +static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
> +{
> +       return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> +}
> +
> +/* With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);

Create a hwirq_to_reg_set helper.

> +       u32 group = data->hwirq % NR_HW_IRQS;

Create a hwirq_to_group helper.

> +
> +       msg->address_hi = upper_32_bits(msi->msi_addr +
> +                                       (((8 * group) + reg_set) << 16));
> +       msg->address_lo = lower_32_bits(msi->msi_addr +
> +                                       (((8 * group) + reg_set) << 16));

Rule of thumb: if you write something complicated twice, you've written
one time too many:

u64 target_addr = msi->msi_addr + ((8 * group) + reg_set) << 16));

msg->address_hi = upper_32_bits(target_addr);
msg->address_lo = lower_32_bits(target_addr);

It doesn't hurt to write something readable.

> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +
> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;

Create a hwirq_to_cpu helper.

> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       /* Update MSI number to target the new CPU */
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> +
> +       return IRQ_SET_MASK_OK;
> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +       struct xgene_msi *msi = domain->host_data;
> +       int msi_irq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
> +                                            msi->num_cpus, 0);
> +       if (msi_irq < NR_MSI_VEC)
> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq,
> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> +                           handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +       u32 hwirq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);

Create hwirq_to_canonical_hwirq helper (hwirq - hwirq_to_cpu(hwirq)).

> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> +                                           &msi_domain_ops, msi);
> +       if (!msi->domain)
> +               return -ENOMEM;
> +
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
> +                                       sizeof(struct xgene_msi_group),
> +                                       GFP_KERNEL);
> +       if (!xgene_msi->msi_groups)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> +{
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       struct xgene_msi_group *msi_groups;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int virq;
> +       int msir_index, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp;
> +
> +       chained_irq_enter(chip, desc);
> +
> +       msi_groups = irq_desc_get_handler_data(desc);
> +       xgene_msi = msi_groups->msi;
> +       msi_grp = msi_groups->msi_grp;
> +
> +       /*
> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> +        * If bit x of this register is set (x is 0..7), one or more interupts
> +        * corresponding to MSInIRx is set.
> +        */
> +       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               /*
> +                * Calculate MSInIRx address to read to check for interrupts
> +                * (refer to termination address and data assignment
> +                * described in xgene_compose_msi_msg function)
> +                */
> +               msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       /*
> +                        * Calculate MSI vector number (refer to the termination
> +                        * address and data assignment described in
> +                        * xgene_compose_msi_msg function)
> +                        */
> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> +                                NR_HW_IRQS) + msi_grp;
> +                       /*
> +                        * As we have multiple hw_irq that maps to single MSI,
> +                        * always look up the virq using the hw_irq as seen from
> +                        * CPU0
> +                        */
> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);

hw_irq = hwirq_to_canonical_hwirq(hw_irq);

> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> +                       WARN_ON(!virq);
> +                       if (virq != 0)
> +                               generic_handle_irq(virq);
> +                       msir_val &= ~(1 << intr_index);
> +               }
> +               grp_select &= ~(1 << msir_index);
> +
> +               if (!grp_select) {
> +                       /*
> +                        * We handled all interrupts happened in this group,
> +                        * resample this group MSI_INTx register in case
> +                        * something else has been made pending in the meantime
> +                        */
> +                       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
> +               }
> +       }
> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               virq = msi->msi_groups[i].gic_irq;
> +               if (virq != 0) {
> +                       irq_set_chained_handler(virq, NULL);
> +                       irq_set_handler_data(virq, NULL);
> +               }
> +       }
> +       kfree(msi->msi_groups);
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);
> +
> +       return 0;
> +}
> +
> +static int xgene_msi_hwirq_alloc(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       struct xgene_msi_group *msi_group;
> +       cpumask_var_t mask;
> +       int i;
> +       int err;
> +
> +       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
> +               msi_group = &msi->msi_groups[i];
> +               if (!msi_group->gic_irq)
> +                       continue;
> +
> +               irq_set_chained_handler(msi_group->gic_irq,
> +                                       xgene_msi_isr);
> +               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
> +               if (err) {
> +                       pr_err("failed to register GIC IRQ handler\n");
> +                       return -EINVAL;
> +               }
> +               /*
> +                * Statically allocate MSI GIC IRQs to each CPU core
> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> +                * to each core.
> +                */
> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +                       cpumask_clear(mask);
> +                       cpumask_set_cpu(cpu, mask);
> +                       err = irq_set_affinity(msi_group->gic_irq, mask);
> +                       if (err)
> +                               pr_err("failed to set affinity for GIC IRQ");
> +                       free_cpumask_var(mask);
> +               } else {
> +                       pr_err("failed to alloc CPU mask for affinity\n");
> +                       err = -EINVAL;
> +               }
> +
> +               if (err) {
> +                       irq_set_chained_handler(msi_group->gic_irq, NULL);
> +                       irq_set_handler_data(msi_group->gic_irq, NULL);
> +                       return -EINVAL;

			return err;

> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_hwirq_free(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       struct xgene_msi_group *msi_group;
> +       int i;
> +
> +       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
> +               msi_group = &msi->msi_groups[i];
> +               if (!msi_group->gic_irq)
> +                       continue;
> +
> +               irq_set_chained_handler(msi_group->gic_irq, NULL);
> +               irq_set_handler_data(msi_group->gic_irq, NULL);
> +       }
> +}
> +
> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
> +                                 unsigned long action, void *hcpu)
> +{
> +       unsigned cpu = (unsigned long)hcpu;
> +
> +       switch (action) {
> +       case CPU_ONLINE:
> +       case CPU_ONLINE_FROZEN:
> +               xgene_msi_hwirq_alloc(cpu);
> +               break;
> +       case CPU_DEAD:
> +       case CPU_DEAD_FROZEN:
> +               xgene_msi_hwirq_free(cpu);
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       return NOTIFY_OK;
> +}
> +
> +static struct notifier_block xgene_msi_cpu_notifier = {
> +       .notifier_call = xgene_msi_cpu_callback,
> +};
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi"},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int cpu;
> +       int virt_msir;
> +       u32 msi_val, msi_idx;
> +
> +       xgene_msi = &xgene_msi_ctrl;
> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +       xgene_msi->msi_addr = res->start;
> +
> +       xgene_msi->num_cpus = num_possible_cpus();
> +
> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               virt_msir = platform_get_irq(pdev, irq_index);
> +               if (virt_msir < 0) {
> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                               irq_index);
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
> +               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
> +               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
> +       }
> +
> +       /*
> +        * MSInIRx registers are read-to-clear, before registering interrupt
> +        * handlers, reading all of them to clear all spurious interrupts
> +        * that may occur before the driver is probed.
> +        */
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
> +                       msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
> +                                                   msi_idx);
> +               /* Read MSIINTn to confirm */
> +               msi_val = xgene_msi_int_read(xgene_msi, irq_index);
> +               if (msi_val) {
> +                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +       }
> +
> +       cpu_notifier_register_begin();
> +
> +       for_each_online_cpu(cpu)
> +               if (xgene_msi_hwirq_alloc(cpu)) {
> +                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
> +                       cpu_notifier_register_done();
> +                       goto error;
> +               }
> +
> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
> +               cpu_notifier_register_done();
> +               goto error;
> +       }
> +
> +       cpu_notifier_register_done();
> +
> +       xgene_msi->mchip.of_node = pdev->dev.of_node;
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error_notifier;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +
> +error_notifier:
> +       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +error:
> +       xgene_msi_remove(pdev);
> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

Once you've fixed the couple of nits above, you can add my

Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>

You should also CC the irqchip maintainers (Thomas and Jason), as I'd
very much like to have their input on this as well. A few "Tested-by"
wouldn't hurt either.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny.

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

* Re: [PATCH v8 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-25 11:52                                                         ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-05-25 11:52 UTC (permalink / raw)
  To: Duc Dang
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Thomas Gleixner, Jason Cooper

On Fri, 22 May 2015 19:41:10 +0100
Duc Dang <dhdang@apm.com> wrote:

> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports.
> 
> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
> driver supports is reduced to 256.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |  10 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 573 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 605 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..0932118 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,21 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +       help
> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
> +         This MSI driver will provide MSI support for 5 PCIe ports of
> +         APM X-Gene v1 SoC
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..0c6823d
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,573 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi_group {
> +       struct xgene_msi                *msi;
> +       int                             gic_irq;
> +       u32                             msi_grp;
> +};
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       struct xgene_msi_group          *msi_groups;
> +       int                             num_cpus;
> +};
> +
> +/* Global data */
> +static struct xgene_msi xgene_msi_ctrl;
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + * MSIINT0                     base_addr + 0x800000
> + * MSIINT1                     base_addr + 0x810000
> + * ...                         ...
> + * MSIINTF                     base_addr + 0x8F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
> + * the MSI pending status caused by 1 of its 8 index registers.
> + */
> +
> +/* MSInIRx read helper */
> +static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
> +                                   u32 msi_grp, u32 msir_idx)
> +{
> +       return readl_relaxed(msi->msi_regs + MSI_IR0 +
> +                             (msi_grp << 19) + (msir_idx << 16));
> +}
> +
> +/* MSIINTn read helper */
> +static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
> +{
> +       return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> +}
> +
> +/* With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);

Create a hwirq_to_reg_set helper.

> +       u32 group = data->hwirq % NR_HW_IRQS;

Create a hwirq_to_group helper.

> +
> +       msg->address_hi = upper_32_bits(msi->msi_addr +
> +                                       (((8 * group) + reg_set) << 16));
> +       msg->address_lo = lower_32_bits(msi->msi_addr +
> +                                       (((8 * group) + reg_set) << 16));

Rule of thumb: if you write something complicated twice, you've written
one time too many:

u64 target_addr = msi->msi_addr + ((8 * group) + reg_set) << 16));

msg->address_hi = upper_32_bits(target_addr);
msg->address_lo = lower_32_bits(target_addr);

It doesn't hurt to write something readable.

> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +
> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;

Create a hwirq_to_cpu helper.

> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       /* Update MSI number to target the new CPU */
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> +
> +       return IRQ_SET_MASK_OK;
> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +       struct xgene_msi *msi = domain->host_data;
> +       int msi_irq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
> +                                            msi->num_cpus, 0);
> +       if (msi_irq < NR_MSI_VEC)
> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq,
> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> +                           handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +       u32 hwirq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);

Create hwirq_to_canonical_hwirq helper (hwirq - hwirq_to_cpu(hwirq)).

> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> +                                           &msi_domain_ops, msi);
> +       if (!msi->domain)
> +               return -ENOMEM;
> +
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
> +                                       sizeof(struct xgene_msi_group),
> +                                       GFP_KERNEL);
> +       if (!xgene_msi->msi_groups)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> +{
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       struct xgene_msi_group *msi_groups;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int virq;
> +       int msir_index, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp;
> +
> +       chained_irq_enter(chip, desc);
> +
> +       msi_groups = irq_desc_get_handler_data(desc);
> +       xgene_msi = msi_groups->msi;
> +       msi_grp = msi_groups->msi_grp;
> +
> +       /*
> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> +        * If bit x of this register is set (x is 0..7), one or more interupts
> +        * corresponding to MSInIRx is set.
> +        */
> +       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               /*
> +                * Calculate MSInIRx address to read to check for interrupts
> +                * (refer to termination address and data assignment
> +                * described in xgene_compose_msi_msg function)
> +                */
> +               msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       /*
> +                        * Calculate MSI vector number (refer to the termination
> +                        * address and data assignment described in
> +                        * xgene_compose_msi_msg function)
> +                        */
> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> +                                NR_HW_IRQS) + msi_grp;
> +                       /*
> +                        * As we have multiple hw_irq that maps to single MSI,
> +                        * always look up the virq using the hw_irq as seen from
> +                        * CPU0
> +                        */
> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);

hw_irq = hwirq_to_canonical_hwirq(hw_irq);

> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> +                       WARN_ON(!virq);
> +                       if (virq != 0)
> +                               generic_handle_irq(virq);
> +                       msir_val &= ~(1 << intr_index);
> +               }
> +               grp_select &= ~(1 << msir_index);
> +
> +               if (!grp_select) {
> +                       /*
> +                        * We handled all interrupts happened in this group,
> +                        * resample this group MSI_INTx register in case
> +                        * something else has been made pending in the meantime
> +                        */
> +                       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
> +               }
> +       }
> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               virq = msi->msi_groups[i].gic_irq;
> +               if (virq != 0) {
> +                       irq_set_chained_handler(virq, NULL);
> +                       irq_set_handler_data(virq, NULL);
> +               }
> +       }
> +       kfree(msi->msi_groups);
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);
> +
> +       return 0;
> +}
> +
> +static int xgene_msi_hwirq_alloc(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       struct xgene_msi_group *msi_group;
> +       cpumask_var_t mask;
> +       int i;
> +       int err;
> +
> +       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
> +               msi_group = &msi->msi_groups[i];
> +               if (!msi_group->gic_irq)
> +                       continue;
> +
> +               irq_set_chained_handler(msi_group->gic_irq,
> +                                       xgene_msi_isr);
> +               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
> +               if (err) {
> +                       pr_err("failed to register GIC IRQ handler\n");
> +                       return -EINVAL;
> +               }
> +               /*
> +                * Statically allocate MSI GIC IRQs to each CPU core
> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> +                * to each core.
> +                */
> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +                       cpumask_clear(mask);
> +                       cpumask_set_cpu(cpu, mask);
> +                       err = irq_set_affinity(msi_group->gic_irq, mask);
> +                       if (err)
> +                               pr_err("failed to set affinity for GIC IRQ");
> +                       free_cpumask_var(mask);
> +               } else {
> +                       pr_err("failed to alloc CPU mask for affinity\n");
> +                       err = -EINVAL;
> +               }
> +
> +               if (err) {
> +                       irq_set_chained_handler(msi_group->gic_irq, NULL);
> +                       irq_set_handler_data(msi_group->gic_irq, NULL);
> +                       return -EINVAL;

			return err;

> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_hwirq_free(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       struct xgene_msi_group *msi_group;
> +       int i;
> +
> +       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
> +               msi_group = &msi->msi_groups[i];
> +               if (!msi_group->gic_irq)
> +                       continue;
> +
> +               irq_set_chained_handler(msi_group->gic_irq, NULL);
> +               irq_set_handler_data(msi_group->gic_irq, NULL);
> +       }
> +}
> +
> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
> +                                 unsigned long action, void *hcpu)
> +{
> +       unsigned cpu = (unsigned long)hcpu;
> +
> +       switch (action) {
> +       case CPU_ONLINE:
> +       case CPU_ONLINE_FROZEN:
> +               xgene_msi_hwirq_alloc(cpu);
> +               break;
> +       case CPU_DEAD:
> +       case CPU_DEAD_FROZEN:
> +               xgene_msi_hwirq_free(cpu);
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       return NOTIFY_OK;
> +}
> +
> +static struct notifier_block xgene_msi_cpu_notifier = {
> +       .notifier_call = xgene_msi_cpu_callback,
> +};
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi"},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int cpu;
> +       int virt_msir;
> +       u32 msi_val, msi_idx;
> +
> +       xgene_msi = &xgene_msi_ctrl;
> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +       xgene_msi->msi_addr = res->start;
> +
> +       xgene_msi->num_cpus = num_possible_cpus();
> +
> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               virt_msir = platform_get_irq(pdev, irq_index);
> +               if (virt_msir < 0) {
> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                               irq_index);
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
> +               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
> +               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
> +       }
> +
> +       /*
> +        * MSInIRx registers are read-to-clear, before registering interrupt
> +        * handlers, reading all of them to clear all spurious interrupts
> +        * that may occur before the driver is probed.
> +        */
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
> +                       msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
> +                                                   msi_idx);
> +               /* Read MSIINTn to confirm */
> +               msi_val = xgene_msi_int_read(xgene_msi, irq_index);
> +               if (msi_val) {
> +                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +       }
> +
> +       cpu_notifier_register_begin();
> +
> +       for_each_online_cpu(cpu)
> +               if (xgene_msi_hwirq_alloc(cpu)) {
> +                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
> +                       cpu_notifier_register_done();
> +                       goto error;
> +               }
> +
> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
> +               cpu_notifier_register_done();
> +               goto error;
> +       }
> +
> +       cpu_notifier_register_done();
> +
> +       xgene_msi->mchip.of_node = pdev->dev.of_node;
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error_notifier;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +
> +error_notifier:
> +       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +error:
> +       xgene_msi_remove(pdev);
> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

Once you've fixed the couple of nits above, you can add my

Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>

You should also CC the irqchip maintainers (Thomas and Jason), as I'd
very much like to have their input on this as well. A few "Tested-by"
wouldn't hurt either.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny.

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

* [PATCH v8 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-25 11:52                                                         ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-05-25 11:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, 22 May 2015 19:41:10 +0100
Duc Dang <dhdang@apm.com> wrote:

> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
> and shared across all 5 PCIe ports.
> 
> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
> driver supports is reduced to 256.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> ---
>  drivers/pci/host/Kconfig         |  10 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 573 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 605 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 7b892a9..0932118 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,21 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
> +       select PCI_XGENE_MSI if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +       help
> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
> +         This MSI driver will provide MSI support for 5 PCIe ports of
> +         APM X-Gene v1 SoC
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index e61d91c..f39bde3 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..0c6823d
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,573 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi_group {
> +       struct xgene_msi                *msi;
> +       int                             gic_irq;
> +       u32                             msi_grp;
> +};
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       struct xgene_msi_group          *msi_groups;
> +       int                             num_cpus;
> +};
> +
> +/* Global data */
> +static struct xgene_msi xgene_msi_ctrl;
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + * MSIINT0                     base_addr + 0x800000
> + * MSIINT1                     base_addr + 0x810000
> + * ...                         ...
> + * MSIINTF                     base_addr + 0x8F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
> + * the MSI pending status caused by 1 of its 8 index registers.
> + */
> +
> +/* MSInIRx read helper */
> +static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
> +                                   u32 msi_grp, u32 msir_idx)
> +{
> +       return readl_relaxed(msi->msi_regs + MSI_IR0 +
> +                             (msi_grp << 19) + (msir_idx << 16));
> +}
> +
> +/* MSIINTn read helper */
> +static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
> +{
> +       return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> +}
> +
> +/* With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);

Create a hwirq_to_reg_set helper.

> +       u32 group = data->hwirq % NR_HW_IRQS;

Create a hwirq_to_group helper.

> +
> +       msg->address_hi = upper_32_bits(msi->msi_addr +
> +                                       (((8 * group) + reg_set) << 16));
> +       msg->address_lo = lower_32_bits(msi->msi_addr +
> +                                       (((8 * group) + reg_set) << 16));

Rule of thumb: if you write something complicated twice, you've written
one time too many:

u64 target_addr = msi->msi_addr + ((8 * group) + reg_set) << 16));

msg->address_hi = upper_32_bits(target_addr);
msg->address_lo = lower_32_bits(target_addr);

It doesn't hurt to write something readable.

> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +
> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;

Create a hwirq_to_cpu helper.

> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       /* Update MSI number to target the new CPU */
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
> +
> +       return IRQ_SET_MASK_OK;
> +}
> +
> +static struct irq_chip xgene_msi_bottom_irq_chip = {
> +       .name                   = "MSI",
> +       .irq_set_affinity       = xgene_msi_set_affinity,
> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
> +};
> +
> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                                 unsigned int nr_irqs, void *args)
> +{
> +       struct xgene_msi *msi = domain->host_data;
> +       int msi_irq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
> +                                            msi->num_cpus, 0);
> +       if (msi_irq < NR_MSI_VEC)
> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
> +       else
> +               msi_irq = -ENOSPC;
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       if (msi_irq < 0)
> +               return msi_irq;
> +
> +       irq_domain_set_info(domain, virq, msi_irq,
> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
> +                           handle_simple_irq, NULL, NULL);
> +       set_irq_flags(virq, IRQF_VALID);
> +
> +       return 0;
> +}
> +
> +static void xgene_irq_domain_free(struct irq_domain *domain,
> +                                 unsigned int virq, unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
> +       u32 hwirq;
> +
> +       mutex_lock(&msi->bitmap_lock);
> +
> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);

Create hwirq_to_canonical_hwirq helper (hwirq - hwirq_to_cpu(hwirq)).

> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
> +
> +       mutex_unlock(&msi->bitmap_lock);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops msi_domain_ops = {
> +       .alloc  = xgene_irq_domain_alloc,
> +       .free   = xgene_irq_domain_free,
> +};
> +
> +static int xgene_allocate_domains(struct xgene_msi *msi)
> +{
> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> +                                           &msi_domain_ops, msi);
> +       if (!msi->domain)
> +               return -ENOMEM;
> +
> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
> +                                                     &xgene_msi_domain_info,
> +                                                     msi->domain);
> +
> +       if (!msi->mchip.domain) {
> +               irq_domain_remove(msi->domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_free_domains(struct xgene_msi *msi)
> +{
> +       if (msi->mchip.domain)
> +               irq_domain_remove(msi->mchip.domain);
> +       if (msi->domain)
> +               irq_domain_remove(msi->domain);
> +}
> +
> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
> +{
> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
> +
> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
> +       if (!xgene_msi->bitmap)
> +               return -ENOMEM;
> +
> +       mutex_init(&xgene_msi->bitmap_lock);
> +
> +       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
> +                                       sizeof(struct xgene_msi_group),
> +                                       GFP_KERNEL);
> +       if (!xgene_msi->msi_groups)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
> +{
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       struct xgene_msi_group *msi_groups;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int virq;
> +       int msir_index, msir_val, hw_irq;
> +       u32 intr_index, grp_select, msi_grp;
> +
> +       chained_irq_enter(chip, desc);
> +
> +       msi_groups = irq_desc_get_handler_data(desc);
> +       xgene_msi = msi_groups->msi;
> +       msi_grp = msi_groups->msi_grp;
> +
> +       /*
> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
> +        * If bit x of this register is set (x is 0..7), one or more interupts
> +        * corresponding to MSInIRx is set.
> +        */
> +       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
> +       while (grp_select) {
> +               msir_index = ffs(grp_select) - 1;
> +               /*
> +                * Calculate MSInIRx address to read to check for interrupts
> +                * (refer to termination address and data assignment
> +                * described in xgene_compose_msi_msg function)
> +                */
> +               msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
> +               while (msir_val) {
> +                       intr_index = ffs(msir_val) - 1;
> +                       /*
> +                        * Calculate MSI vector number (refer to the termination
> +                        * address and data assignment described in
> +                        * xgene_compose_msi_msg function)
> +                        */
> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
> +                                NR_HW_IRQS) + msi_grp;
> +                       /*
> +                        * As we have multiple hw_irq that maps to single MSI,
> +                        * always look up the virq using the hw_irq as seen from
> +                        * CPU0
> +                        */
> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);

hw_irq = hwirq_to_canonical_hwirq(hw_irq);

> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
> +                       WARN_ON(!virq);
> +                       if (virq != 0)
> +                               generic_handle_irq(virq);
> +                       msir_val &= ~(1 << intr_index);
> +               }
> +               grp_select &= ~(1 << msir_index);
> +
> +               if (!grp_select) {
> +                       /*
> +                        * We handled all interrupts happened in this group,
> +                        * resample this group MSI_INTx register in case
> +                        * something else has been made pending in the meantime
> +                        */
> +                       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
> +               }
> +       }
> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static int xgene_msi_remove(struct platform_device *pdev)
> +{
> +       int virq, i;
> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
> +
> +       for (i = 0; i < NR_HW_IRQS; i++) {
> +               virq = msi->msi_groups[i].gic_irq;
> +               if (virq != 0) {
> +                       irq_set_chained_handler(virq, NULL);
> +                       irq_set_handler_data(virq, NULL);
> +               }
> +       }
> +       kfree(msi->msi_groups);
> +
> +       kfree(msi->bitmap);
> +       msi->bitmap = NULL;
> +
> +       xgene_free_domains(msi);
> +
> +       return 0;
> +}
> +
> +static int xgene_msi_hwirq_alloc(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       struct xgene_msi_group *msi_group;
> +       cpumask_var_t mask;
> +       int i;
> +       int err;
> +
> +       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
> +               msi_group = &msi->msi_groups[i];
> +               if (!msi_group->gic_irq)
> +                       continue;
> +
> +               irq_set_chained_handler(msi_group->gic_irq,
> +                                       xgene_msi_isr);
> +               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
> +               if (err) {
> +                       pr_err("failed to register GIC IRQ handler\n");
> +                       return -EINVAL;
> +               }
> +               /*
> +                * Statically allocate MSI GIC IRQs to each CPU core
> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
> +                * to each core.
> +                */
> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
> +                       cpumask_clear(mask);
> +                       cpumask_set_cpu(cpu, mask);
> +                       err = irq_set_affinity(msi_group->gic_irq, mask);
> +                       if (err)
> +                               pr_err("failed to set affinity for GIC IRQ");
> +                       free_cpumask_var(mask);
> +               } else {
> +                       pr_err("failed to alloc CPU mask for affinity\n");
> +                       err = -EINVAL;
> +               }
> +
> +               if (err) {
> +                       irq_set_chained_handler(msi_group->gic_irq, NULL);
> +                       irq_set_handler_data(msi_group->gic_irq, NULL);
> +                       return -EINVAL;

			return err;

> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static void xgene_msi_hwirq_free(unsigned int cpu)
> +{
> +       struct xgene_msi *msi = &xgene_msi_ctrl;
> +       struct xgene_msi_group *msi_group;
> +       int i;
> +
> +       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
> +               msi_group = &msi->msi_groups[i];
> +               if (!msi_group->gic_irq)
> +                       continue;
> +
> +               irq_set_chained_handler(msi_group->gic_irq, NULL);
> +               irq_set_handler_data(msi_group->gic_irq, NULL);
> +       }
> +}
> +
> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
> +                                 unsigned long action, void *hcpu)
> +{
> +       unsigned cpu = (unsigned long)hcpu;
> +
> +       switch (action) {
> +       case CPU_ONLINE:
> +       case CPU_ONLINE_FROZEN:
> +               xgene_msi_hwirq_alloc(cpu);
> +               break;
> +       case CPU_DEAD:
> +       case CPU_DEAD_FROZEN:
> +               xgene_msi_hwirq_free(cpu);
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       return NOTIFY_OK;
> +}
> +
> +static struct notifier_block xgene_msi_cpu_notifier = {
> +       .notifier_call = xgene_msi_cpu_callback,
> +};
> +
> +static const struct of_device_id xgene_msi_match_table[] = {
> +       {.compatible = "apm,xgene1-msi"},
> +       {},
> +};
> +
> +static int xgene_msi_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int rc, irq_index;
> +       struct xgene_msi *xgene_msi;
> +       unsigned int cpu;
> +       int virt_msir;
> +       u32 msi_val, msi_idx;
> +
> +       xgene_msi = &xgene_msi_ctrl;
> +
> +       platform_set_drvdata(pdev, xgene_msi);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(xgene_msi->msi_regs)) {
> +               dev_err(&pdev->dev, "no reg space\n");
> +               rc = -EINVAL;
> +               goto error;
> +       }
> +       xgene_msi->msi_addr = res->start;
> +
> +       xgene_msi->num_cpus = num_possible_cpus();
> +
> +       rc = xgene_msi_init_allocator(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
> +               goto error;
> +       }
> +
> +       rc = xgene_allocate_domains(xgene_msi);
> +       if (rc) {
> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
> +               goto error;
> +       }
> +
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               virt_msir = platform_get_irq(pdev, irq_index);
> +               if (virt_msir < 0) {
> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
> +                               irq_index);
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
> +               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
> +               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
> +       }
> +
> +       /*
> +        * MSInIRx registers are read-to-clear, before registering interrupt
> +        * handlers, reading all of them to clear all spurious interrupts
> +        * that may occur before the driver is probed.
> +        */
> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
> +               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
> +                       msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
> +                                                   msi_idx);
> +               /* Read MSIINTn to confirm */
> +               msi_val = xgene_msi_int_read(xgene_msi, irq_index);
> +               if (msi_val) {
> +                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
> +                       rc = -EINVAL;
> +                       goto error;
> +               }
> +       }
> +
> +       cpu_notifier_register_begin();
> +
> +       for_each_online_cpu(cpu)
> +               if (xgene_msi_hwirq_alloc(cpu)) {
> +                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
> +                       cpu_notifier_register_done();
> +                       goto error;
> +               }
> +
> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
> +               cpu_notifier_register_done();
> +               goto error;
> +       }
> +
> +       cpu_notifier_register_done();
> +
> +       xgene_msi->mchip.of_node = pdev->dev.of_node;
> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
> +       if (rc) {
> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
> +               goto error_notifier;
> +       }
> +
> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
> +
> +       return 0;
> +
> +error_notifier:
> +       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
> +error:
> +       xgene_msi_remove(pdev);
> +       return rc;
> +}
> +
> +static struct platform_driver xgene_msi_driver = {
> +       .driver = {
> +               .name = "xgene-msi",
> +               .owner = THIS_MODULE,
> +               .of_match_table = xgene_msi_match_table,
> +       },
> +       .probe = xgene_msi_probe,
> +       .remove = xgene_msi_remove,
> +};
> +
> +static int __init xgene_pcie_msi_init(void)
> +{
> +       return platform_driver_register(&xgene_msi_driver);
> +}
> +subsys_initcall(xgene_pcie_msi_init);
> +
> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
> index 22751ed..3e6faa1 100644
> --- a/drivers/pci/host/pci-xgene.c
> +++ b/drivers/pci/host/pci-xgene.c
> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>         return 0;
>  }
> 
> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
> +{
> +       struct device_node *msi_node;
> +
> +       msi_node = of_parse_phandle(bus->dev.of_node,
> +                                       "msi-parent", 0);
> +       if (!msi_node)
> +               return -ENODEV;
> +
> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
> +       if (bus->msi)
> +               bus->msi->dev = &bus->dev;
> +       else
> +               return -ENODEV;
> +       return 0;
> +}
> +
>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>  {
>         struct device_node *dn = pdev->dev.of_node;
> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>         if (!bus)
>                 return -ENOMEM;
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI))
> +               if (xgene_pcie_msi_enable(bus))
> +                       dev_info(port->dev, "failed to enable MSI\n");
> +
>         pci_scan_child_bus(bus);
>         pci_assign_unassigned_bus_resources(bus);
>         pci_bus_add_devices(bus);
> --
> 1.9.1
> 

Once you've fixed the couple of nits above, you can add my

Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>

You should also CC the irqchip maintainers (Thomas and Jason), as I'd
very much like to have their input on this as well. A few "Tested-by"
wouldn't hurt either.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny.

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

* [PATCH v9 0/4]PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-05-25 11:52                                                         ` Marc Zyngier
@ 2015-05-27 18:27                                                           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-27 18:27 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau,
	Marc Zyngier, Thomas Gleixner, Jason Cooper, Mark Rutland
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI
vectors this driver supports is reduced to 256 to maintain the correct set_affinity
behavior for each MSI.

v9 changes:
        1. Add more helpers for manipulating MSI messages
        2. Improve code readability
	3. Make PCI_XGENE_MSI selectable

v8 changes:
        1. Add helper to read MSI registers
        2. Resample interrupt status of the same group after handling interrupts
           in that group
        3. Error handling in case fail to allocate CPU mask for affinity setting

v7 changes:
        1. Add more error handling cases
        2. Clear spurious interrupts that may happen during driver probe
        3. Not using free_irq for chained irqs
        4. Improve GIC IRQ number look up in chained handler

v6 changes:
        1. Correctly allocate MSI with bitmap_find_next_zero_area
        2. Add notifier for CPU hotplug case
        3. Driver clean up
        4. Add more detailed information into device tree binding document
           and move the device tree binding patch before the driver/dts patch

v5 changes:
        1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs
        for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer
        them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1
        supports is reduced to 256.

v4 changes:
        1. Remove affinity setting for each MSI
        2. Add description about register layout, MSI termination address and data
        3. Correct total number of MSI vectors to 2048
        4. Clean up error messages
        5. Remove unused module code

v3 changes:
        1. Implement MSI support using PCI MSI IRQ domain
        2. Only use msi_controller to store IRQ domain

v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
 drivers/pci/host/Kconfig                           |  10 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 595 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 730 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1


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

* [PATCH v9 0/4]PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-05-27 18:27                                                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-27 18:27 UTC (permalink / raw)
  To: linux-arm-kernel

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI
vectors this driver supports is reduced to 256 to maintain the correct set_affinity
behavior for each MSI.

v9 changes:
        1. Add more helpers for manipulating MSI messages
        2. Improve code readability
	3. Make PCI_XGENE_MSI selectable

v8 changes:
        1. Add helper to read MSI registers
        2. Resample interrupt status of the same group after handling interrupts
           in that group
        3. Error handling in case fail to allocate CPU mask for affinity setting

v7 changes:
        1. Add more error handling cases
        2. Clear spurious interrupts that may happen during driver probe
        3. Not using free_irq for chained irqs
        4. Improve GIC IRQ number look up in chained handler

v6 changes:
        1. Correctly allocate MSI with bitmap_find_next_zero_area
        2. Add notifier for CPU hotplug case
        3. Driver clean up
        4. Add more detailed information into device tree binding document
           and move the device tree binding patch before the driver/dts patch

v5 changes:
        1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs
        for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer
        them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1
        supports is reduced to 256.

v4 changes:
        1. Remove affinity setting for each MSI
        2. Add description about register layout, MSI termination address and data
        3. Correct total number of MSI vectors to 2048
        4. Clean up error messages
        5. Remove unused module code

v3 changes:
        1. Implement MSI support using PCI MSI IRQ domain
        2. Only use msi_controller to store IRQ domain

v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
 drivers/pci/host/Kconfig                           |  10 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 595 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 730 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1

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

* [PATCH v9 1/4] documentation: dts: Add the device tree binding for APM  X-Gene v1 PCIe MSI device tree node
  2015-05-27 18:27                                                           ` Duc Dang
@ 2015-05-27 18:27                                                           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-27 18:27 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau,
	Marc Zyngier, Thomas Gleixner, Jason Cooper, Mark Rutland
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

X-Gene v1 PCIe MSI controller block provides PCIE MSI functionality for
5 X-Gene v1 PCIE ports

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..36d881c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+	      X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+	      interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi@79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = 	<0x0 0x10 0x4>
+				<0x0 0x11 0x4>
+				<0x0 0x12 0x4>
+				<0x0 0x13 0x4>
+				<0x0 0x14 0x4>
+				<0x0 0x15 0x4>
+				<0x0 0x16 0x4>
+				<0x0 0x17 0x4>
+				<0x0 0x18 0x4>
+				<0x0 0x19 0x4>
+				<0x0 0x1a 0x4>
+				<0x0 0x1b 0x4>
+				<0x0 0x1c 0x4>
+				<0x0 0x1d 0x4>
+				<0x0 0x1e 0x4>
+				<0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie@1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1


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

* [PATCH v9 1/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
@ 2015-05-27 18:27                                                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-27 18:27 UTC (permalink / raw)
  To: linux-arm-kernel

X-Gene v1 PCIe MSI controller block provides PCIE MSI functionality for
5 X-Gene v1 PCIE ports

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..36d881c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+	      X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+	      interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = 	<0x0 0x10 0x4>
+				<0x0 0x11 0x4>
+				<0x0 0x12 0x4>
+				<0x0 0x13 0x4>
+				<0x0 0x14 0x4>
+				<0x0 0x15 0x4>
+				<0x0 0x16 0x4>
+				<0x0 0x17 0x4>
+				<0x0 0x18 0x4>
+				<0x0 0x19 0x4>
+				<0x0 0x1a 0x4>
+				<0x0 0x1b 0x4>
+				<0x0 0x1c 0x4>
+				<0x0 0x1d 0x4>
+				<0x0 0x1e 0x4>
+				<0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1

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

* [PATCH v9 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-05-27 18:27                                                           ` Duc Dang
@ 2015-05-27 18:27                                                           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-27 18:27 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau,
	Marc Zyngier, Thomas Gleixner, Jason Cooper, Mark Rutland
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

APM X-Gene v1 SoC supports its own implementation of MSI, which is not
compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
This MSI block supports 2048 MSI termination ports coalesced into 16
physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support
set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically
allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI
interrupt to target CPU, MSI vector is moved around these HW IRQs lines.
With this approach, the total MSI vectors this driver supports is reduced
to 256.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 drivers/pci/host/Kconfig         |  10 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 595 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 627 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 1dfb567..a93fda8 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,21 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+	default y
+	help
+	  Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
+	  This MSI driver will provide MSI support for 5 PCIe ports of
+	  APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index f733b4e..1957431 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,6 +11,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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
 obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..461cb11
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,595 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0			0x000000
+#define MSI_INT0		0x800000
+#define IDX_PER_GROUP		8
+#define IRQS_PER_IDX		16
+#define NR_HW_IRQS		16
+#define NR_MSI_VEC		(IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi_group {
+	struct xgene_msi		*msi;
+	int				gic_irq;
+	u32				msi_grp;
+};
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	u64				msi_addr;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	struct xgene_msi_group		*msi_groups;
+	int				num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0			base_addr
+ * MSI0IR1			base_addr +  0x10000
+ * ...				...
+ * MSI0IR6			base_addr +  0x60000
+ * MSI0IR7			base_addr +  0x70000
+ * MSI1IR0			base_addr +  0x80000
+ * MSI1IR1			base_addr +  0x90000
+ * ...				...
+ * MSI1IR7			base_addr +  0xF0000
+ * MSI2IR0			base_addr + 0x100000
+ * ...				...
+ * MSIFIR0			base_addr + 0x780000
+ * MSIFIR1			base_addr + 0x790000
+ * ...				...
+ * MSIFIR7			base_addr + 0x7F0000
+ * MSIINT0			base_addr + 0x800000
+ * MSIINT1			base_addr + 0x810000
+ * ...				...
+ * MSIINTF			base_addr + 0x8F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
+ * the MSI pending status caused by 1 of its 8 index registers.
+ */
+
+/* MSInIRx read helper */
+static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
+				    u32 msi_grp, u32 msir_idx)
+{
+	return readl_relaxed(msi->msi_regs + MSI_IR0 +
+			      (msi_grp << 19) + (msir_idx << 16));
+}
+
+/* MSIINTn read helper */
+static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
+{
+	return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+}
+
+/* With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *		Group 0: 0-255
+ *		Group 1: 256-511
+ *		Group 2: 512-767
+ *		...
+ *		Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *	As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *		Group 0: 0-15
+ *		Group 1: 16-32
+ *		...
+ *		Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static inline u32 hwirq_to_reg_set(unsigned long hwirq)
+{
+	return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX));
+}
+
+static inline u32 hwirq_to_group(unsigned long hwirq)
+{
+	return (hwirq % NR_HW_IRQS);
+}
+
+static inline u32 hwirq_to_msi_data(unsigned long hwirq)
+{
+	return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX);
+}
+
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 reg_set = hwirq_to_reg_set(data->hwirq);
+	u32 group = hwirq_to_group(data->hwirq);
+	u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16);
+
+	msg->address_hi = upper_32_bits(target_addr);
+	msg->address_lo = lower_32_bits(target_addr);
+	msg->data = hwirq_to_msi_data(data->hwirq);
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static inline int hwirq_to_cpu(unsigned long hwirq)
+{
+	return ((hwirq % NR_HW_IRQS) % xgene_msi_ctrl.num_cpus);
+}
+
+static inline unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq)
+{
+	return (hwirq - (hwirq % xgene_msi_ctrl.num_cpus));
+}
+
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+				  const struct cpumask *mask, bool force)
+{
+	int target_cpu = cpumask_first(mask);
+	int curr_cpu;
+
+	curr_cpu = hwirq_to_cpu(irq_data->hwirq);
+	if (curr_cpu == target_cpu)
+		return IRQ_SET_MASK_OK_DONE;
+
+	/* Update MSI number to target the new CPU */
+	irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+
+	return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+	struct xgene_msi *msi = domain->host_data;
+	int msi_irq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+					     msi->num_cpus, 0);
+	if (msi_irq < NR_MSI_VEC)
+		bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+	else
+		msi_irq = -ENOSPC;
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq,
+			    &xgene_msi_bottom_irq_chip, domain->host_data,
+			    handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+	u32 hwirq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	hwirq = hwirq_to_canonical_hwirq(d->hwirq);
+	bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+					    &msi_domain_ops, msi);
+	if (!msi->domain)
+		return -ENOMEM;
+
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
+					sizeof(struct xgene_msi_group),
+					GFP_KERNEL);
+	if (!xgene_msi->msi_groups)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct xgene_msi_group *msi_groups;
+	struct xgene_msi *xgene_msi;
+	unsigned int virq;
+	int msir_index, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp;
+
+	chained_irq_enter(chip, desc);
+
+	msi_groups = irq_desc_get_handler_data(desc);
+	xgene_msi = msi_groups->msi;
+	msi_grp = msi_groups->msi_grp;
+
+	/*
+	 * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+	 * If bit x of this register is set (x is 0..7), one or more interupts
+	 * corresponding to MSInIRx is set.
+	 */
+	grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		/*
+		 * Calculate MSInIRx address to read to check for interrupts
+		 * (refer to termination address and data assignment
+		 * described in xgene_compose_msi_msg function)
+		 */
+		msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			/*
+			 * Calculate MSI vector number (refer to the termination
+			 * address and data assignment described in
+			 * xgene_compose_msi_msg function)
+			 */
+			hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+				 NR_HW_IRQS) + msi_grp;
+			/*
+			 * As we have multiple hw_irq that maps to single MSI,
+			 * always look up the virq using the hw_irq as seen from
+			 * CPU0
+			 */
+			hw_irq = hwirq_to_canonical_hwirq(hw_irq);
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			WARN_ON(!virq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+		}
+		grp_select &= ~(1 << msir_index);
+
+		if (!grp_select) {
+			/*
+			 * We handled all interrupts happened in this group,
+			 * resample this group MSI_INTx register in case
+			 * something else has been made pending in the meantime
+			 */
+			grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+		}
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		virq = msi->msi_groups[i].gic_irq;
+		if (virq != 0) {
+			irq_set_chained_handler(virq, NULL);
+			irq_set_handler_data(virq, NULL);
+		}
+	}
+	kfree(msi->msi_groups);
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static int xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	cpumask_var_t mask;
+	int i;
+	int err;
+
+	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq,
+					xgene_msi_isr);
+		err = irq_set_handler_data(msi_group->gic_irq, msi_group);
+		if (err) {
+			pr_err("failed to register GIC IRQ handler\n");
+			return -EINVAL;
+		}
+		/*
+		 * Statically allocate MSI GIC IRQs to each CPU core
+		 * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+		 * to each core.
+		 */
+		if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+			cpumask_clear(mask);
+			cpumask_set_cpu(cpu, mask);
+			err = irq_set_affinity(msi_group->gic_irq, mask);
+			if (err)
+				pr_err("failed to set affinity for GIC IRQ");
+			free_cpumask_var(mask);
+		} else {
+			pr_err("failed to alloc CPU mask for affinity\n");
+			err = -EINVAL;
+		}
+
+		if (err) {
+			irq_set_chained_handler(msi_group->gic_irq, NULL);
+			irq_set_handler_data(msi_group->gic_irq, NULL);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	int i;
+
+	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq, NULL);
+		irq_set_handler_data(msi_group->gic_irq, NULL);
+	}
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+				  unsigned long action, void *hcpu)
+{
+	unsigned cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		xgene_msi_hwirq_alloc(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		xgene_msi_hwirq_free(cpu);
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+	.notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi"},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct xgene_msi *xgene_msi;
+	unsigned int cpu;
+	int virt_msir;
+	u32 msi_val, msi_idx;
+
+	xgene_msi = &xgene_msi_ctrl;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	xgene_msi->msi_addr = res->start;
+
+	xgene_msi->num_cpus = num_possible_cpus();
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		virt_msir = platform_get_irq(pdev, irq_index);
+		if (virt_msir < 0) {
+			dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+				irq_index);
+			rc = -EINVAL;
+			goto error;
+		}
+		xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
+		xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
+		xgene_msi->msi_groups[irq_index].msi = xgene_msi;
+	}
+
+	/*
+	 * MSInIRx registers are read-to-clear, before registering interrupt
+	 * handlers, reading all of them to clear all spurious interrupts
+	 * that may occur before the driver is probed.
+	 */
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
+			msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
+						    msi_idx);
+		/* Read MSIINTn to confirm */
+		msi_val = xgene_msi_int_read(xgene_msi, irq_index);
+		if (msi_val) {
+			dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
+			rc = -EINVAL;
+			goto error;
+		}
+	}
+
+	cpu_notifier_register_begin();
+
+	for_each_online_cpu(cpu)
+		if (xgene_msi_hwirq_alloc(cpu)) {
+			dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
+			cpu_notifier_register_done();
+			goto error;
+		}
+
+	rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+		cpu_notifier_register_done();
+		goto error;
+	}
+
+	cpu_notifier_register_done();
+
+	xgene_msi->mchip.of_node = pdev->dev.of_node;
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error_notifier;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+
+error_notifier:
+	unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index ee082c0..a9b4f16 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1


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

* [PATCH v9 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-27 18:27                                                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-27 18:27 UTC (permalink / raw)
  To: linux-arm-kernel

APM X-Gene v1 SoC supports its own implementation of MSI, which is not
compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
This MSI block supports 2048 MSI termination ports coalesced into 16
physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support
set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically
allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI
interrupt to target CPU, MSI vector is moved around these HW IRQs lines.
With this approach, the total MSI vectors this driver supports is reduced
to 256.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 drivers/pci/host/Kconfig         |  10 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 595 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 627 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 1dfb567..a93fda8 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,21 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+	default y
+	help
+	  Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
+	  This MSI driver will provide MSI support for 5 PCIe ports of
+	  APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index f733b4e..1957431 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,6 +11,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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
 obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..461cb11
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,595 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0			0x000000
+#define MSI_INT0		0x800000
+#define IDX_PER_GROUP		8
+#define IRQS_PER_IDX		16
+#define NR_HW_IRQS		16
+#define NR_MSI_VEC		(IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi_group {
+	struct xgene_msi		*msi;
+	int				gic_irq;
+	u32				msi_grp;
+};
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	u64				msi_addr;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	struct xgene_msi_group		*msi_groups;
+	int				num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0			base_addr
+ * MSI0IR1			base_addr +  0x10000
+ * ...				...
+ * MSI0IR6			base_addr +  0x60000
+ * MSI0IR7			base_addr +  0x70000
+ * MSI1IR0			base_addr +  0x80000
+ * MSI1IR1			base_addr +  0x90000
+ * ...				...
+ * MSI1IR7			base_addr +  0xF0000
+ * MSI2IR0			base_addr + 0x100000
+ * ...				...
+ * MSIFIR0			base_addr + 0x780000
+ * MSIFIR1			base_addr + 0x790000
+ * ...				...
+ * MSIFIR7			base_addr + 0x7F0000
+ * MSIINT0			base_addr + 0x800000
+ * MSIINT1			base_addr + 0x810000
+ * ...				...
+ * MSIINTF			base_addr + 0x8F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
+ * the MSI pending status caused by 1 of its 8 index registers.
+ */
+
+/* MSInIRx read helper */
+static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
+				    u32 msi_grp, u32 msir_idx)
+{
+	return readl_relaxed(msi->msi_regs + MSI_IR0 +
+			      (msi_grp << 19) + (msir_idx << 16));
+}
+
+/* MSIINTn read helper */
+static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
+{
+	return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+}
+
+/* With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *		Group 0: 0-255
+ *		Group 1: 256-511
+ *		Group 2: 512-767
+ *		...
+ *		Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *	As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *		Group 0: 0-15
+ *		Group 1: 16-32
+ *		...
+ *		Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static inline u32 hwirq_to_reg_set(unsigned long hwirq)
+{
+	return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX));
+}
+
+static inline u32 hwirq_to_group(unsigned long hwirq)
+{
+	return (hwirq % NR_HW_IRQS);
+}
+
+static inline u32 hwirq_to_msi_data(unsigned long hwirq)
+{
+	return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX);
+}
+
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 reg_set = hwirq_to_reg_set(data->hwirq);
+	u32 group = hwirq_to_group(data->hwirq);
+	u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16);
+
+	msg->address_hi = upper_32_bits(target_addr);
+	msg->address_lo = lower_32_bits(target_addr);
+	msg->data = hwirq_to_msi_data(data->hwirq);
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static inline int hwirq_to_cpu(unsigned long hwirq)
+{
+	return ((hwirq % NR_HW_IRQS) % xgene_msi_ctrl.num_cpus);
+}
+
+static inline unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq)
+{
+	return (hwirq - (hwirq % xgene_msi_ctrl.num_cpus));
+}
+
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+				  const struct cpumask *mask, bool force)
+{
+	int target_cpu = cpumask_first(mask);
+	int curr_cpu;
+
+	curr_cpu = hwirq_to_cpu(irq_data->hwirq);
+	if (curr_cpu == target_cpu)
+		return IRQ_SET_MASK_OK_DONE;
+
+	/* Update MSI number to target the new CPU */
+	irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+
+	return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+	struct xgene_msi *msi = domain->host_data;
+	int msi_irq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+					     msi->num_cpus, 0);
+	if (msi_irq < NR_MSI_VEC)
+		bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+	else
+		msi_irq = -ENOSPC;
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq,
+			    &xgene_msi_bottom_irq_chip, domain->host_data,
+			    handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+	u32 hwirq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	hwirq = hwirq_to_canonical_hwirq(d->hwirq);
+	bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+					    &msi_domain_ops, msi);
+	if (!msi->domain)
+		return -ENOMEM;
+
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
+					sizeof(struct xgene_msi_group),
+					GFP_KERNEL);
+	if (!xgene_msi->msi_groups)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct xgene_msi_group *msi_groups;
+	struct xgene_msi *xgene_msi;
+	unsigned int virq;
+	int msir_index, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp;
+
+	chained_irq_enter(chip, desc);
+
+	msi_groups = irq_desc_get_handler_data(desc);
+	xgene_msi = msi_groups->msi;
+	msi_grp = msi_groups->msi_grp;
+
+	/*
+	 * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+	 * If bit x of this register is set (x is 0..7), one or more interupts
+	 * corresponding to MSInIRx is set.
+	 */
+	grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		/*
+		 * Calculate MSInIRx address to read to check for interrupts
+		 * (refer to termination address and data assignment
+		 * described in xgene_compose_msi_msg function)
+		 */
+		msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			/*
+			 * Calculate MSI vector number (refer to the termination
+			 * address and data assignment described in
+			 * xgene_compose_msi_msg function)
+			 */
+			hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+				 NR_HW_IRQS) + msi_grp;
+			/*
+			 * As we have multiple hw_irq that maps to single MSI,
+			 * always look up the virq using the hw_irq as seen from
+			 * CPU0
+			 */
+			hw_irq = hwirq_to_canonical_hwirq(hw_irq);
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			WARN_ON(!virq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+		}
+		grp_select &= ~(1 << msir_index);
+
+		if (!grp_select) {
+			/*
+			 * We handled all interrupts happened in this group,
+			 * resample this group MSI_INTx register in case
+			 * something else has been made pending in the meantime
+			 */
+			grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+		}
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		virq = msi->msi_groups[i].gic_irq;
+		if (virq != 0) {
+			irq_set_chained_handler(virq, NULL);
+			irq_set_handler_data(virq, NULL);
+		}
+	}
+	kfree(msi->msi_groups);
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static int xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	cpumask_var_t mask;
+	int i;
+	int err;
+
+	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq,
+					xgene_msi_isr);
+		err = irq_set_handler_data(msi_group->gic_irq, msi_group);
+		if (err) {
+			pr_err("failed to register GIC IRQ handler\n");
+			return -EINVAL;
+		}
+		/*
+		 * Statically allocate MSI GIC IRQs to each CPU core
+		 * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+		 * to each core.
+		 */
+		if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+			cpumask_clear(mask);
+			cpumask_set_cpu(cpu, mask);
+			err = irq_set_affinity(msi_group->gic_irq, mask);
+			if (err)
+				pr_err("failed to set affinity for GIC IRQ");
+			free_cpumask_var(mask);
+		} else {
+			pr_err("failed to alloc CPU mask for affinity\n");
+			err = -EINVAL;
+		}
+
+		if (err) {
+			irq_set_chained_handler(msi_group->gic_irq, NULL);
+			irq_set_handler_data(msi_group->gic_irq, NULL);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	int i;
+
+	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq, NULL);
+		irq_set_handler_data(msi_group->gic_irq, NULL);
+	}
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+				  unsigned long action, void *hcpu)
+{
+	unsigned cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		xgene_msi_hwirq_alloc(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		xgene_msi_hwirq_free(cpu);
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+	.notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi"},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct xgene_msi *xgene_msi;
+	unsigned int cpu;
+	int virt_msir;
+	u32 msi_val, msi_idx;
+
+	xgene_msi = &xgene_msi_ctrl;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	xgene_msi->msi_addr = res->start;
+
+	xgene_msi->num_cpus = num_possible_cpus();
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		virt_msir = platform_get_irq(pdev, irq_index);
+		if (virt_msir < 0) {
+			dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+				irq_index);
+			rc = -EINVAL;
+			goto error;
+		}
+		xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
+		xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
+		xgene_msi->msi_groups[irq_index].msi = xgene_msi;
+	}
+
+	/*
+	 * MSInIRx registers are read-to-clear, before registering interrupt
+	 * handlers, reading all of them to clear all spurious interrupts
+	 * that may occur before the driver is probed.
+	 */
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
+			msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
+						    msi_idx);
+		/* Read MSIINTn to confirm */
+		msi_val = xgene_msi_int_read(xgene_msi, irq_index);
+		if (msi_val) {
+			dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
+			rc = -EINVAL;
+			goto error;
+		}
+	}
+
+	cpu_notifier_register_begin();
+
+	for_each_online_cpu(cpu)
+		if (xgene_msi_hwirq_alloc(cpu)) {
+			dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
+			cpu_notifier_register_done();
+			goto error;
+		}
+
+	rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+		cpu_notifier_register_done();
+		goto error;
+	}
+
+	cpu_notifier_register_done();
+
+	xgene_msi->mchip.of_node = pdev->dev.of_node;
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error_notifier;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+
+error_notifier:
+	unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index ee082c0..a9b4f16 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1

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

* [PATCH v9 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
  2015-05-27 18:27                                                           ` Duc Dang
@ 2015-05-27 18:27                                                           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-27 18:27 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau,
	Marc Zyngier, Thomas Gleixner, Jason Cooper, Mark Rutland
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index c8d3e0e..d8f3a1c 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -374,6 +374,28 @@
 			};
 		};
 
+		msi: msi@79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie@1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -395,6 +417,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie@1f2c0000 {
@@ -418,6 +441,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie@1f2d0000 {
@@ -441,6 +465,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie@1f500000 {
@@ -464,6 +489,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie@1f510000 {
@@ -487,6 +513,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial@1c020000 {
-- 
1.9.1


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

* [PATCH v9 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
@ 2015-05-27 18:27                                                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-27 18:27 UTC (permalink / raw)
  To: linux-arm-kernel

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index c8d3e0e..d8f3a1c 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -374,6 +374,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -395,6 +417,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -418,6 +441,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -441,6 +465,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -464,6 +489,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -487,6 +513,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
-- 
1.9.1

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

* [PATCH v9 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
  2015-05-27 18:27                                                           ` Duc Dang
@ 2015-05-27 18:27                                                           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-27 18:27 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau,
	Marc Zyngier, Thomas Gleixner, Jason Cooper, Mark Rutland
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 781e099..59aa7b3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7564,6 +7564,14 @@ L:	linux-pci@vger.kernel.org
 S:	Orphan
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci@vger.kernel.org
+L:	linux-arm-kernel@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia@lists.infradead.org
-- 
1.9.1


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

* [PATCH v9 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
@ 2015-05-27 18:27                                                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-27 18:27 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 781e099..59aa7b3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7564,6 +7564,14 @@ L:	linux-pci at vger.kernel.org
 S:	Orphan
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
-- 
1.9.1

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

* Re: [PATCH v8 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-05-25 11:52                                                         ` Marc Zyngier
  (?)
@ 2015-05-27 18:31                                                           ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-27 18:31 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Thomas Gleixner, Jason Cooper

On Mon, May 25, 2015 at 4:52 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On Fri, 22 May 2015 19:41:10 +0100
> Duc Dang <dhdang@apm.com> wrote:
>
>> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
>> to GIC V2M specification for MSI Termination.
>>
>> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
>> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
>> and shared across all 5 PCIe ports.
>>
>> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
>> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
>> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
>> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
>> driver supports is reduced to 256.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  drivers/pci/host/Kconfig         |  10 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 573 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 605 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 7b892a9..0932118 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,21 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>> +       select PCI_XGENE_MSI if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +       help
>> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
>> +         This MSI driver will provide MSI support for 5 PCIe ports of
>> +         APM X-Gene v1 SoC
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index e61d91c..f39bde3 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..0c6823d
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,573 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/cpu.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_IR0                        0x000000
>> +#define MSI_INT0               0x800000
>> +#define IDX_PER_GROUP          8
>> +#define IRQS_PER_IDX           16
>> +#define NR_HW_IRQS             16
>> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
>> +
>> +struct xgene_msi_group {
>> +       struct xgene_msi                *msi;
>> +       int                             gic_irq;
>> +       u32                             msi_grp;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       u64                             msi_addr;
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       struct xgene_msi_group          *msi_groups;
>> +       int                             num_cpus;
>> +};
>> +
>> +/* Global data */
>> +static struct xgene_msi xgene_msi_ctrl;
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +/*
>> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
>> + * n is group number (0..F), x is index of registers in each group (0..7)
>> + * The registers layout is like following:
>> + * MSI0IR0                     base_addr
>> + * MSI0IR1                     base_addr +  0x10000
>> + * ...                         ...
>> + * MSI0IR6                     base_addr +  0x60000
>> + * MSI0IR7                     base_addr +  0x70000
>> + * MSI1IR0                     base_addr +  0x80000
>> + * MSI1IR1                     base_addr +  0x90000
>> + * ...                         ...
>> + * MSI1IR7                     base_addr +  0xF0000
>> + * MSI2IR0                     base_addr + 0x100000
>> + * ...                         ...
>> + * MSIFIR0                     base_addr + 0x780000
>> + * MSIFIR1                     base_addr + 0x790000
>> + * ...                         ...
>> + * MSIFIR7                     base_addr + 0x7F0000
>> + * MSIINT0                     base_addr + 0x800000
>> + * MSIINT1                     base_addr + 0x810000
>> + * ...                         ...
>> + * MSIINTF                     base_addr + 0x8F0000
>> + *
>> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
>> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
>> + * registers.
>> + *
>> + * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
>> + * the MSI pending status caused by 1 of its 8 index registers.
>> + */
>> +
>> +/* MSInIRx read helper */
>> +static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
>> +                                   u32 msi_grp, u32 msir_idx)
>> +{
>> +       return readl_relaxed(msi->msi_regs + MSI_IR0 +
>> +                             (msi_grp << 19) + (msir_idx << 16));
>> +}
>> +
>> +/* MSIINTn read helper */
>> +static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
>> +{
>> +       return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +}
>> +
>> +/* With 2048 MSI vectors supported, the MSI message can be construct using
>> + * following scheme:
>> + * - Divide into 8 256-vector groups
>> + *             Group 0: 0-255
>> + *             Group 1: 256-511
>> + *             Group 2: 512-767
>> + *             ...
>> + *             Group 7: 1792-2047
>> + * - Each 256-vector group is divided into 16 16-vector groups
>> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
>> + *             Group 0: 0-15
>> + *             Group 1: 16-32
>> + *             ...
>> + *             Group 15: 240-255
>> + * - The termination address of MSI vector in 256-vector group n and 16-vector
>> + * group x is the address of MSIxIRn
>> + * - The data for MSI vector in 16-vector group x is x
>> + */
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
>
> Create a hwirq_to_reg_set helper.
>
>> +       u32 group = data->hwirq % NR_HW_IRQS;
>
> Create a hwirq_to_group helper.
>
>> +
>> +       msg->address_hi = upper_32_bits(msi->msi_addr +
>> +                                       (((8 * group) + reg_set) << 16));
>> +       msg->address_lo = lower_32_bits(msi->msi_addr +
>> +                                       (((8 * group) + reg_set) << 16));
>
> Rule of thumb: if you write something complicated twice, you've written
> one time too many:
>
> u64 target_addr = msi->msi_addr + ((8 * group) + reg_set) << 16));
>
> msg->address_hi = upper_32_bits(target_addr);
> msg->address_lo = lower_32_bits(target_addr);
>
> It doesn't hurt to write something readable.
>
>> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
>> +}
>> +
>> +/*
>> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
>> + * To maintain the expected behaviour of .set_affinity for each MSI
>> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
>> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
>> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
>> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
>> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
>> + */
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
>> +       int target_cpu = cpumask_first(mask);
>> +       int curr_cpu;
>> +
>> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
>
> Create a hwirq_to_cpu helper.
>
>> +       if (curr_cpu == target_cpu)
>> +               return IRQ_SET_MASK_OK_DONE;
>> +
>> +       /* Update MSI number to target the new CPU */
>> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
>> +
>> +       return IRQ_SET_MASK_OK;
>> +}
>> +
>> +static struct irq_chip xgene_msi_bottom_irq_chip = {
>> +       .name                   = "MSI",
>> +       .irq_set_affinity       = xgene_msi_set_affinity,
>> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
>> +};
>> +
>> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
>> +                                 unsigned int nr_irqs, void *args)
>> +{
>> +       struct xgene_msi *msi = domain->host_data;
>> +       int msi_irq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
>> +                                            msi->num_cpus, 0);
>> +       if (msi_irq < NR_MSI_VEC)
>> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
>> +       else
>> +               msi_irq = -ENOSPC;
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       if (msi_irq < 0)
>> +               return msi_irq;
>> +
>> +       irq_domain_set_info(domain, virq, msi_irq,
>> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
>> +                           handle_simple_irq, NULL, NULL);
>> +       set_irq_flags(virq, IRQF_VALID);
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>> +                                 unsigned int virq, unsigned int nr_irqs)
>> +{
>> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>> +       u32 hwirq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
>
> Create hwirq_to_canonical_hwirq helper (hwirq - hwirq_to_cpu(hwirq)).
>
>> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops msi_domain_ops = {
>> +       .alloc  = xgene_irq_domain_alloc,
>> +       .free   = xgene_irq_domain_free,
>> +};
>> +
>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>> +{
>> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
>> +                                           &msi_domain_ops, msi);
>> +       if (!msi->domain)
>> +               return -ENOMEM;
>> +
>> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>> +                                                     &xgene_msi_domain_info,
>> +                                                     msi->domain);
>> +
>> +       if (!msi->mchip.domain) {
>> +               irq_domain_remove(msi->domain);
>> +               return -ENOMEM;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_free_domains(struct xgene_msi *msi)
>> +{
>> +       if (msi->mchip.domain)
>> +               irq_domain_remove(msi->mchip.domain);
>> +       if (msi->domain)
>> +               irq_domain_remove(msi->domain);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
>> +
>> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +       if (!xgene_msi->bitmap)
>> +               return -ENOMEM;
>> +
>> +       mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
>> +                                       sizeof(struct xgene_msi_group),
>> +                                       GFP_KERNEL);
>> +       if (!xgene_msi->msi_groups)
>> +               return -ENOMEM;
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
>> +{
>> +       struct irq_chip *chip = irq_desc_get_chip(desc);
>> +       struct xgene_msi_group *msi_groups;
>> +       struct xgene_msi *xgene_msi;
>> +       unsigned int virq;
>> +       int msir_index, msir_val, hw_irq;
>> +       u32 intr_index, grp_select, msi_grp;
>> +
>> +       chained_irq_enter(chip, desc);
>> +
>> +       msi_groups = irq_desc_get_handler_data(desc);
>> +       xgene_msi = msi_groups->msi;
>> +       msi_grp = msi_groups->msi_grp;
>> +
>> +       /*
>> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
>> +        * If bit x of this register is set (x is 0..7), one or more interupts
>> +        * corresponding to MSInIRx is set.
>> +        */
>> +       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
>> +       while (grp_select) {
>> +               msir_index = ffs(grp_select) - 1;
>> +               /*
>> +                * Calculate MSInIRx address to read to check for interrupts
>> +                * (refer to termination address and data assignment
>> +                * described in xgene_compose_msi_msg function)
>> +                */
>> +               msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
>> +               while (msir_val) {
>> +                       intr_index = ffs(msir_val) - 1;
>> +                       /*
>> +                        * Calculate MSI vector number (refer to the termination
>> +                        * address and data assignment described in
>> +                        * xgene_compose_msi_msg function)
>> +                        */
>> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
>> +                                NR_HW_IRQS) + msi_grp;
>> +                       /*
>> +                        * As we have multiple hw_irq that maps to single MSI,
>> +                        * always look up the virq using the hw_irq as seen from
>> +                        * CPU0
>> +                        */
>> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
>
> hw_irq = hwirq_to_canonical_hwirq(hw_irq);
>
>> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> +                       WARN_ON(!virq);
>> +                       if (virq != 0)
>> +                               generic_handle_irq(virq);
>> +                       msir_val &= ~(1 << intr_index);
>> +               }
>> +               grp_select &= ~(1 << msir_index);
>> +
>> +               if (!grp_select) {
>> +                       /*
>> +                        * We handled all interrupts happened in this group,
>> +                        * resample this group MSI_INTx register in case
>> +                        * something else has been made pending in the meantime
>> +                        */
>> +                       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
>> +               }
>> +       }
>> +
>> +       chained_irq_exit(chip, desc);
>> +}
>> +
>> +static int xgene_msi_remove(struct platform_device *pdev)
>> +{
>> +       int virq, i;
>> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
>> +
>> +       for (i = 0; i < NR_HW_IRQS; i++) {
>> +               virq = msi->msi_groups[i].gic_irq;
>> +               if (virq != 0) {
>> +                       irq_set_chained_handler(virq, NULL);
>> +                       irq_set_handler_data(virq, NULL);
>> +               }
>> +       }
>> +       kfree(msi->msi_groups);
>> +
>> +       kfree(msi->bitmap);
>> +       msi->bitmap = NULL;
>> +
>> +       xgene_free_domains(msi);
>> +
>> +       return 0;
>> +}
>> +
>> +static int xgene_msi_hwirq_alloc(unsigned int cpu)
>> +{
>> +       struct xgene_msi *msi = &xgene_msi_ctrl;
>> +       struct xgene_msi_group *msi_group;
>> +       cpumask_var_t mask;
>> +       int i;
>> +       int err;
>> +
>> +       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
>> +               msi_group = &msi->msi_groups[i];
>> +               if (!msi_group->gic_irq)
>> +                       continue;
>> +
>> +               irq_set_chained_handler(msi_group->gic_irq,
>> +                                       xgene_msi_isr);
>> +               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
>> +               if (err) {
>> +                       pr_err("failed to register GIC IRQ handler\n");
>> +                       return -EINVAL;
>> +               }
>> +               /*
>> +                * Statically allocate MSI GIC IRQs to each CPU core
>> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
>> +                * to each core.
>> +                */
>> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
>> +                       cpumask_clear(mask);
>> +                       cpumask_set_cpu(cpu, mask);
>> +                       err = irq_set_affinity(msi_group->gic_irq, mask);
>> +                       if (err)
>> +                               pr_err("failed to set affinity for GIC IRQ");
>> +                       free_cpumask_var(mask);
>> +               } else {
>> +                       pr_err("failed to alloc CPU mask for affinity\n");
>> +                       err = -EINVAL;
>> +               }
>> +
>> +               if (err) {
>> +                       irq_set_chained_handler(msi_group->gic_irq, NULL);
>> +                       irq_set_handler_data(msi_group->gic_irq, NULL);
>> +                       return -EINVAL;
>
>                         return err;
>
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_msi_hwirq_free(unsigned int cpu)
>> +{
>> +       struct xgene_msi *msi = &xgene_msi_ctrl;
>> +       struct xgene_msi_group *msi_group;
>> +       int i;
>> +
>> +       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
>> +               msi_group = &msi->msi_groups[i];
>> +               if (!msi_group->gic_irq)
>> +                       continue;
>> +
>> +               irq_set_chained_handler(msi_group->gic_irq, NULL);
>> +               irq_set_handler_data(msi_group->gic_irq, NULL);
>> +       }
>> +}
>> +
>> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
>> +                                 unsigned long action, void *hcpu)
>> +{
>> +       unsigned cpu = (unsigned long)hcpu;
>> +
>> +       switch (action) {
>> +       case CPU_ONLINE:
>> +       case CPU_ONLINE_FROZEN:
>> +               xgene_msi_hwirq_alloc(cpu);
>> +               break;
>> +       case CPU_DEAD:
>> +       case CPU_DEAD_FROZEN:
>> +               xgene_msi_hwirq_free(cpu);
>> +               break;
>> +       default:
>> +               break;
>> +       }
>> +
>> +       return NOTIFY_OK;
>> +}
>> +
>> +static struct notifier_block xgene_msi_cpu_notifier = {
>> +       .notifier_call = xgene_msi_cpu_callback,
>> +};
>> +
>> +static const struct of_device_id xgene_msi_match_table[] = {
>> +       {.compatible = "apm,xgene1-msi"},
>> +       {},
>> +};
>> +
>> +static int xgene_msi_probe(struct platform_device *pdev)
>> +{
>> +       struct resource *res;
>> +       int rc, irq_index;
>> +       struct xgene_msi *xgene_msi;
>> +       unsigned int cpu;
>> +       int virt_msir;
>> +       u32 msi_val, msi_idx;
>> +
>> +       xgene_msi = &xgene_msi_ctrl;
>> +
>> +       platform_set_drvdata(pdev, xgene_msi);
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(xgene_msi->msi_regs)) {
>> +               dev_err(&pdev->dev, "no reg space\n");
>> +               rc = -EINVAL;
>> +               goto error;
>> +       }
>> +       xgene_msi->msi_addr = res->start;
>> +
>> +       xgene_msi->num_cpus = num_possible_cpus();
>> +
>> +       rc = xgene_msi_init_allocator(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
>> +               goto error;
>> +       }
>> +
>> +       rc = xgene_allocate_domains(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
>> +               goto error;
>> +       }
>> +
>> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
>> +               virt_msir = platform_get_irq(pdev, irq_index);
>> +               if (virt_msir < 0) {
>> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
>> +                               irq_index);
>> +                       rc = -EINVAL;
>> +                       goto error;
>> +               }
>> +               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
>> +               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
>> +               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
>> +       }
>> +
>> +       /*
>> +        * MSInIRx registers are read-to-clear, before registering interrupt
>> +        * handlers, reading all of them to clear all spurious interrupts
>> +        * that may occur before the driver is probed.
>> +        */
>> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
>> +               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
>> +                       msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
>> +                                                   msi_idx);
>> +               /* Read MSIINTn to confirm */
>> +               msi_val = xgene_msi_int_read(xgene_msi, irq_index);
>> +               if (msi_val) {
>> +                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
>> +                       rc = -EINVAL;
>> +                       goto error;
>> +               }
>> +       }
>> +
>> +       cpu_notifier_register_begin();
>> +
>> +       for_each_online_cpu(cpu)
>> +               if (xgene_msi_hwirq_alloc(cpu)) {
>> +                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
>> +                       cpu_notifier_register_done();
>> +                       goto error;
>> +               }
>> +
>> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
>> +               cpu_notifier_register_done();
>> +               goto error;
>> +       }
>> +
>> +       cpu_notifier_register_done();
>> +
>> +       xgene_msi->mchip.of_node = pdev->dev.of_node;
>> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
>> +               goto error_notifier;
>> +       }
>> +
>> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
>> +
>> +       return 0;
>> +
>> +error_notifier:
>> +       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
>> +error:
>> +       xgene_msi_remove(pdev);
>> +       return rc;
>> +}
>> +
>> +static struct platform_driver xgene_msi_driver = {
>> +       .driver = {
>> +               .name = "xgene-msi",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = xgene_msi_match_table,
>> +       },
>> +       .probe = xgene_msi_probe,
>> +       .remove = xgene_msi_remove,
>> +};
>> +
>> +static int __init xgene_pcie_msi_init(void)
>> +{
>> +       return platform_driver_register(&xgene_msi_driver);
>> +}
>> +subsys_initcall(xgene_pcie_msi_init);
>> +
>> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
>> index 22751ed..3e6faa1 100644
>> --- a/drivers/pci/host/pci-xgene.c
>> +++ b/drivers/pci/host/pci-xgene.c
>> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>>         return 0;
>>  }
>>
>> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
>> +{
>> +       struct device_node *msi_node;
>> +
>> +       msi_node = of_parse_phandle(bus->dev.of_node,
>> +                                       "msi-parent", 0);
>> +       if (!msi_node)
>> +               return -ENODEV;
>> +
>> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
>> +       if (bus->msi)
>> +               bus->msi->dev = &bus->dev;
>> +       else
>> +               return -ENODEV;
>> +       return 0;
>> +}
>> +
>>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>  {
>>         struct device_node *dn = pdev->dev.of_node;
>> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>         if (!bus)
>>                 return -ENOMEM;
>>
>> +       if (IS_ENABLED(CONFIG_PCI_MSI))
>> +               if (xgene_pcie_msi_enable(bus))
>> +                       dev_info(port->dev, "failed to enable MSI\n");
>> +
>>         pci_scan_child_bus(bus);
>>         pci_assign_unassigned_bus_resources(bus);
>>         pci_bus_add_devices(bus);
>> --
>> 1.9.1
>>
>
> Once you've fixed the couple of nits above, you can add my
>
> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
>
> You should also CC the irqchip maintainers (Thomas and Jason), as I'd
> very much like to have their input on this as well. A few "Tested-by"
> wouldn't hurt either.
>
Hi Marc,

I added the changes you suggested into v9 patch set and also added
Thomas, Jason to the thread.

> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny.

Regards,
Duc Dang.

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

* Re: [PATCH v8 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-27 18:31                                                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-27 18:31 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Thomas Gleixner, Jason Cooper

On Mon, May 25, 2015 at 4:52 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On Fri, 22 May 2015 19:41:10 +0100
> Duc Dang <dhdang@apm.com> wrote:
>
>> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
>> to GIC V2M specification for MSI Termination.
>>
>> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
>> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
>> and shared across all 5 PCIe ports.
>>
>> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
>> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
>> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
>> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
>> driver supports is reduced to 256.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  drivers/pci/host/Kconfig         |  10 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 573 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 605 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 7b892a9..0932118 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,21 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>> +       select PCI_XGENE_MSI if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +       help
>> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
>> +         This MSI driver will provide MSI support for 5 PCIe ports of
>> +         APM X-Gene v1 SoC
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index e61d91c..f39bde3 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..0c6823d
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,573 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/cpu.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_IR0                        0x000000
>> +#define MSI_INT0               0x800000
>> +#define IDX_PER_GROUP          8
>> +#define IRQS_PER_IDX           16
>> +#define NR_HW_IRQS             16
>> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
>> +
>> +struct xgene_msi_group {
>> +       struct xgene_msi                *msi;
>> +       int                             gic_irq;
>> +       u32                             msi_grp;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       u64                             msi_addr;
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       struct xgene_msi_group          *msi_groups;
>> +       int                             num_cpus;
>> +};
>> +
>> +/* Global data */
>> +static struct xgene_msi xgene_msi_ctrl;
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +/*
>> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
>> + * n is group number (0..F), x is index of registers in each group (0..7)
>> + * The registers layout is like following:
>> + * MSI0IR0                     base_addr
>> + * MSI0IR1                     base_addr +  0x10000
>> + * ...                         ...
>> + * MSI0IR6                     base_addr +  0x60000
>> + * MSI0IR7                     base_addr +  0x70000
>> + * MSI1IR0                     base_addr +  0x80000
>> + * MSI1IR1                     base_addr +  0x90000
>> + * ...                         ...
>> + * MSI1IR7                     base_addr +  0xF0000
>> + * MSI2IR0                     base_addr + 0x100000
>> + * ...                         ...
>> + * MSIFIR0                     base_addr + 0x780000
>> + * MSIFIR1                     base_addr + 0x790000
>> + * ...                         ...
>> + * MSIFIR7                     base_addr + 0x7F0000
>> + * MSIINT0                     base_addr + 0x800000
>> + * MSIINT1                     base_addr + 0x810000
>> + * ...                         ...
>> + * MSIINTF                     base_addr + 0x8F0000
>> + *
>> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
>> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
>> + * registers.
>> + *
>> + * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
>> + * the MSI pending status caused by 1 of its 8 index registers.
>> + */
>> +
>> +/* MSInIRx read helper */
>> +static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
>> +                                   u32 msi_grp, u32 msir_idx)
>> +{
>> +       return readl_relaxed(msi->msi_regs + MSI_IR0 +
>> +                             (msi_grp << 19) + (msir_idx << 16));
>> +}
>> +
>> +/* MSIINTn read helper */
>> +static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
>> +{
>> +       return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +}
>> +
>> +/* With 2048 MSI vectors supported, the MSI message can be construct using
>> + * following scheme:
>> + * - Divide into 8 256-vector groups
>> + *             Group 0: 0-255
>> + *             Group 1: 256-511
>> + *             Group 2: 512-767
>> + *             ...
>> + *             Group 7: 1792-2047
>> + * - Each 256-vector group is divided into 16 16-vector groups
>> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
>> + *             Group 0: 0-15
>> + *             Group 1: 16-32
>> + *             ...
>> + *             Group 15: 240-255
>> + * - The termination address of MSI vector in 256-vector group n and 16-vector
>> + * group x is the address of MSIxIRn
>> + * - The data for MSI vector in 16-vector group x is x
>> + */
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
>
> Create a hwirq_to_reg_set helper.
>
>> +       u32 group = data->hwirq % NR_HW_IRQS;
>
> Create a hwirq_to_group helper.
>
>> +
>> +       msg->address_hi = upper_32_bits(msi->msi_addr +
>> +                                       (((8 * group) + reg_set) << 16));
>> +       msg->address_lo = lower_32_bits(msi->msi_addr +
>> +                                       (((8 * group) + reg_set) << 16));
>
> Rule of thumb: if you write something complicated twice, you've written
> one time too many:
>
> u64 target_addr = msi->msi_addr + ((8 * group) + reg_set) << 16));
>
> msg->address_hi = upper_32_bits(target_addr);
> msg->address_lo = lower_32_bits(target_addr);
>
> It doesn't hurt to write something readable.
>
>> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
>> +}
>> +
>> +/*
>> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
>> + * To maintain the expected behaviour of .set_affinity for each MSI
>> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
>> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
>> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
>> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
>> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
>> + */
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
>> +       int target_cpu = cpumask_first(mask);
>> +       int curr_cpu;
>> +
>> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
>
> Create a hwirq_to_cpu helper.
>
>> +       if (curr_cpu == target_cpu)
>> +               return IRQ_SET_MASK_OK_DONE;
>> +
>> +       /* Update MSI number to target the new CPU */
>> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
>> +
>> +       return IRQ_SET_MASK_OK;
>> +}
>> +
>> +static struct irq_chip xgene_msi_bottom_irq_chip = {
>> +       .name                   = "MSI",
>> +       .irq_set_affinity       = xgene_msi_set_affinity,
>> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
>> +};
>> +
>> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
>> +                                 unsigned int nr_irqs, void *args)
>> +{
>> +       struct xgene_msi *msi = domain->host_data;
>> +       int msi_irq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
>> +                                            msi->num_cpus, 0);
>> +       if (msi_irq < NR_MSI_VEC)
>> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
>> +       else
>> +               msi_irq = -ENOSPC;
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       if (msi_irq < 0)
>> +               return msi_irq;
>> +
>> +       irq_domain_set_info(domain, virq, msi_irq,
>> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
>> +                           handle_simple_irq, NULL, NULL);
>> +       set_irq_flags(virq, IRQF_VALID);
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>> +                                 unsigned int virq, unsigned int nr_irqs)
>> +{
>> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>> +       u32 hwirq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
>
> Create hwirq_to_canonical_hwirq helper (hwirq - hwirq_to_cpu(hwirq)).
>
>> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops msi_domain_ops = {
>> +       .alloc  = xgene_irq_domain_alloc,
>> +       .free   = xgene_irq_domain_free,
>> +};
>> +
>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>> +{
>> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
>> +                                           &msi_domain_ops, msi);
>> +       if (!msi->domain)
>> +               return -ENOMEM;
>> +
>> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>> +                                                     &xgene_msi_domain_info,
>> +                                                     msi->domain);
>> +
>> +       if (!msi->mchip.domain) {
>> +               irq_domain_remove(msi->domain);
>> +               return -ENOMEM;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_free_domains(struct xgene_msi *msi)
>> +{
>> +       if (msi->mchip.domain)
>> +               irq_domain_remove(msi->mchip.domain);
>> +       if (msi->domain)
>> +               irq_domain_remove(msi->domain);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
>> +
>> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +       if (!xgene_msi->bitmap)
>> +               return -ENOMEM;
>> +
>> +       mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
>> +                                       sizeof(struct xgene_msi_group),
>> +                                       GFP_KERNEL);
>> +       if (!xgene_msi->msi_groups)
>> +               return -ENOMEM;
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
>> +{
>> +       struct irq_chip *chip = irq_desc_get_chip(desc);
>> +       struct xgene_msi_group *msi_groups;
>> +       struct xgene_msi *xgene_msi;
>> +       unsigned int virq;
>> +       int msir_index, msir_val, hw_irq;
>> +       u32 intr_index, grp_select, msi_grp;
>> +
>> +       chained_irq_enter(chip, desc);
>> +
>> +       msi_groups = irq_desc_get_handler_data(desc);
>> +       xgene_msi = msi_groups->msi;
>> +       msi_grp = msi_groups->msi_grp;
>> +
>> +       /*
>> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
>> +        * If bit x of this register is set (x is 0..7), one or more interupts
>> +        * corresponding to MSInIRx is set.
>> +        */
>> +       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
>> +       while (grp_select) {
>> +               msir_index = ffs(grp_select) - 1;
>> +               /*
>> +                * Calculate MSInIRx address to read to check for interrupts
>> +                * (refer to termination address and data assignment
>> +                * described in xgene_compose_msi_msg function)
>> +                */
>> +               msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
>> +               while (msir_val) {
>> +                       intr_index = ffs(msir_val) - 1;
>> +                       /*
>> +                        * Calculate MSI vector number (refer to the termination
>> +                        * address and data assignment described in
>> +                        * xgene_compose_msi_msg function)
>> +                        */
>> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
>> +                                NR_HW_IRQS) + msi_grp;
>> +                       /*
>> +                        * As we have multiple hw_irq that maps to single MSI,
>> +                        * always look up the virq using the hw_irq as seen from
>> +                        * CPU0
>> +                        */
>> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
>
> hw_irq = hwirq_to_canonical_hwirq(hw_irq);
>
>> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> +                       WARN_ON(!virq);
>> +                       if (virq != 0)
>> +                               generic_handle_irq(virq);
>> +                       msir_val &= ~(1 << intr_index);
>> +               }
>> +               grp_select &= ~(1 << msir_index);
>> +
>> +               if (!grp_select) {
>> +                       /*
>> +                        * We handled all interrupts happened in this group,
>> +                        * resample this group MSI_INTx register in case
>> +                        * something else has been made pending in the meantime
>> +                        */
>> +                       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
>> +               }
>> +       }
>> +
>> +       chained_irq_exit(chip, desc);
>> +}
>> +
>> +static int xgene_msi_remove(struct platform_device *pdev)
>> +{
>> +       int virq, i;
>> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
>> +
>> +       for (i = 0; i < NR_HW_IRQS; i++) {
>> +               virq = msi->msi_groups[i].gic_irq;
>> +               if (virq != 0) {
>> +                       irq_set_chained_handler(virq, NULL);
>> +                       irq_set_handler_data(virq, NULL);
>> +               }
>> +       }
>> +       kfree(msi->msi_groups);
>> +
>> +       kfree(msi->bitmap);
>> +       msi->bitmap = NULL;
>> +
>> +       xgene_free_domains(msi);
>> +
>> +       return 0;
>> +}
>> +
>> +static int xgene_msi_hwirq_alloc(unsigned int cpu)
>> +{
>> +       struct xgene_msi *msi = &xgene_msi_ctrl;
>> +       struct xgene_msi_group *msi_group;
>> +       cpumask_var_t mask;
>> +       int i;
>> +       int err;
>> +
>> +       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
>> +               msi_group = &msi->msi_groups[i];
>> +               if (!msi_group->gic_irq)
>> +                       continue;
>> +
>> +               irq_set_chained_handler(msi_group->gic_irq,
>> +                                       xgene_msi_isr);
>> +               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
>> +               if (err) {
>> +                       pr_err("failed to register GIC IRQ handler\n");
>> +                       return -EINVAL;
>> +               }
>> +               /*
>> +                * Statically allocate MSI GIC IRQs to each CPU core
>> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
>> +                * to each core.
>> +                */
>> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
>> +                       cpumask_clear(mask);
>> +                       cpumask_set_cpu(cpu, mask);
>> +                       err = irq_set_affinity(msi_group->gic_irq, mask);
>> +                       if (err)
>> +                               pr_err("failed to set affinity for GIC IRQ");
>> +                       free_cpumask_var(mask);
>> +               } else {
>> +                       pr_err("failed to alloc CPU mask for affinity\n");
>> +                       err = -EINVAL;
>> +               }
>> +
>> +               if (err) {
>> +                       irq_set_chained_handler(msi_group->gic_irq, NULL);
>> +                       irq_set_handler_data(msi_group->gic_irq, NULL);
>> +                       return -EINVAL;
>
>                         return err;
>
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_msi_hwirq_free(unsigned int cpu)
>> +{
>> +       struct xgene_msi *msi = &xgene_msi_ctrl;
>> +       struct xgene_msi_group *msi_group;
>> +       int i;
>> +
>> +       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
>> +               msi_group = &msi->msi_groups[i];
>> +               if (!msi_group->gic_irq)
>> +                       continue;
>> +
>> +               irq_set_chained_handler(msi_group->gic_irq, NULL);
>> +               irq_set_handler_data(msi_group->gic_irq, NULL);
>> +       }
>> +}
>> +
>> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
>> +                                 unsigned long action, void *hcpu)
>> +{
>> +       unsigned cpu = (unsigned long)hcpu;
>> +
>> +       switch (action) {
>> +       case CPU_ONLINE:
>> +       case CPU_ONLINE_FROZEN:
>> +               xgene_msi_hwirq_alloc(cpu);
>> +               break;
>> +       case CPU_DEAD:
>> +       case CPU_DEAD_FROZEN:
>> +               xgene_msi_hwirq_free(cpu);
>> +               break;
>> +       default:
>> +               break;
>> +       }
>> +
>> +       return NOTIFY_OK;
>> +}
>> +
>> +static struct notifier_block xgene_msi_cpu_notifier = {
>> +       .notifier_call = xgene_msi_cpu_callback,
>> +};
>> +
>> +static const struct of_device_id xgene_msi_match_table[] = {
>> +       {.compatible = "apm,xgene1-msi"},
>> +       {},
>> +};
>> +
>> +static int xgene_msi_probe(struct platform_device *pdev)
>> +{
>> +       struct resource *res;
>> +       int rc, irq_index;
>> +       struct xgene_msi *xgene_msi;
>> +       unsigned int cpu;
>> +       int virt_msir;
>> +       u32 msi_val, msi_idx;
>> +
>> +       xgene_msi = &xgene_msi_ctrl;
>> +
>> +       platform_set_drvdata(pdev, xgene_msi);
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(xgene_msi->msi_regs)) {
>> +               dev_err(&pdev->dev, "no reg space\n");
>> +               rc = -EINVAL;
>> +               goto error;
>> +       }
>> +       xgene_msi->msi_addr = res->start;
>> +
>> +       xgene_msi->num_cpus = num_possible_cpus();
>> +
>> +       rc = xgene_msi_init_allocator(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
>> +               goto error;
>> +       }
>> +
>> +       rc = xgene_allocate_domains(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
>> +               goto error;
>> +       }
>> +
>> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
>> +               virt_msir = platform_get_irq(pdev, irq_index);
>> +               if (virt_msir < 0) {
>> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
>> +                               irq_index);
>> +                       rc = -EINVAL;
>> +                       goto error;
>> +               }
>> +               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
>> +               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
>> +               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
>> +       }
>> +
>> +       /*
>> +        * MSInIRx registers are read-to-clear, before registering interrupt
>> +        * handlers, reading all of them to clear all spurious interrupts
>> +        * that may occur before the driver is probed.
>> +        */
>> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
>> +               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
>> +                       msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
>> +                                                   msi_idx);
>> +               /* Read MSIINTn to confirm */
>> +               msi_val = xgene_msi_int_read(xgene_msi, irq_index);
>> +               if (msi_val) {
>> +                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
>> +                       rc = -EINVAL;
>> +                       goto error;
>> +               }
>> +       }
>> +
>> +       cpu_notifier_register_begin();
>> +
>> +       for_each_online_cpu(cpu)
>> +               if (xgene_msi_hwirq_alloc(cpu)) {
>> +                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
>> +                       cpu_notifier_register_done();
>> +                       goto error;
>> +               }
>> +
>> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
>> +               cpu_notifier_register_done();
>> +               goto error;
>> +       }
>> +
>> +       cpu_notifier_register_done();
>> +
>> +       xgene_msi->mchip.of_node = pdev->dev.of_node;
>> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
>> +               goto error_notifier;
>> +       }
>> +
>> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
>> +
>> +       return 0;
>> +
>> +error_notifier:
>> +       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
>> +error:
>> +       xgene_msi_remove(pdev);
>> +       return rc;
>> +}
>> +
>> +static struct platform_driver xgene_msi_driver = {
>> +       .driver = {
>> +               .name = "xgene-msi",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = xgene_msi_match_table,
>> +       },
>> +       .probe = xgene_msi_probe,
>> +       .remove = xgene_msi_remove,
>> +};
>> +
>> +static int __init xgene_pcie_msi_init(void)
>> +{
>> +       return platform_driver_register(&xgene_msi_driver);
>> +}
>> +subsys_initcall(xgene_pcie_msi_init);
>> +
>> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
>> index 22751ed..3e6faa1 100644
>> --- a/drivers/pci/host/pci-xgene.c
>> +++ b/drivers/pci/host/pci-xgene.c
>> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>>         return 0;
>>  }
>>
>> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
>> +{
>> +       struct device_node *msi_node;
>> +
>> +       msi_node = of_parse_phandle(bus->dev.of_node,
>> +                                       "msi-parent", 0);
>> +       if (!msi_node)
>> +               return -ENODEV;
>> +
>> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
>> +       if (bus->msi)
>> +               bus->msi->dev = &bus->dev;
>> +       else
>> +               return -ENODEV;
>> +       return 0;
>> +}
>> +
>>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>  {
>>         struct device_node *dn = pdev->dev.of_node;
>> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>         if (!bus)
>>                 return -ENOMEM;
>>
>> +       if (IS_ENABLED(CONFIG_PCI_MSI))
>> +               if (xgene_pcie_msi_enable(bus))
>> +                       dev_info(port->dev, "failed to enable MSI\n");
>> +
>>         pci_scan_child_bus(bus);
>>         pci_assign_unassigned_bus_resources(bus);
>>         pci_bus_add_devices(bus);
>> --
>> 1.9.1
>>
>
> Once you've fixed the couple of nits above, you can add my
>
> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
>
> You should also CC the irqchip maintainers (Thomas and Jason), as I'd
> very much like to have their input on this as well. A few "Tested-by"
> wouldn't hurt either.
>
Hi Marc,

I added the changes you suggested into v9 patch set and also added
Thomas, Jason to the thread.

> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny.

Regards,
Duc Dang.

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

* [PATCH v8 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-27 18:31                                                           ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-27 18:31 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, May 25, 2015 at 4:52 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On Fri, 22 May 2015 19:41:10 +0100
> Duc Dang <dhdang@apm.com> wrote:
>
>> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
>> to GIC V2M specification for MSI Termination.
>>
>> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI
>> block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines
>> and shared across all 5 PCIe ports.
>>
>> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity
>> correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene
>> v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector
>> is moved around these HW IRQs lines. With this approach, the total MSI vectors this
>> driver supports is reduced to 256.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> ---
>>  drivers/pci/host/Kconfig         |  10 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 573 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 605 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 7b892a9..0932118 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,21 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>> +       select PCI_XGENE_MSI if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +       help
>> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
>> +         This MSI driver will provide MSI support for 5 PCIe ports of
>> +         APM X-Gene v1 SoC
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index e61d91c..f39bde3 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,5 +11,6 @@ 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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..0c6823d
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,573 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/cpu.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_IR0                        0x000000
>> +#define MSI_INT0               0x800000
>> +#define IDX_PER_GROUP          8
>> +#define IRQS_PER_IDX           16
>> +#define NR_HW_IRQS             16
>> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
>> +
>> +struct xgene_msi_group {
>> +       struct xgene_msi                *msi;
>> +       int                             gic_irq;
>> +       u32                             msi_grp;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       u64                             msi_addr;
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       struct xgene_msi_group          *msi_groups;
>> +       int                             num_cpus;
>> +};
>> +
>> +/* Global data */
>> +static struct xgene_msi xgene_msi_ctrl;
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +/*
>> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
>> + * n is group number (0..F), x is index of registers in each group (0..7)
>> + * The registers layout is like following:
>> + * MSI0IR0                     base_addr
>> + * MSI0IR1                     base_addr +  0x10000
>> + * ...                         ...
>> + * MSI0IR6                     base_addr +  0x60000
>> + * MSI0IR7                     base_addr +  0x70000
>> + * MSI1IR0                     base_addr +  0x80000
>> + * MSI1IR1                     base_addr +  0x90000
>> + * ...                         ...
>> + * MSI1IR7                     base_addr +  0xF0000
>> + * MSI2IR0                     base_addr + 0x100000
>> + * ...                         ...
>> + * MSIFIR0                     base_addr + 0x780000
>> + * MSIFIR1                     base_addr + 0x790000
>> + * ...                         ...
>> + * MSIFIR7                     base_addr + 0x7F0000
>> + * MSIINT0                     base_addr + 0x800000
>> + * MSIINT1                     base_addr + 0x810000
>> + * ...                         ...
>> + * MSIINTF                     base_addr + 0x8F0000
>> + *
>> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
>> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
>> + * registers.
>> + *
>> + * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
>> + * the MSI pending status caused by 1 of its 8 index registers.
>> + */
>> +
>> +/* MSInIRx read helper */
>> +static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
>> +                                   u32 msi_grp, u32 msir_idx)
>> +{
>> +       return readl_relaxed(msi->msi_regs + MSI_IR0 +
>> +                             (msi_grp << 19) + (msir_idx << 16));
>> +}
>> +
>> +/* MSIINTn read helper */
>> +static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
>> +{
>> +       return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +}
>> +
>> +/* With 2048 MSI vectors supported, the MSI message can be construct using
>> + * following scheme:
>> + * - Divide into 8 256-vector groups
>> + *             Group 0: 0-255
>> + *             Group 1: 256-511
>> + *             Group 2: 512-767
>> + *             ...
>> + *             Group 7: 1792-2047
>> + * - Each 256-vector group is divided into 16 16-vector groups
>> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
>> + *             Group 0: 0-15
>> + *             Group 1: 16-32
>> + *             ...
>> + *             Group 15: 240-255
>> + * - The termination address of MSI vector in 256-vector group n and 16-vector
>> + * group x is the address of MSIxIRn
>> + * - The data for MSI vector in 16-vector group x is x
>> + */
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
>
> Create a hwirq_to_reg_set helper.
>
>> +       u32 group = data->hwirq % NR_HW_IRQS;
>
> Create a hwirq_to_group helper.
>
>> +
>> +       msg->address_hi = upper_32_bits(msi->msi_addr +
>> +                                       (((8 * group) + reg_set) << 16));
>> +       msg->address_lo = lower_32_bits(msi->msi_addr +
>> +                                       (((8 * group) + reg_set) << 16));
>
> Rule of thumb: if you write something complicated twice, you've written
> one time too many:
>
> u64 target_addr = msi->msi_addr + ((8 * group) + reg_set) << 16));
>
> msg->address_hi = upper_32_bits(target_addr);
> msg->address_lo = lower_32_bits(target_addr);
>
> It doesn't hurt to write something readable.
>
>> +       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
>> +}
>> +
>> +/*
>> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
>> + * To maintain the expected behaviour of .set_affinity for each MSI
>> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
>> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
>> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
>> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
>> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
>> + */
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
>> +       int target_cpu = cpumask_first(mask);
>> +       int curr_cpu;
>> +
>> +       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
>
> Create a hwirq_to_cpu helper.
>
>> +       if (curr_cpu == target_cpu)
>> +               return IRQ_SET_MASK_OK_DONE;
>> +
>> +       /* Update MSI number to target the new CPU */
>> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
>> +
>> +       return IRQ_SET_MASK_OK;
>> +}
>> +
>> +static struct irq_chip xgene_msi_bottom_irq_chip = {
>> +       .name                   = "MSI",
>> +       .irq_set_affinity       = xgene_msi_set_affinity,
>> +       .irq_compose_msi_msg    = xgene_compose_msi_msg,
>> +};
>> +
>> +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
>> +                                 unsigned int nr_irqs, void *args)
>> +{
>> +       struct xgene_msi *msi = domain->host_data;
>> +       int msi_irq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
>> +                                            msi->num_cpus, 0);
>> +       if (msi_irq < NR_MSI_VEC)
>> +               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
>> +       else
>> +               msi_irq = -ENOSPC;
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       if (msi_irq < 0)
>> +               return msi_irq;
>> +
>> +       irq_domain_set_info(domain, virq, msi_irq,
>> +                           &xgene_msi_bottom_irq_chip, domain->host_data,
>> +                           handle_simple_irq, NULL, NULL);
>> +       set_irq_flags(virq, IRQF_VALID);
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_irq_domain_free(struct irq_domain *domain,
>> +                                 unsigned int virq, unsigned int nr_irqs)
>> +{
>> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
>> +       u32 hwirq;
>> +
>> +       mutex_lock(&msi->bitmap_lock);
>> +
>> +       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
>
> Create hwirq_to_canonical_hwirq helper (hwirq - hwirq_to_cpu(hwirq)).
>
>> +       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
>> +
>> +       mutex_unlock(&msi->bitmap_lock);
>> +
>> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops msi_domain_ops = {
>> +       .alloc  = xgene_irq_domain_alloc,
>> +       .free   = xgene_irq_domain_free,
>> +};
>> +
>> +static int xgene_allocate_domains(struct xgene_msi *msi)
>> +{
>> +       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
>> +                                           &msi_domain_ops, msi);
>> +       if (!msi->domain)
>> +               return -ENOMEM;
>> +
>> +       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
>> +                                                     &xgene_msi_domain_info,
>> +                                                     msi->domain);
>> +
>> +       if (!msi->mchip.domain) {
>> +               irq_domain_remove(msi->domain);
>> +               return -ENOMEM;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_free_domains(struct xgene_msi *msi)
>> +{
>> +       if (msi->mchip.domain)
>> +               irq_domain_remove(msi->mchip.domain);
>> +       if (msi->domain)
>> +               irq_domain_remove(msi->domain);
>> +}
>> +
>> +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
>> +{
>> +       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
>> +
>> +       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
>> +       if (!xgene_msi->bitmap)
>> +               return -ENOMEM;
>> +
>> +       mutex_init(&xgene_msi->bitmap_lock);
>> +
>> +       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
>> +                                       sizeof(struct xgene_msi_group),
>> +                                       GFP_KERNEL);
>> +       if (!xgene_msi->msi_groups)
>> +               return -ENOMEM;
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
>> +{
>> +       struct irq_chip *chip = irq_desc_get_chip(desc);
>> +       struct xgene_msi_group *msi_groups;
>> +       struct xgene_msi *xgene_msi;
>> +       unsigned int virq;
>> +       int msir_index, msir_val, hw_irq;
>> +       u32 intr_index, grp_select, msi_grp;
>> +
>> +       chained_irq_enter(chip, desc);
>> +
>> +       msi_groups = irq_desc_get_handler_data(desc);
>> +       xgene_msi = msi_groups->msi;
>> +       msi_grp = msi_groups->msi_grp;
>> +
>> +       /*
>> +        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
>> +        * If bit x of this register is set (x is 0..7), one or more interupts
>> +        * corresponding to MSInIRx is set.
>> +        */
>> +       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
>> +       while (grp_select) {
>> +               msir_index = ffs(grp_select) - 1;
>> +               /*
>> +                * Calculate MSInIRx address to read to check for interrupts
>> +                * (refer to termination address and data assignment
>> +                * described in xgene_compose_msi_msg function)
>> +                */
>> +               msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
>> +               while (msir_val) {
>> +                       intr_index = ffs(msir_val) - 1;
>> +                       /*
>> +                        * Calculate MSI vector number (refer to the termination
>> +                        * address and data assignment described in
>> +                        * xgene_compose_msi_msg function)
>> +                        */
>> +                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
>> +                                NR_HW_IRQS) + msi_grp;
>> +                       /*
>> +                        * As we have multiple hw_irq that maps to single MSI,
>> +                        * always look up the virq using the hw_irq as seen from
>> +                        * CPU0
>> +                        */
>> +                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
>
> hw_irq = hwirq_to_canonical_hwirq(hw_irq);
>
>> +                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
>> +                       WARN_ON(!virq);
>> +                       if (virq != 0)
>> +                               generic_handle_irq(virq);
>> +                       msir_val &= ~(1 << intr_index);
>> +               }
>> +               grp_select &= ~(1 << msir_index);
>> +
>> +               if (!grp_select) {
>> +                       /*
>> +                        * We handled all interrupts happened in this group,
>> +                        * resample this group MSI_INTx register in case
>> +                        * something else has been made pending in the meantime
>> +                        */
>> +                       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
>> +               }
>> +       }
>> +
>> +       chained_irq_exit(chip, desc);
>> +}
>> +
>> +static int xgene_msi_remove(struct platform_device *pdev)
>> +{
>> +       int virq, i;
>> +       struct xgene_msi *msi = platform_get_drvdata(pdev);
>> +
>> +       for (i = 0; i < NR_HW_IRQS; i++) {
>> +               virq = msi->msi_groups[i].gic_irq;
>> +               if (virq != 0) {
>> +                       irq_set_chained_handler(virq, NULL);
>> +                       irq_set_handler_data(virq, NULL);
>> +               }
>> +       }
>> +       kfree(msi->msi_groups);
>> +
>> +       kfree(msi->bitmap);
>> +       msi->bitmap = NULL;
>> +
>> +       xgene_free_domains(msi);
>> +
>> +       return 0;
>> +}
>> +
>> +static int xgene_msi_hwirq_alloc(unsigned int cpu)
>> +{
>> +       struct xgene_msi *msi = &xgene_msi_ctrl;
>> +       struct xgene_msi_group *msi_group;
>> +       cpumask_var_t mask;
>> +       int i;
>> +       int err;
>> +
>> +       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
>> +               msi_group = &msi->msi_groups[i];
>> +               if (!msi_group->gic_irq)
>> +                       continue;
>> +
>> +               irq_set_chained_handler(msi_group->gic_irq,
>> +                                       xgene_msi_isr);
>> +               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
>> +               if (err) {
>> +                       pr_err("failed to register GIC IRQ handler\n");
>> +                       return -EINVAL;
>> +               }
>> +               /*
>> +                * Statically allocate MSI GIC IRQs to each CPU core
>> +                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
>> +                * to each core.
>> +                */
>> +               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
>> +                       cpumask_clear(mask);
>> +                       cpumask_set_cpu(cpu, mask);
>> +                       err = irq_set_affinity(msi_group->gic_irq, mask);
>> +                       if (err)
>> +                               pr_err("failed to set affinity for GIC IRQ");
>> +                       free_cpumask_var(mask);
>> +               } else {
>> +                       pr_err("failed to alloc CPU mask for affinity\n");
>> +                       err = -EINVAL;
>> +               }
>> +
>> +               if (err) {
>> +                       irq_set_chained_handler(msi_group->gic_irq, NULL);
>> +                       irq_set_handler_data(msi_group->gic_irq, NULL);
>> +                       return -EINVAL;
>
>                         return err;
>
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void xgene_msi_hwirq_free(unsigned int cpu)
>> +{
>> +       struct xgene_msi *msi = &xgene_msi_ctrl;
>> +       struct xgene_msi_group *msi_group;
>> +       int i;
>> +
>> +       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
>> +               msi_group = &msi->msi_groups[i];
>> +               if (!msi_group->gic_irq)
>> +                       continue;
>> +
>> +               irq_set_chained_handler(msi_group->gic_irq, NULL);
>> +               irq_set_handler_data(msi_group->gic_irq, NULL);
>> +       }
>> +}
>> +
>> +static int xgene_msi_cpu_callback(struct notifier_block *nfb,
>> +                                 unsigned long action, void *hcpu)
>> +{
>> +       unsigned cpu = (unsigned long)hcpu;
>> +
>> +       switch (action) {
>> +       case CPU_ONLINE:
>> +       case CPU_ONLINE_FROZEN:
>> +               xgene_msi_hwirq_alloc(cpu);
>> +               break;
>> +       case CPU_DEAD:
>> +       case CPU_DEAD_FROZEN:
>> +               xgene_msi_hwirq_free(cpu);
>> +               break;
>> +       default:
>> +               break;
>> +       }
>> +
>> +       return NOTIFY_OK;
>> +}
>> +
>> +static struct notifier_block xgene_msi_cpu_notifier = {
>> +       .notifier_call = xgene_msi_cpu_callback,
>> +};
>> +
>> +static const struct of_device_id xgene_msi_match_table[] = {
>> +       {.compatible = "apm,xgene1-msi"},
>> +       {},
>> +};
>> +
>> +static int xgene_msi_probe(struct platform_device *pdev)
>> +{
>> +       struct resource *res;
>> +       int rc, irq_index;
>> +       struct xgene_msi *xgene_msi;
>> +       unsigned int cpu;
>> +       int virt_msir;
>> +       u32 msi_val, msi_idx;
>> +
>> +       xgene_msi = &xgene_msi_ctrl;
>> +
>> +       platform_set_drvdata(pdev, xgene_msi);
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(xgene_msi->msi_regs)) {
>> +               dev_err(&pdev->dev, "no reg space\n");
>> +               rc = -EINVAL;
>> +               goto error;
>> +       }
>> +       xgene_msi->msi_addr = res->start;
>> +
>> +       xgene_msi->num_cpus = num_possible_cpus();
>> +
>> +       rc = xgene_msi_init_allocator(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
>> +               goto error;
>> +       }
>> +
>> +       rc = xgene_allocate_domains(xgene_msi);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
>> +               goto error;
>> +       }
>> +
>> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
>> +               virt_msir = platform_get_irq(pdev, irq_index);
>> +               if (virt_msir < 0) {
>> +                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
>> +                               irq_index);
>> +                       rc = -EINVAL;
>> +                       goto error;
>> +               }
>> +               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
>> +               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
>> +               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
>> +       }
>> +
>> +       /*
>> +        * MSInIRx registers are read-to-clear, before registering interrupt
>> +        * handlers, reading all of them to clear all spurious interrupts
>> +        * that may occur before the driver is probed.
>> +        */
>> +       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
>> +               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
>> +                       msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
>> +                                                   msi_idx);
>> +               /* Read MSIINTn to confirm */
>> +               msi_val = xgene_msi_int_read(xgene_msi, irq_index);
>> +               if (msi_val) {
>> +                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
>> +                       rc = -EINVAL;
>> +                       goto error;
>> +               }
>> +       }
>> +
>> +       cpu_notifier_register_begin();
>> +
>> +       for_each_online_cpu(cpu)
>> +               if (xgene_msi_hwirq_alloc(cpu)) {
>> +                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
>> +                       cpu_notifier_register_done();
>> +                       goto error;
>> +               }
>> +
>> +       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
>> +               cpu_notifier_register_done();
>> +               goto error;
>> +       }
>> +
>> +       cpu_notifier_register_done();
>> +
>> +       xgene_msi->mchip.of_node = pdev->dev.of_node;
>> +       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
>> +       if (rc) {
>> +               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
>> +               goto error_notifier;
>> +       }
>> +
>> +       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
>> +
>> +       return 0;
>> +
>> +error_notifier:
>> +       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
>> +error:
>> +       xgene_msi_remove(pdev);
>> +       return rc;
>> +}
>> +
>> +static struct platform_driver xgene_msi_driver = {
>> +       .driver = {
>> +               .name = "xgene-msi",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = xgene_msi_match_table,
>> +       },
>> +       .probe = xgene_msi_probe,
>> +       .remove = xgene_msi_remove,
>> +};
>> +
>> +static int __init xgene_pcie_msi_init(void)
>> +{
>> +       return platform_driver_register(&xgene_msi_driver);
>> +}
>> +subsys_initcall(xgene_pcie_msi_init);
>> +
>> diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
>> index 22751ed..3e6faa1 100644
>> --- a/drivers/pci/host/pci-xgene.c
>> +++ b/drivers/pci/host/pci-xgene.c
>> @@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
>>         return 0;
>>  }
>>
>> +static int xgene_pcie_msi_enable(struct pci_bus *bus)
>> +{
>> +       struct device_node *msi_node;
>> +
>> +       msi_node = of_parse_phandle(bus->dev.of_node,
>> +                                       "msi-parent", 0);
>> +       if (!msi_node)
>> +               return -ENODEV;
>> +
>> +       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
>> +       if (bus->msi)
>> +               bus->msi->dev = &bus->dev;
>> +       else
>> +               return -ENODEV;
>> +       return 0;
>> +}
>> +
>>  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>  {
>>         struct device_node *dn = pdev->dev.of_node;
>> @@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
>>         if (!bus)
>>                 return -ENOMEM;
>>
>> +       if (IS_ENABLED(CONFIG_PCI_MSI))
>> +               if (xgene_pcie_msi_enable(bus))
>> +                       dev_info(port->dev, "failed to enable MSI\n");
>> +
>>         pci_scan_child_bus(bus);
>>         pci_assign_unassigned_bus_resources(bus);
>>         pci_bus_add_devices(bus);
>> --
>> 1.9.1
>>
>
> Once you've fixed the couple of nits above, you can add my
>
> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
>
> You should also CC the irqchip maintainers (Thomas and Jason), as I'd
> very much like to have their input on this as well. A few "Tested-by"
> wouldn't hurt either.
>
Hi Marc,

I added the changes you suggested into v9 patch set and also added
Thomas, Jason to the thread.

> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny.

Regards,
Duc Dang.

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

* Re: [PATCH v9 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-05-27 18:27                                                           ` Duc Dang
  (?)
@ 2015-05-28  8:05                                                             ` Marc Zyngier
  -1 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-05-28  8:05 UTC (permalink / raw)
  To: Duc Dang, Bjorn Helgaas, Arnd Bergmann, grant.likely,
	Liviu Dudau, Thomas Gleixner, Jason Cooper, Mark Rutland
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On 27/05/15 19:27, Duc Dang wrote:
> APM X-Gene v1 SoC supports its own implementation of MSI, which is not
> compliant to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
> This MSI block supports 2048 MSI termination ports coalesced into 16
> physical HW IRQ lines and shared across all 5 PCIe ports.
> 
> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support
> set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically
> allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI
> interrupt to target CPU, MSI vector is moved around these HW IRQs lines.
> With this approach, the total MSI vectors this driver supports is reduced
> to 256.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
>  drivers/pci/host/Kconfig         |  10 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 595 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 627 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 1dfb567..a93fda8 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,21 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +       default y
> +       help
> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
> +         This MSI driver will provide MSI support for 5 PCIe ports of
> +         APM X-Gene v1 SoC
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index f733b4e..1957431 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,6 +11,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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>  obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..461cb11
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,595 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi_group {
> +       struct xgene_msi                *msi;
> +       int                             gic_irq;
> +       u32                             msi_grp;
> +};
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       struct xgene_msi_group          *msi_groups;
> +       int                             num_cpus;
> +};
> +
> +/* Global data */
> +static struct xgene_msi xgene_msi_ctrl;
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + * MSIINT0                     base_addr + 0x800000
> + * MSIINT1                     base_addr + 0x810000
> + * ...                         ...
> + * MSIINTF                     base_addr + 0x8F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
> + * the MSI pending status caused by 1 of its 8 index registers.
> + */
> +
> +/* MSInIRx read helper */
> +static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
> +                                   u32 msi_grp, u32 msir_idx)
> +{
> +       return readl_relaxed(msi->msi_regs + MSI_IR0 +
> +                             (msi_grp << 19) + (msir_idx << 16));
> +}
> +
> +/* MSIINTn read helper */
> +static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
> +{
> +       return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> +}
> +
> +/* With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static inline u32 hwirq_to_reg_set(unsigned long hwirq)
> +{
> +       return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX));
> +}
> +
> +static inline u32 hwirq_to_group(unsigned long hwirq)
> +{
> +       return (hwirq % NR_HW_IRQS);
> +}
> +
> +static inline u32 hwirq_to_msi_data(unsigned long hwirq)
> +{
> +       return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX);
> +}

These "inline" are superfluous (including the ones for the HW
accessors). The compiler is bright enough to do it itself.

> +
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = hwirq_to_reg_set(data->hwirq);
> +       u32 group = hwirq_to_group(data->hwirq);
> +       u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16);
> +
> +       msg->address_hi = upper_32_bits(target_addr);
> +       msg->address_lo = lower_32_bits(target_addr);
> +       msg->data = hwirq_to_msi_data(data->hwirq);
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static inline int hwirq_to_cpu(unsigned long hwirq)
> +{
> +       return ((hwirq % NR_HW_IRQS) % xgene_msi_ctrl.num_cpus);
> +}
> +
> +static inline unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq)
> +{
> +       return (hwirq - (hwirq % xgene_msi_ctrl.num_cpus));
> +}

Hmmm. This is weird. The canonical hwirq is defined as the IRQ seen from
CPU0. So it is possible to write hwirq_to_canonical_hwirq as:

static irq_hw_number_t hwirq_to_canonical_hwirq(irq_hw_number_t hwirq)
{
	return hwirq - hwirq_to_cpu(hwirq);
}

But you've defined hwirq_to_cpu as "(hwirq % NR_HW_IRQS) %
xgene_msi_ctrl.num_cpus", which doesn't match your definition of a
canonical hwirq. Can you explain the discrepancy?

> +
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +
> +       curr_cpu = hwirq_to_cpu(irq_data->hwirq);
> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       /* Update MSI number to target the new CPU */
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);

irq_data->hwirq = hwirq_to_canonical_hwirq(irq_data->hwirq) + target_cpu;

> +
> +       return IRQ_SET_MASK_OK;
> +}

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH v9 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-28  8:05                                                             ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-05-28  8:05 UTC (permalink / raw)
  To: Duc Dang, Bjorn Helgaas, Arnd Bergmann, grant.likely,
	Liviu Dudau, Thomas Gleixner, Jason Cooper, Mark Rutland
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan

On 27/05/15 19:27, Duc Dang wrote:
> APM X-Gene v1 SoC supports its own implementation of MSI, which is not
> compliant to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
> This MSI block supports 2048 MSI termination ports coalesced into 16
> physical HW IRQ lines and shared across all 5 PCIe ports.
> 
> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support
> set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically
> allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI
> interrupt to target CPU, MSI vector is moved around these HW IRQs lines.
> With this approach, the total MSI vectors this driver supports is reduced
> to 256.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
>  drivers/pci/host/Kconfig         |  10 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 595 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 627 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 1dfb567..a93fda8 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,21 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +       default y
> +       help
> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
> +         This MSI driver will provide MSI support for 5 PCIe ports of
> +         APM X-Gene v1 SoC
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index f733b4e..1957431 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,6 +11,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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>  obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..461cb11
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,595 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi_group {
> +       struct xgene_msi                *msi;
> +       int                             gic_irq;
> +       u32                             msi_grp;
> +};
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       struct xgene_msi_group          *msi_groups;
> +       int                             num_cpus;
> +};
> +
> +/* Global data */
> +static struct xgene_msi xgene_msi_ctrl;
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + * MSIINT0                     base_addr + 0x800000
> + * MSIINT1                     base_addr + 0x810000
> + * ...                         ...
> + * MSIINTF                     base_addr + 0x8F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
> + * the MSI pending status caused by 1 of its 8 index registers.
> + */
> +
> +/* MSInIRx read helper */
> +static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
> +                                   u32 msi_grp, u32 msir_idx)
> +{
> +       return readl_relaxed(msi->msi_regs + MSI_IR0 +
> +                             (msi_grp << 19) + (msir_idx << 16));
> +}
> +
> +/* MSIINTn read helper */
> +static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
> +{
> +       return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> +}
> +
> +/* With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static inline u32 hwirq_to_reg_set(unsigned long hwirq)
> +{
> +       return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX));
> +}
> +
> +static inline u32 hwirq_to_group(unsigned long hwirq)
> +{
> +       return (hwirq % NR_HW_IRQS);
> +}
> +
> +static inline u32 hwirq_to_msi_data(unsigned long hwirq)
> +{
> +       return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX);
> +}

These "inline" are superfluous (including the ones for the HW
accessors). The compiler is bright enough to do it itself.

> +
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = hwirq_to_reg_set(data->hwirq);
> +       u32 group = hwirq_to_group(data->hwirq);
> +       u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16);
> +
> +       msg->address_hi = upper_32_bits(target_addr);
> +       msg->address_lo = lower_32_bits(target_addr);
> +       msg->data = hwirq_to_msi_data(data->hwirq);
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static inline int hwirq_to_cpu(unsigned long hwirq)
> +{
> +       return ((hwirq % NR_HW_IRQS) % xgene_msi_ctrl.num_cpus);
> +}
> +
> +static inline unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq)
> +{
> +       return (hwirq - (hwirq % xgene_msi_ctrl.num_cpus));
> +}

Hmmm. This is weird. The canonical hwirq is defined as the IRQ seen from
CPU0. So it is possible to write hwirq_to_canonical_hwirq as:

static irq_hw_number_t hwirq_to_canonical_hwirq(irq_hw_number_t hwirq)
{
	return hwirq - hwirq_to_cpu(hwirq);
}

But you've defined hwirq_to_cpu as "(hwirq % NR_HW_IRQS) %
xgene_msi_ctrl.num_cpus", which doesn't match your definition of a
canonical hwirq. Can you explain the discrepancy?

> +
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +
> +       curr_cpu = hwirq_to_cpu(irq_data->hwirq);
> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       /* Update MSI number to target the new CPU */
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);

irq_data->hwirq = hwirq_to_canonical_hwirq(irq_data->hwirq) + target_cpu;

> +
> +       return IRQ_SET_MASK_OK;
> +}

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* [PATCH v9 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-28  8:05                                                             ` Marc Zyngier
  0 siblings, 0 replies; 230+ messages in thread
From: Marc Zyngier @ 2015-05-28  8:05 UTC (permalink / raw)
  To: linux-arm-kernel

On 27/05/15 19:27, Duc Dang wrote:
> APM X-Gene v1 SoC supports its own implementation of MSI, which is not
> compliant to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
> This MSI block supports 2048 MSI termination ports coalesced into 16
> physical HW IRQ lines and shared across all 5 PCIe ports.
> 
> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support
> set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically
> allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI
> interrupt to target CPU, MSI vector is moved around these HW IRQs lines.
> With this approach, the total MSI vectors this driver supports is reduced
> to 256.
> 
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
>  drivers/pci/host/Kconfig         |  10 +
>  drivers/pci/host/Makefile        |   1 +
>  drivers/pci/host/pci-xgene-msi.c | 595 +++++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c     |  21 ++
>  4 files changed, 627 insertions(+)
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 1dfb567..a93fda8 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -89,11 +89,21 @@ config PCI_XGENE
>         depends on ARCH_XGENE
>         depends on OF
>         select PCIEPORTBUS
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>         help
>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>           and have varied lanes from x1 to x8.
> 
> +config PCI_XGENE_MSI
> +       bool "X-Gene v1 PCIe MSI feature"
> +       depends on PCI_XGENE && PCI_MSI
> +       default y
> +       help
> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
> +         This MSI driver will provide MSI support for 5 PCIe ports of
> +         APM X-Gene v1 SoC
> +
>  config PCI_LAYERSCAPE
>         bool "Freescale Layerscape PCIe controller"
>         depends on OF && ARM
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index f733b4e..1957431 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -11,6 +11,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_PCI_XGENE) += pci-xgene.o
> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>  obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> new file mode 100644
> index 0000000..461cb11
> --- /dev/null
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -0,0 +1,595 @@
> +/*
> + * APM X-Gene MSI Driver
> + *
> + * Copyright (c) 2014, Applied Micro Circuits Corporation
> + * Author: Tanmay Inamdar <tinamdar@apm.com>
> + *        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 as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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.
> + */
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_pci.h>
> +
> +#define MSI_IR0                        0x000000
> +#define MSI_INT0               0x800000
> +#define IDX_PER_GROUP          8
> +#define IRQS_PER_IDX           16
> +#define NR_HW_IRQS             16
> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
> +
> +struct xgene_msi_group {
> +       struct xgene_msi                *msi;
> +       int                             gic_irq;
> +       u32                             msi_grp;
> +};
> +
> +struct xgene_msi {
> +       struct device_node              *node;
> +       struct msi_controller           mchip;
> +       struct irq_domain               *domain;
> +       u64                             msi_addr;
> +       void __iomem                    *msi_regs;
> +       unsigned long                   *bitmap;
> +       struct mutex                    bitmap_lock;
> +       struct xgene_msi_group          *msi_groups;
> +       int                             num_cpus;
> +};
> +
> +/* Global data */
> +static struct xgene_msi xgene_msi_ctrl;
> +
> +static struct irq_chip xgene_msi_top_irq_chip = {
> +       .name           = "X-Gene1 MSI",
> +       .irq_enable     = pci_msi_unmask_irq,
> +       .irq_disable    = pci_msi_mask_irq,
> +       .irq_mask       = pci_msi_mask_irq,
> +       .irq_unmask     = pci_msi_unmask_irq,
> +};
> +
> +static struct  msi_domain_info xgene_msi_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                 MSI_FLAG_PCI_MSIX),
> +       .chip   = &xgene_msi_top_irq_chip,
> +};
> +
> +/*
> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
> + * n is group number (0..F), x is index of registers in each group (0..7)
> + * The registers layout is like following:
> + * MSI0IR0                     base_addr
> + * MSI0IR1                     base_addr +  0x10000
> + * ...                         ...
> + * MSI0IR6                     base_addr +  0x60000
> + * MSI0IR7                     base_addr +  0x70000
> + * MSI1IR0                     base_addr +  0x80000
> + * MSI1IR1                     base_addr +  0x90000
> + * ...                         ...
> + * MSI1IR7                     base_addr +  0xF0000
> + * MSI2IR0                     base_addr + 0x100000
> + * ...                         ...
> + * MSIFIR0                     base_addr + 0x780000
> + * MSIFIR1                     base_addr + 0x790000
> + * ...                         ...
> + * MSIFIR7                     base_addr + 0x7F0000
> + * MSIINT0                     base_addr + 0x800000
> + * MSIINT1                     base_addr + 0x810000
> + * ...                         ...
> + * MSIINTF                     base_addr + 0x8F0000
> + *
> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
> + * registers.
> + *
> + * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
> + * the MSI pending status caused by 1 of its 8 index registers.
> + */
> +
> +/* MSInIRx read helper */
> +static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
> +                                   u32 msi_grp, u32 msir_idx)
> +{
> +       return readl_relaxed(msi->msi_regs + MSI_IR0 +
> +                             (msi_grp << 19) + (msir_idx << 16));
> +}
> +
> +/* MSIINTn read helper */
> +static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
> +{
> +       return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
> +}
> +
> +/* With 2048 MSI vectors supported, the MSI message can be construct using
> + * following scheme:
> + * - Divide into 8 256-vector groups
> + *             Group 0: 0-255
> + *             Group 1: 256-511
> + *             Group 2: 512-767
> + *             ...
> + *             Group 7: 1792-2047
> + * - Each 256-vector group is divided into 16 16-vector groups
> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
> + *             Group 0: 0-15
> + *             Group 1: 16-32
> + *             ...
> + *             Group 15: 240-255
> + * - The termination address of MSI vector in 256-vector group n and 16-vector
> + * group x is the address of MSIxIRn
> + * - The data for MSI vector in 16-vector group x is x
> + */
> +static inline u32 hwirq_to_reg_set(unsigned long hwirq)
> +{
> +       return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX));
> +}
> +
> +static inline u32 hwirq_to_group(unsigned long hwirq)
> +{
> +       return (hwirq % NR_HW_IRQS);
> +}
> +
> +static inline u32 hwirq_to_msi_data(unsigned long hwirq)
> +{
> +       return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX);
> +}

These "inline" are superfluous (including the ones for the HW
accessors). The compiler is bright enough to do it itself.

> +
> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
> +       u32 reg_set = hwirq_to_reg_set(data->hwirq);
> +       u32 group = hwirq_to_group(data->hwirq);
> +       u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16);
> +
> +       msg->address_hi = upper_32_bits(target_addr);
> +       msg->address_lo = lower_32_bits(target_addr);
> +       msg->data = hwirq_to_msi_data(data->hwirq);
> +}
> +
> +/*
> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
> + * To maintain the expected behaviour of .set_affinity for each MSI
> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
> + */
> +static inline int hwirq_to_cpu(unsigned long hwirq)
> +{
> +       return ((hwirq % NR_HW_IRQS) % xgene_msi_ctrl.num_cpus);
> +}
> +
> +static inline unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq)
> +{
> +       return (hwirq - (hwirq % xgene_msi_ctrl.num_cpus));
> +}

Hmmm. This is weird. The canonical hwirq is defined as the IRQ seen from
CPU0. So it is possible to write hwirq_to_canonical_hwirq as:

static irq_hw_number_t hwirq_to_canonical_hwirq(irq_hw_number_t hwirq)
{
	return hwirq - hwirq_to_cpu(hwirq);
}

But you've defined hwirq_to_cpu as "(hwirq % NR_HW_IRQS) %
xgene_msi_ctrl.num_cpus", which doesn't match your definition of a
canonical hwirq. Can you explain the discrepancy?

> +
> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
> +                                 const struct cpumask *mask, bool force)
> +{
> +       int target_cpu = cpumask_first(mask);
> +       int curr_cpu;
> +
> +       curr_cpu = hwirq_to_cpu(irq_data->hwirq);
> +       if (curr_cpu == target_cpu)
> +               return IRQ_SET_MASK_OK_DONE;
> +
> +       /* Update MSI number to target the new CPU */
> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);

irq_data->hwirq = hwirq_to_canonical_hwirq(irq_data->hwirq) + target_cpu;

> +
> +       return IRQ_SET_MASK_OK;
> +}

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH v9 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-05-28  8:05                                                             ` Marc Zyngier
  (?)
@ 2015-05-28 17:16                                                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-28 17:16 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	Thomas Gleixner, Jason Cooper, Mark Rutland, linux-pci,
	linux-arm-kernel, linux-kernel, Tanmay Inamdar, Loc Ho, Feng Kan

On Thu, May 28, 2015 at 1:05 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 27/05/15 19:27, Duc Dang wrote:
>> APM X-Gene v1 SoC supports its own implementation of MSI, which is not
>> compliant to GIC V2M specification for MSI Termination.
>>
>> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
>> This MSI block supports 2048 MSI termination ports coalesced into 16
>> physical HW IRQ lines and shared across all 5 PCIe ports.
>>
>> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support
>> set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically
>> allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI
>> interrupt to target CPU, MSI vector is moved around these HW IRQs lines.
>> With this approach, the total MSI vectors this driver supports is reduced
>> to 256.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
>> ---
>>  drivers/pci/host/Kconfig         |  10 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 595 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 627 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 1dfb567..a93fda8 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,21 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +       default y
>> +       help
>> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
>> +         This MSI driver will provide MSI support for 5 PCIe ports of
>> +         APM X-Gene v1 SoC
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index f733b4e..1957431 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,6 +11,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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>  obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..461cb11
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,595 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/cpu.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_IR0                        0x000000
>> +#define MSI_INT0               0x800000
>> +#define IDX_PER_GROUP          8
>> +#define IRQS_PER_IDX           16
>> +#define NR_HW_IRQS             16
>> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
>> +
>> +struct xgene_msi_group {
>> +       struct xgene_msi                *msi;
>> +       int                             gic_irq;
>> +       u32                             msi_grp;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       u64                             msi_addr;
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       struct xgene_msi_group          *msi_groups;
>> +       int                             num_cpus;
>> +};
>> +
>> +/* Global data */
>> +static struct xgene_msi xgene_msi_ctrl;
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +/*
>> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
>> + * n is group number (0..F), x is index of registers in each group (0..7)
>> + * The registers layout is like following:
>> + * MSI0IR0                     base_addr
>> + * MSI0IR1                     base_addr +  0x10000
>> + * ...                         ...
>> + * MSI0IR6                     base_addr +  0x60000
>> + * MSI0IR7                     base_addr +  0x70000
>> + * MSI1IR0                     base_addr +  0x80000
>> + * MSI1IR1                     base_addr +  0x90000
>> + * ...                         ...
>> + * MSI1IR7                     base_addr +  0xF0000
>> + * MSI2IR0                     base_addr + 0x100000
>> + * ...                         ...
>> + * MSIFIR0                     base_addr + 0x780000
>> + * MSIFIR1                     base_addr + 0x790000
>> + * ...                         ...
>> + * MSIFIR7                     base_addr + 0x7F0000
>> + * MSIINT0                     base_addr + 0x800000
>> + * MSIINT1                     base_addr + 0x810000
>> + * ...                         ...
>> + * MSIINTF                     base_addr + 0x8F0000
>> + *
>> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
>> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
>> + * registers.
>> + *
>> + * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
>> + * the MSI pending status caused by 1 of its 8 index registers.
>> + */
>> +
>> +/* MSInIRx read helper */
>> +static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
>> +                                   u32 msi_grp, u32 msir_idx)
>> +{
>> +       return readl_relaxed(msi->msi_regs + MSI_IR0 +
>> +                             (msi_grp << 19) + (msir_idx << 16));
>> +}
>> +
>> +/* MSIINTn read helper */
>> +static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
>> +{
>> +       return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +}
>> +
>> +/* With 2048 MSI vectors supported, the MSI message can be construct using
>> + * following scheme:
>> + * - Divide into 8 256-vector groups
>> + *             Group 0: 0-255
>> + *             Group 1: 256-511
>> + *             Group 2: 512-767
>> + *             ...
>> + *             Group 7: 1792-2047
>> + * - Each 256-vector group is divided into 16 16-vector groups
>> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
>> + *             Group 0: 0-15
>> + *             Group 1: 16-32
>> + *             ...
>> + *             Group 15: 240-255
>> + * - The termination address of MSI vector in 256-vector group n and 16-vector
>> + * group x is the address of MSIxIRn
>> + * - The data for MSI vector in 16-vector group x is x
>> + */
>> +static inline u32 hwirq_to_reg_set(unsigned long hwirq)
>> +{
>> +       return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX));
>> +}
>> +
>> +static inline u32 hwirq_to_group(unsigned long hwirq)
>> +{
>> +       return (hwirq % NR_HW_IRQS);
>> +}
>> +
>> +static inline u32 hwirq_to_msi_data(unsigned long hwirq)
>> +{
>> +       return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX);
>> +}
>
> These "inline" are superfluous (including the ones for the HW
> accessors). The compiler is bright enough to do it itself.

I will remove these "inline"
>
>> +
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 reg_set = hwirq_to_reg_set(data->hwirq);
>> +       u32 group = hwirq_to_group(data->hwirq);
>> +       u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16);
>> +
>> +       msg->address_hi = upper_32_bits(target_addr);
>> +       msg->address_lo = lower_32_bits(target_addr);
>> +       msg->data = hwirq_to_msi_data(data->hwirq);
>> +}
>> +
>> +/*
>> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
>> + * To maintain the expected behaviour of .set_affinity for each MSI
>> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
>> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
>> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
>> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
>> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
>> + */
>> +static inline int hwirq_to_cpu(unsigned long hwirq)
>> +{
>> +       return ((hwirq % NR_HW_IRQS) % xgene_msi_ctrl.num_cpus);
>> +}
>> +
>> +static inline unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq)
>> +{
>> +       return (hwirq - (hwirq % xgene_msi_ctrl.num_cpus));
>> +}
>
> Hmmm. This is weird. The canonical hwirq is defined as the IRQ seen from
> CPU0. So it is possible to write hwirq_to_canonical_hwirq as:
>
> static irq_hw_number_t hwirq_to_canonical_hwirq(irq_hw_number_t hwirq)
> {
>         return hwirq - hwirq_to_cpu(hwirq);
> }
>
> But you've defined hwirq_to_cpu as "(hwirq % NR_HW_IRQS) %
> xgene_msi_ctrl.num_cpus", which doesn't match your definition of a
> canonical hwirq. Can you explain the discrepancy?
>
In "(hwirq % NR_HW_IRQS) % xgene_msi_ctrl.num_cpus", I looked for the
group IRQ that the hwirq belongs to first, then figure out the CPU
that this group IRQ assigned to. With hwirq allocation scheme that is
using in this driver, defining hwirq_to_cpu as "hwirq %
xgene_msi_ctrl.num_cpus" also provides the same result. So I will
change it and use hwirq_to_cpu in hwirq_to_canonical_hwirq
implementation.

>> +
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       int target_cpu = cpumask_first(mask);
>> +       int curr_cpu;
>> +
>> +       curr_cpu = hwirq_to_cpu(irq_data->hwirq);
>> +       if (curr_cpu == target_cpu)
>> +               return IRQ_SET_MASK_OK_DONE;
>> +
>> +       /* Update MSI number to target the new CPU */
>> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
>
> irq_data->hwirq = hwirq_to_canonical_hwirq(irq_data->hwirq) + target_cpu;
>
>> +
>> +       return IRQ_SET_MASK_OK;
>> +}
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...
Regards,
Duc Dang.

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

* Re: [PATCH v9 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-28 17:16                                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-28 17:16 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Bjorn Helgaas, Arnd Bergmann, grant.likely, Liviu Dudau,
	Thomas Gleixner, Jason Cooper, Mark Rutland, linux-pci,
	linux-arm-kernel, linux-kernel, Tanmay Inamdar, Loc Ho, Feng Kan

On Thu, May 28, 2015 at 1:05 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 27/05/15 19:27, Duc Dang wrote:
>> APM X-Gene v1 SoC supports its own implementation of MSI, which is not
>> compliant to GIC V2M specification for MSI Termination.
>>
>> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
>> This MSI block supports 2048 MSI termination ports coalesced into 16
>> physical HW IRQ lines and shared across all 5 PCIe ports.
>>
>> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support
>> set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically
>> allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI
>> interrupt to target CPU, MSI vector is moved around these HW IRQs lines.
>> With this approach, the total MSI vectors this driver supports is reduced
>> to 256.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
>> ---
>>  drivers/pci/host/Kconfig         |  10 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 595 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 627 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 1dfb567..a93fda8 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,21 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +       default y
>> +       help
>> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
>> +         This MSI driver will provide MSI support for 5 PCIe ports of
>> +         APM X-Gene v1 SoC
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index f733b4e..1957431 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,6 +11,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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>  obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..461cb11
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,595 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/cpu.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_IR0                        0x000000
>> +#define MSI_INT0               0x800000
>> +#define IDX_PER_GROUP          8
>> +#define IRQS_PER_IDX           16
>> +#define NR_HW_IRQS             16
>> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
>> +
>> +struct xgene_msi_group {
>> +       struct xgene_msi                *msi;
>> +       int                             gic_irq;
>> +       u32                             msi_grp;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       u64                             msi_addr;
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       struct xgene_msi_group          *msi_groups;
>> +       int                             num_cpus;
>> +};
>> +
>> +/* Global data */
>> +static struct xgene_msi xgene_msi_ctrl;
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +/*
>> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
>> + * n is group number (0..F), x is index of registers in each group (0..7)
>> + * The registers layout is like following:
>> + * MSI0IR0                     base_addr
>> + * MSI0IR1                     base_addr +  0x10000
>> + * ...                         ...
>> + * MSI0IR6                     base_addr +  0x60000
>> + * MSI0IR7                     base_addr +  0x70000
>> + * MSI1IR0                     base_addr +  0x80000
>> + * MSI1IR1                     base_addr +  0x90000
>> + * ...                         ...
>> + * MSI1IR7                     base_addr +  0xF0000
>> + * MSI2IR0                     base_addr + 0x100000
>> + * ...                         ...
>> + * MSIFIR0                     base_addr + 0x780000
>> + * MSIFIR1                     base_addr + 0x790000
>> + * ...                         ...
>> + * MSIFIR7                     base_addr + 0x7F0000
>> + * MSIINT0                     base_addr + 0x800000
>> + * MSIINT1                     base_addr + 0x810000
>> + * ...                         ...
>> + * MSIINTF                     base_addr + 0x8F0000
>> + *
>> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
>> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
>> + * registers.
>> + *
>> + * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
>> + * the MSI pending status caused by 1 of its 8 index registers.
>> + */
>> +
>> +/* MSInIRx read helper */
>> +static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
>> +                                   u32 msi_grp, u32 msir_idx)
>> +{
>> +       return readl_relaxed(msi->msi_regs + MSI_IR0 +
>> +                             (msi_grp << 19) + (msir_idx << 16));
>> +}
>> +
>> +/* MSIINTn read helper */
>> +static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
>> +{
>> +       return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +}
>> +
>> +/* With 2048 MSI vectors supported, the MSI message can be construct using
>> + * following scheme:
>> + * - Divide into 8 256-vector groups
>> + *             Group 0: 0-255
>> + *             Group 1: 256-511
>> + *             Group 2: 512-767
>> + *             ...
>> + *             Group 7: 1792-2047
>> + * - Each 256-vector group is divided into 16 16-vector groups
>> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
>> + *             Group 0: 0-15
>> + *             Group 1: 16-32
>> + *             ...
>> + *             Group 15: 240-255
>> + * - The termination address of MSI vector in 256-vector group n and 16-vector
>> + * group x is the address of MSIxIRn
>> + * - The data for MSI vector in 16-vector group x is x
>> + */
>> +static inline u32 hwirq_to_reg_set(unsigned long hwirq)
>> +{
>> +       return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX));
>> +}
>> +
>> +static inline u32 hwirq_to_group(unsigned long hwirq)
>> +{
>> +       return (hwirq % NR_HW_IRQS);
>> +}
>> +
>> +static inline u32 hwirq_to_msi_data(unsigned long hwirq)
>> +{
>> +       return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX);
>> +}
>
> These "inline" are superfluous (including the ones for the HW
> accessors). The compiler is bright enough to do it itself.

I will remove these "inline"
>
>> +
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 reg_set = hwirq_to_reg_set(data->hwirq);
>> +       u32 group = hwirq_to_group(data->hwirq);
>> +       u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16);
>> +
>> +       msg->address_hi = upper_32_bits(target_addr);
>> +       msg->address_lo = lower_32_bits(target_addr);
>> +       msg->data = hwirq_to_msi_data(data->hwirq);
>> +}
>> +
>> +/*
>> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
>> + * To maintain the expected behaviour of .set_affinity for each MSI
>> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
>> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
>> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
>> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
>> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
>> + */
>> +static inline int hwirq_to_cpu(unsigned long hwirq)
>> +{
>> +       return ((hwirq % NR_HW_IRQS) % xgene_msi_ctrl.num_cpus);
>> +}
>> +
>> +static inline unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq)
>> +{
>> +       return (hwirq - (hwirq % xgene_msi_ctrl.num_cpus));
>> +}
>
> Hmmm. This is weird. The canonical hwirq is defined as the IRQ seen from
> CPU0. So it is possible to write hwirq_to_canonical_hwirq as:
>
> static irq_hw_number_t hwirq_to_canonical_hwirq(irq_hw_number_t hwirq)
> {
>         return hwirq - hwirq_to_cpu(hwirq);
> }
>
> But you've defined hwirq_to_cpu as "(hwirq % NR_HW_IRQS) %
> xgene_msi_ctrl.num_cpus", which doesn't match your definition of a
> canonical hwirq. Can you explain the discrepancy?
>
In "(hwirq % NR_HW_IRQS) % xgene_msi_ctrl.num_cpus", I looked for the
group IRQ that the hwirq belongs to first, then figure out the CPU
that this group IRQ assigned to. With hwirq allocation scheme that is
using in this driver, defining hwirq_to_cpu as "hwirq %
xgene_msi_ctrl.num_cpus" also provides the same result. So I will
change it and use hwirq_to_cpu in hwirq_to_canonical_hwirq
implementation.

>> +
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       int target_cpu = cpumask_first(mask);
>> +       int curr_cpu;
>> +
>> +       curr_cpu = hwirq_to_cpu(irq_data->hwirq);
>> +       if (curr_cpu == target_cpu)
>> +               return IRQ_SET_MASK_OK_DONE;
>> +
>> +       /* Update MSI number to target the new CPU */
>> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
>
> irq_data->hwirq = hwirq_to_canonical_hwirq(irq_data->hwirq) + target_cpu;
>
>> +
>> +       return IRQ_SET_MASK_OK;
>> +}
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...
Regards,
Duc Dang.

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

* [PATCH v9 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-28 17:16                                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-28 17:16 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, May 28, 2015 at 1:05 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 27/05/15 19:27, Duc Dang wrote:
>> APM X-Gene v1 SoC supports its own implementation of MSI, which is not
>> compliant to GIC V2M specification for MSI Termination.
>>
>> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
>> This MSI block supports 2048 MSI termination ports coalesced into 16
>> physical HW IRQ lines and shared across all 5 PCIe ports.
>>
>> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support
>> set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically
>> allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI
>> interrupt to target CPU, MSI vector is moved around these HW IRQs lines.
>> With this approach, the total MSI vectors this driver supports is reduced
>> to 256.
>>
>> Signed-off-by: Duc Dang <dhdang@apm.com>
>> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
>> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
>> ---
>>  drivers/pci/host/Kconfig         |  10 +
>>  drivers/pci/host/Makefile        |   1 +
>>  drivers/pci/host/pci-xgene-msi.c | 595 +++++++++++++++++++++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c     |  21 ++
>>  4 files changed, 627 insertions(+)
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 1dfb567..a93fda8 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -89,11 +89,21 @@ config PCI_XGENE
>>         depends on ARCH_XGENE
>>         depends on OF
>>         select PCIEPORTBUS
>> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>>         help
>>           Say Y here if you want internal PCI support on APM X-Gene SoC.
>>           There are 5 internal PCIe ports available. Each port is GEN3 capable
>>           and have varied lanes from x1 to x8.
>>
>> +config PCI_XGENE_MSI
>> +       bool "X-Gene v1 PCIe MSI feature"
>> +       depends on PCI_XGENE && PCI_MSI
>> +       default y
>> +       help
>> +         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
>> +         This MSI driver will provide MSI support for 5 PCIe ports of
>> +         APM X-Gene v1 SoC
>> +
>>  config PCI_LAYERSCAPE
>>         bool "Freescale Layerscape PCIe controller"
>>         depends on OF && ARM
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index f733b4e..1957431 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -11,6 +11,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_PCI_XGENE) += pci-xgene.o
>> +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
>>  obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
>>  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
>>  obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> new file mode 100644
>> index 0000000..461cb11
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -0,0 +1,595 @@
>> +/*
>> + * APM X-Gene MSI Driver
>> + *
>> + * Copyright (c) 2014, Applied Micro Circuits Corporation
>> + * Author: Tanmay Inamdar <tinamdar@apm.com>
>> + *        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 as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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.
>> + */
>> +#include <linux/cpu.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of_pci.h>
>> +
>> +#define MSI_IR0                        0x000000
>> +#define MSI_INT0               0x800000
>> +#define IDX_PER_GROUP          8
>> +#define IRQS_PER_IDX           16
>> +#define NR_HW_IRQS             16
>> +#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
>> +
>> +struct xgene_msi_group {
>> +       struct xgene_msi                *msi;
>> +       int                             gic_irq;
>> +       u32                             msi_grp;
>> +};
>> +
>> +struct xgene_msi {
>> +       struct device_node              *node;
>> +       struct msi_controller           mchip;
>> +       struct irq_domain               *domain;
>> +       u64                             msi_addr;
>> +       void __iomem                    *msi_regs;
>> +       unsigned long                   *bitmap;
>> +       struct mutex                    bitmap_lock;
>> +       struct xgene_msi_group          *msi_groups;
>> +       int                             num_cpus;
>> +};
>> +
>> +/* Global data */
>> +static struct xgene_msi xgene_msi_ctrl;
>> +
>> +static struct irq_chip xgene_msi_top_irq_chip = {
>> +       .name           = "X-Gene1 MSI",
>> +       .irq_enable     = pci_msi_unmask_irq,
>> +       .irq_disable    = pci_msi_mask_irq,
>> +       .irq_mask       = pci_msi_mask_irq,
>> +       .irq_unmask     = pci_msi_unmask_irq,
>> +};
>> +
>> +static struct  msi_domain_info xgene_msi_domain_info = {
>> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +                 MSI_FLAG_PCI_MSIX),
>> +       .chip   = &xgene_msi_top_irq_chip,
>> +};
>> +
>> +/*
>> + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
>> + * n is group number (0..F), x is index of registers in each group (0..7)
>> + * The registers layout is like following:
>> + * MSI0IR0                     base_addr
>> + * MSI0IR1                     base_addr +  0x10000
>> + * ...                         ...
>> + * MSI0IR6                     base_addr +  0x60000
>> + * MSI0IR7                     base_addr +  0x70000
>> + * MSI1IR0                     base_addr +  0x80000
>> + * MSI1IR1                     base_addr +  0x90000
>> + * ...                         ...
>> + * MSI1IR7                     base_addr +  0xF0000
>> + * MSI2IR0                     base_addr + 0x100000
>> + * ...                         ...
>> + * MSIFIR0                     base_addr + 0x780000
>> + * MSIFIR1                     base_addr + 0x790000
>> + * ...                         ...
>> + * MSIFIR7                     base_addr + 0x7F0000
>> + * MSIINT0                     base_addr + 0x800000
>> + * MSIINT1                     base_addr + 0x810000
>> + * ...                         ...
>> + * MSIINTF                     base_addr + 0x8F0000
>> + *
>> + * Each index register support 16 MSI vectors (0..15) to generate interrupt.
>> + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
>> + * registers.
>> + *
>> + * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
>> + * the MSI pending status caused by 1 of its 8 index registers.
>> + */
>> +
>> +/* MSInIRx read helper */
>> +static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
>> +                                   u32 msi_grp, u32 msir_idx)
>> +{
>> +       return readl_relaxed(msi->msi_regs + MSI_IR0 +
>> +                             (msi_grp << 19) + (msir_idx << 16));
>> +}
>> +
>> +/* MSIINTn read helper */
>> +static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
>> +{
>> +       return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
>> +}
>> +
>> +/* With 2048 MSI vectors supported, the MSI message can be construct using
>> + * following scheme:
>> + * - Divide into 8 256-vector groups
>> + *             Group 0: 0-255
>> + *             Group 1: 256-511
>> + *             Group 2: 512-767
>> + *             ...
>> + *             Group 7: 1792-2047
>> + * - Each 256-vector group is divided into 16 16-vector groups
>> + *     As an example: 16 16-vector groups for 256-vector group 0-255 is
>> + *             Group 0: 0-15
>> + *             Group 1: 16-32
>> + *             ...
>> + *             Group 15: 240-255
>> + * - The termination address of MSI vector in 256-vector group n and 16-vector
>> + * group x is the address of MSIxIRn
>> + * - The data for MSI vector in 16-vector group x is x
>> + */
>> +static inline u32 hwirq_to_reg_set(unsigned long hwirq)
>> +{
>> +       return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX));
>> +}
>> +
>> +static inline u32 hwirq_to_group(unsigned long hwirq)
>> +{
>> +       return (hwirq % NR_HW_IRQS);
>> +}
>> +
>> +static inline u32 hwirq_to_msi_data(unsigned long hwirq)
>> +{
>> +       return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX);
>> +}
>
> These "inline" are superfluous (including the ones for the HW
> accessors). The compiler is bright enough to do it itself.

I will remove these "inline"
>
>> +
>> +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
>> +       u32 reg_set = hwirq_to_reg_set(data->hwirq);
>> +       u32 group = hwirq_to_group(data->hwirq);
>> +       u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16);
>> +
>> +       msg->address_hi = upper_32_bits(target_addr);
>> +       msg->address_lo = lower_32_bits(target_addr);
>> +       msg->data = hwirq_to_msi_data(data->hwirq);
>> +}
>> +
>> +/*
>> + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
>> + * To maintain the expected behaviour of .set_affinity for each MSI
>> + * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
>> + * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
>> + * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
>> + * correct X-Gene v1 core. As a consequence, the total MSI vectors that
>> + * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
>> + */
>> +static inline int hwirq_to_cpu(unsigned long hwirq)
>> +{
>> +       return ((hwirq % NR_HW_IRQS) % xgene_msi_ctrl.num_cpus);
>> +}
>> +
>> +static inline unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq)
>> +{
>> +       return (hwirq - (hwirq % xgene_msi_ctrl.num_cpus));
>> +}
>
> Hmmm. This is weird. The canonical hwirq is defined as the IRQ seen from
> CPU0. So it is possible to write hwirq_to_canonical_hwirq as:
>
> static irq_hw_number_t hwirq_to_canonical_hwirq(irq_hw_number_t hwirq)
> {
>         return hwirq - hwirq_to_cpu(hwirq);
> }
>
> But you've defined hwirq_to_cpu as "(hwirq % NR_HW_IRQS) %
> xgene_msi_ctrl.num_cpus", which doesn't match your definition of a
> canonical hwirq. Can you explain the discrepancy?
>
In "(hwirq % NR_HW_IRQS) % xgene_msi_ctrl.num_cpus", I looked for the
group IRQ that the hwirq belongs to first, then figure out the CPU
that this group IRQ assigned to. With hwirq allocation scheme that is
using in this driver, defining hwirq_to_cpu as "hwirq %
xgene_msi_ctrl.num_cpus" also provides the same result. So I will
change it and use hwirq_to_cpu in hwirq_to_canonical_hwirq
implementation.

>> +
>> +static int xgene_msi_set_affinity(struct irq_data *irq_data,
>> +                                 const struct cpumask *mask, bool force)
>> +{
>> +       int target_cpu = cpumask_first(mask);
>> +       int curr_cpu;
>> +
>> +       curr_cpu = hwirq_to_cpu(irq_data->hwirq);
>> +       if (curr_cpu == target_cpu)
>> +               return IRQ_SET_MASK_OK_DONE;
>> +
>> +       /* Update MSI number to target the new CPU */
>> +       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
>
> irq_data->hwirq = hwirq_to_canonical_hwirq(irq_data->hwirq) + target_cpu;
>
>> +
>> +       return IRQ_SET_MASK_OK;
>> +}
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...
Regards,
Duc Dang.

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

* [PATCH v10 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-05-28  8:05                                                             ` Marc Zyngier
@ 2015-05-29 18:24                                                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-29 18:24 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau,
	Marc Zyngier, Thomas Gleixner, Jason Cooper, Mark Rutland
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
This MSI block supports 2048 MSI termination ports coalesced into 16 physical
HW IRQ lines and shared across all 5 PCIe ports. Starting from version 5 of
this patch series, the total MSI vectors this driver supports is reduced to 256
to maintain the correct set_affinity behavior for each MSI.

v10 changes:
	1. Remove inline in helper functions
	2. Simplify hwirq_to_cpu implementation

v9 changes:
        1. Add more helpers for manipulating MSI messages
        2. Improve code readability
        3. Make PCI_XGENE_MSI selectable

v8 changes:
        1. Add helper to read MSI registers
        2. Resample interrupt status of the same group after handling interrupts
           in that group
        3. Error handling in case fail to allocate CPU mask for affinity setting

v7 changes:
        1. Add more error handling cases
        2. Clear spurious interrupts that may happen during driver probe
        3. Not using free_irq for chained irqs
        4. Improve GIC IRQ number look up in chained handler

v6 changes:
        1. Correctly allocate MSI with bitmap_find_next_zero_area
        2. Add notifier for CPU hotplug case
        3. Driver clean up
        4. Add more detailed information into device tree binding document
           and move the device tree binding patch before the driver/dts patch

v5 changes:
        1. Implement set_affinity for each MSI by statically allocating 2
	   MSI GIC IRQs for each X-Gene CPU core and moving MSI vectors around
	   these GIC IRQs to steer them to target CPU core. As a consequence,
	   the total MSI vectors that X-Gene v1 supports is reduced to 256.

v4 changes:
        1. Remove affinity setting for each MSI
        2. Add description about register layout, MSI termination address
	   and data
        3. Correct total number of MSI vectors to 2048
        4. Clean up error messages
        5. Remove unused module code

v3 changes:
        1. Implement MSI support using PCI MSI IRQ domain
        2. Only use msi_controller to store IRQ domain

v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
 drivers/pci/host/Kconfig                           |  10 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 595 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 730 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1


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

* [PATCH v10 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-05-29 18:24                                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-29 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
This MSI block supports 2048 MSI termination ports coalesced into 16 physical
HW IRQ lines and shared across all 5 PCIe ports. Starting from version 5 of
this patch series, the total MSI vectors this driver supports is reduced to 256
to maintain the correct set_affinity behavior for each MSI.

v10 changes:
	1. Remove inline in helper functions
	2. Simplify hwirq_to_cpu implementation

v9 changes:
        1. Add more helpers for manipulating MSI messages
        2. Improve code readability
        3. Make PCI_XGENE_MSI selectable

v8 changes:
        1. Add helper to read MSI registers
        2. Resample interrupt status of the same group after handling interrupts
           in that group
        3. Error handling in case fail to allocate CPU mask for affinity setting

v7 changes:
        1. Add more error handling cases
        2. Clear spurious interrupts that may happen during driver probe
        3. Not using free_irq for chained irqs
        4. Improve GIC IRQ number look up in chained handler

v6 changes:
        1. Correctly allocate MSI with bitmap_find_next_zero_area
        2. Add notifier for CPU hotplug case
        3. Driver clean up
        4. Add more detailed information into device tree binding document
           and move the device tree binding patch before the driver/dts patch

v5 changes:
        1. Implement set_affinity for each MSI by statically allocating 2
	   MSI GIC IRQs for each X-Gene CPU core and moving MSI vectors around
	   these GIC IRQs to steer them to target CPU core. As a consequence,
	   the total MSI vectors that X-Gene v1 supports is reduced to 256.

v4 changes:
        1. Remove affinity setting for each MSI
        2. Add description about register layout, MSI termination address
	   and data
        3. Correct total number of MSI vectors to 2048
        4. Clean up error messages
        5. Remove unused module code

v3 changes:
        1. Implement MSI support using PCI MSI IRQ domain
        2. Only use msi_controller to store IRQ domain

v2 changes:
        1. Use msi_controller structure
        2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
 drivers/pci/host/Kconfig                           |  10 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 595 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 730 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

-- 
1.9.1

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

* [PATCH v10 1/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
  2015-05-29 18:24                                                               ` Duc Dang
@ 2015-05-29 18:24                                                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-29 18:24 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau,
	Marc Zyngier, Thomas Gleixner, Jason Cooper, Mark Rutland
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

X-Gene v1 PCIe MSI controller block provides PCIE MSI functionality
for 5 X-Gene v1 PCIE ports

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..36d881c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+	      X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+	      interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi@79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = 	<0x0 0x10 0x4>
+				<0x0 0x11 0x4>
+				<0x0 0x12 0x4>
+				<0x0 0x13 0x4>
+				<0x0 0x14 0x4>
+				<0x0 0x15 0x4>
+				<0x0 0x16 0x4>
+				<0x0 0x17 0x4>
+				<0x0 0x18 0x4>
+				<0x0 0x19 0x4>
+				<0x0 0x1a 0x4>
+				<0x0 0x1b 0x4>
+				<0x0 0x1c 0x4>
+				<0x0 0x1d 0x4>
+				<0x0 0x1e 0x4>
+				<0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie@1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1


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

* [PATCH v10 1/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node
@ 2015-05-29 18:24                                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-29 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

X-Gene v1 PCIe MSI controller block provides PCIE MSI functionality
for 5 X-Gene v1 PCIE ports

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt

diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644
index 0000000..36d881c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+	      X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+	      interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = 	<0x0 0x10 0x4>
+				<0x0 0x11 0x4>
+				<0x0 0x12 0x4>
+				<0x0 0x13 0x4>
+				<0x0 0x14 0x4>
+				<0x0 0x15 0x4>
+				<0x0 0x16 0x4>
+				<0x0 0x17 0x4>
+				<0x0 0x18 0x4>
+				<0x0 0x19 0x4>
+				<0x0 0x1a 0x4>
+				<0x0 0x1b 0x4>
+				<0x0 0x1c 0x4>
+				<0x0 0x1d 0x4>
+				<0x0 0x1e 0x4>
+				<0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
-- 
1.9.1

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

* [PATCH v10 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
  2015-05-29 18:24                                                               ` Duc Dang
@ 2015-05-29 18:24                                                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-29 18:24 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau,
	Marc Zyngier, Thomas Gleixner, Jason Cooper, Mark Rutland
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

APM X-Gene v1 SoC supports its own implementation of MSI, which is
not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
This MSI block supports 2048 MSI termination ports coalesced into 16
physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support
set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically
allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI
interrupt to target CPU, MSI vector is moved around these HW IRQs lines.
With this approach, the total MSI vectors this driver supports is reduced
to 256.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 drivers/pci/host/Kconfig         |  10 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 595 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 627 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 1dfb567..a93fda8 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,21 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+	default y
+	help
+	  Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
+	  This MSI driver will provide MSI support for 5 PCIe ports of
+	  APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index f733b4e..1957431 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,6 +11,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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
 obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..378f272
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,595 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0			0x000000
+#define MSI_INT0		0x800000
+#define IDX_PER_GROUP		8
+#define IRQS_PER_IDX		16
+#define NR_HW_IRQS		16
+#define NR_MSI_VEC		(IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi_group {
+	struct xgene_msi		*msi;
+	int				gic_irq;
+	u32				msi_grp;
+};
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	u64				msi_addr;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	struct xgene_msi_group		*msi_groups;
+	int				num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0			base_addr
+ * MSI0IR1			base_addr +  0x10000
+ * ...				...
+ * MSI0IR6			base_addr +  0x60000
+ * MSI0IR7			base_addr +  0x70000
+ * MSI1IR0			base_addr +  0x80000
+ * MSI1IR1			base_addr +  0x90000
+ * ...				...
+ * MSI1IR7			base_addr +  0xF0000
+ * MSI2IR0			base_addr + 0x100000
+ * ...				...
+ * MSIFIR0			base_addr + 0x780000
+ * MSIFIR1			base_addr + 0x790000
+ * ...				...
+ * MSIFIR7			base_addr + 0x7F0000
+ * MSIINT0			base_addr + 0x800000
+ * MSIINT1			base_addr + 0x810000
+ * ...				...
+ * MSIINTF			base_addr + 0x8F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
+ * the MSI pending status caused by 1 of its 8 index registers.
+ */
+
+/* MSInIRx read helper */
+static u32 xgene_msi_ir_read(struct xgene_msi *msi,
+				    u32 msi_grp, u32 msir_idx)
+{
+	return readl_relaxed(msi->msi_regs + MSI_IR0 +
+			      (msi_grp << 19) + (msir_idx << 16));
+}
+
+/* MSIINTn read helper */
+static u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
+{
+	return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+}
+
+/* With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *		Group 0: 0-255
+ *		Group 1: 256-511
+ *		Group 2: 512-767
+ *		...
+ *		Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *	As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *		Group 0: 0-15
+ *		Group 1: 16-32
+ *		...
+ *		Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static u32 hwirq_to_reg_set(unsigned long hwirq)
+{
+	return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX));
+}
+
+static u32 hwirq_to_group(unsigned long hwirq)
+{
+	return (hwirq % NR_HW_IRQS);
+}
+
+static u32 hwirq_to_msi_data(unsigned long hwirq)
+{
+	return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX);
+}
+
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 reg_set = hwirq_to_reg_set(data->hwirq);
+	u32 group = hwirq_to_group(data->hwirq);
+	u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16);
+
+	msg->address_hi = upper_32_bits(target_addr);
+	msg->address_lo = lower_32_bits(target_addr);
+	msg->data = hwirq_to_msi_data(data->hwirq);
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static int hwirq_to_cpu(unsigned long hwirq)
+{
+	return (hwirq % xgene_msi_ctrl.num_cpus);
+}
+
+static unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq)
+{
+	return (hwirq - hwirq_to_cpu(hwirq));
+}
+
+static int xgene_msi_set_affinity(struct irq_data *irqdata,
+				  const struct cpumask *mask, bool force)
+{
+	int target_cpu = cpumask_first(mask);
+	int curr_cpu;
+
+	curr_cpu = hwirq_to_cpu(irqdata->hwirq);
+	if (curr_cpu == target_cpu)
+		return IRQ_SET_MASK_OK_DONE;
+
+	/* Update MSI number to target the new CPU */
+	irqdata->hwirq = hwirq_to_canonical_hwirq(irqdata->hwirq) + target_cpu;
+
+	return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+	struct xgene_msi *msi = domain->host_data;
+	int msi_irq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+					     msi->num_cpus, 0);
+	if (msi_irq < NR_MSI_VEC)
+		bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+	else
+		msi_irq = -ENOSPC;
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq,
+			    &xgene_msi_bottom_irq_chip, domain->host_data,
+			    handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+	u32 hwirq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	hwirq = hwirq_to_canonical_hwirq(d->hwirq);
+	bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+					    &msi_domain_ops, msi);
+	if (!msi->domain)
+		return -ENOMEM;
+
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
+					sizeof(struct xgene_msi_group),
+					GFP_KERNEL);
+	if (!xgene_msi->msi_groups)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct xgene_msi_group *msi_groups;
+	struct xgene_msi *xgene_msi;
+	unsigned int virq;
+	int msir_index, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp;
+
+	chained_irq_enter(chip, desc);
+
+	msi_groups = irq_desc_get_handler_data(desc);
+	xgene_msi = msi_groups->msi;
+	msi_grp = msi_groups->msi_grp;
+
+	/*
+	 * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+	 * If bit x of this register is set (x is 0..7), one or more interupts
+	 * corresponding to MSInIRx is set.
+	 */
+	grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		/*
+		 * Calculate MSInIRx address to read to check for interrupts
+		 * (refer to termination address and data assignment
+		 * described in xgene_compose_msi_msg function)
+		 */
+		msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			/*
+			 * Calculate MSI vector number (refer to the termination
+			 * address and data assignment described in
+			 * xgene_compose_msi_msg function)
+			 */
+			hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+				 NR_HW_IRQS) + msi_grp;
+			/*
+			 * As we have multiple hw_irq that maps to single MSI,
+			 * always look up the virq using the hw_irq as seen from
+			 * CPU0
+			 */
+			hw_irq = hwirq_to_canonical_hwirq(hw_irq);
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			WARN_ON(!virq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+		}
+		grp_select &= ~(1 << msir_index);
+
+		if (!grp_select) {
+			/*
+			 * We handled all interrupts happened in this group,
+			 * resample this group MSI_INTx register in case
+			 * something else has been made pending in the meantime
+			 */
+			grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+		}
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		virq = msi->msi_groups[i].gic_irq;
+		if (virq != 0) {
+			irq_set_chained_handler(virq, NULL);
+			irq_set_handler_data(virq, NULL);
+		}
+	}
+	kfree(msi->msi_groups);
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static int xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	cpumask_var_t mask;
+	int i;
+	int err;
+
+	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq,
+					xgene_msi_isr);
+		err = irq_set_handler_data(msi_group->gic_irq, msi_group);
+		if (err) {
+			pr_err("failed to register GIC IRQ handler\n");
+			return -EINVAL;
+		}
+		/*
+		 * Statically allocate MSI GIC IRQs to each CPU core
+		 * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+		 * to each core.
+		 */
+		if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+			cpumask_clear(mask);
+			cpumask_set_cpu(cpu, mask);
+			err = irq_set_affinity(msi_group->gic_irq, mask);
+			if (err)
+				pr_err("failed to set affinity for GIC IRQ");
+			free_cpumask_var(mask);
+		} else {
+			pr_err("failed to alloc CPU mask for affinity\n");
+			err = -EINVAL;
+		}
+
+		if (err) {
+			irq_set_chained_handler(msi_group->gic_irq, NULL);
+			irq_set_handler_data(msi_group->gic_irq, NULL);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	int i;
+
+	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq, NULL);
+		irq_set_handler_data(msi_group->gic_irq, NULL);
+	}
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+				  unsigned long action, void *hcpu)
+{
+	unsigned cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		xgene_msi_hwirq_alloc(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		xgene_msi_hwirq_free(cpu);
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+	.notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi"},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct xgene_msi *xgene_msi;
+	unsigned int cpu;
+	int virt_msir;
+	u32 msi_val, msi_idx;
+
+	xgene_msi = &xgene_msi_ctrl;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	xgene_msi->msi_addr = res->start;
+
+	xgene_msi->num_cpus = num_possible_cpus();
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		virt_msir = platform_get_irq(pdev, irq_index);
+		if (virt_msir < 0) {
+			dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+				irq_index);
+			rc = -EINVAL;
+			goto error;
+		}
+		xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
+		xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
+		xgene_msi->msi_groups[irq_index].msi = xgene_msi;
+	}
+
+	/*
+	 * MSInIRx registers are read-to-clear, before registering interrupt
+	 * handlers, reading all of them to clear all spurious interrupts
+	 * that may occur before the driver is probed.
+	 */
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
+			msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
+						    msi_idx);
+		/* Read MSIINTn to confirm */
+		msi_val = xgene_msi_int_read(xgene_msi, irq_index);
+		if (msi_val) {
+			dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
+			rc = -EINVAL;
+			goto error;
+		}
+	}
+
+	cpu_notifier_register_begin();
+
+	for_each_online_cpu(cpu)
+		if (xgene_msi_hwirq_alloc(cpu)) {
+			dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
+			cpu_notifier_register_done();
+			goto error;
+		}
+
+	rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+		cpu_notifier_register_done();
+		goto error;
+	}
+
+	cpu_notifier_register_done();
+
+	xgene_msi->mchip.of_node = pdev->dev.of_node;
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error_notifier;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+
+error_notifier:
+	unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index ee082c0..a9b4f16 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1


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

* [PATCH v10 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver
@ 2015-05-29 18:24                                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-29 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

APM X-Gene v1 SoC supports its own implementation of MSI, which is
not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
This MSI block supports 2048 MSI termination ports coalesced into 16
physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support
set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically
allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI
interrupt to target CPU, MSI vector is moved around these HW IRQs lines.
With this approach, the total MSI vectors this driver supports is reduced
to 256.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 drivers/pci/host/Kconfig         |  10 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 595 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 627 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 1dfb567..a93fda8 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,21 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+	default y
+	help
+	  Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
+	  This MSI driver will provide MSI support for 5 PCIe ports of
+	  APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index f733b4e..1957431 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,6 +11,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_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
 obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..378f272
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,595 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *	   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 as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0			0x000000
+#define MSI_INT0		0x800000
+#define IDX_PER_GROUP		8
+#define IRQS_PER_IDX		16
+#define NR_HW_IRQS		16
+#define NR_MSI_VEC		(IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi_group {
+	struct xgene_msi		*msi;
+	int				gic_irq;
+	u32				msi_grp;
+};
+
+struct xgene_msi {
+	struct device_node		*node;
+	struct msi_controller		mchip;
+	struct irq_domain		*domain;
+	u64				msi_addr;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	struct xgene_msi_group		*msi_groups;
+	int				num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+	.name		= "X-Gene1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_PCI_MSIX),
+	.chip	= &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0			base_addr
+ * MSI0IR1			base_addr +  0x10000
+ * ...				...
+ * MSI0IR6			base_addr +  0x60000
+ * MSI0IR7			base_addr +  0x70000
+ * MSI1IR0			base_addr +  0x80000
+ * MSI1IR1			base_addr +  0x90000
+ * ...				...
+ * MSI1IR7			base_addr +  0xF0000
+ * MSI2IR0			base_addr + 0x100000
+ * ...				...
+ * MSIFIR0			base_addr + 0x780000
+ * MSIFIR1			base_addr + 0x790000
+ * ...				...
+ * MSIFIR7			base_addr + 0x7F0000
+ * MSIINT0			base_addr + 0x800000
+ * MSIINT1			base_addr + 0x810000
+ * ...				...
+ * MSIINTF			base_addr + 0x8F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
+ * the MSI pending status caused by 1 of its 8 index registers.
+ */
+
+/* MSInIRx read helper */
+static u32 xgene_msi_ir_read(struct xgene_msi *msi,
+				    u32 msi_grp, u32 msir_idx)
+{
+	return readl_relaxed(msi->msi_regs + MSI_IR0 +
+			      (msi_grp << 19) + (msir_idx << 16));
+}
+
+/* MSIINTn read helper */
+static u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
+{
+	return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+}
+
+/* With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *		Group 0: 0-255
+ *		Group 1: 256-511
+ *		Group 2: 512-767
+ *		...
+ *		Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *	As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *		Group 0: 0-15
+ *		Group 1: 16-32
+ *		...
+ *		Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static u32 hwirq_to_reg_set(unsigned long hwirq)
+{
+	return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX));
+}
+
+static u32 hwirq_to_group(unsigned long hwirq)
+{
+	return (hwirq % NR_HW_IRQS);
+}
+
+static u32 hwirq_to_msi_data(unsigned long hwirq)
+{
+	return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX);
+}
+
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+	u32 reg_set = hwirq_to_reg_set(data->hwirq);
+	u32 group = hwirq_to_group(data->hwirq);
+	u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16);
+
+	msg->address_hi = upper_32_bits(target_addr);
+	msg->address_lo = lower_32_bits(target_addr);
+	msg->data = hwirq_to_msi_data(data->hwirq);
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static int hwirq_to_cpu(unsigned long hwirq)
+{
+	return (hwirq % xgene_msi_ctrl.num_cpus);
+}
+
+static unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq)
+{
+	return (hwirq - hwirq_to_cpu(hwirq));
+}
+
+static int xgene_msi_set_affinity(struct irq_data *irqdata,
+				  const struct cpumask *mask, bool force)
+{
+	int target_cpu = cpumask_first(mask);
+	int curr_cpu;
+
+	curr_cpu = hwirq_to_cpu(irqdata->hwirq);
+	if (curr_cpu == target_cpu)
+		return IRQ_SET_MASK_OK_DONE;
+
+	/* Update MSI number to target the new CPU */
+	irqdata->hwirq = hwirq_to_canonical_hwirq(irqdata->hwirq) + target_cpu;
+
+	return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+	.name			= "MSI",
+	.irq_set_affinity       = xgene_msi_set_affinity,
+	.irq_compose_msi_msg	= xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *args)
+{
+	struct xgene_msi *msi = domain->host_data;
+	int msi_irq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+					     msi->num_cpus, 0);
+	if (msi_irq < NR_MSI_VEC)
+		bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+	else
+		msi_irq = -ENOSPC;
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	if (msi_irq < 0)
+		return msi_irq;
+
+	irq_domain_set_info(domain, virq, msi_irq,
+			    &xgene_msi_bottom_irq_chip, domain->host_data,
+			    handle_simple_irq, NULL, NULL);
+	set_irq_flags(virq, IRQF_VALID);
+
+	return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+	u32 hwirq;
+
+	mutex_lock(&msi->bitmap_lock);
+
+	hwirq = hwirq_to_canonical_hwirq(d->hwirq);
+	bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+					    &msi_domain_ops, msi);
+	if (!msi->domain)
+		return -ENOMEM;
+
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
+					sizeof(struct xgene_msi_group),
+					GFP_KERNEL);
+	if (!xgene_msi->msi_groups)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct xgene_msi_group *msi_groups;
+	struct xgene_msi *xgene_msi;
+	unsigned int virq;
+	int msir_index, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp;
+
+	chained_irq_enter(chip, desc);
+
+	msi_groups = irq_desc_get_handler_data(desc);
+	xgene_msi = msi_groups->msi;
+	msi_grp = msi_groups->msi_grp;
+
+	/*
+	 * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+	 * If bit x of this register is set (x is 0..7), one or more interupts
+	 * corresponding to MSInIRx is set.
+	 */
+	grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		/*
+		 * Calculate MSInIRx address to read to check for interrupts
+		 * (refer to termination address and data assignment
+		 * described in xgene_compose_msi_msg function)
+		 */
+		msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			/*
+			 * Calculate MSI vector number (refer to the termination
+			 * address and data assignment described in
+			 * xgene_compose_msi_msg function)
+			 */
+			hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+				 NR_HW_IRQS) + msi_grp;
+			/*
+			 * As we have multiple hw_irq that maps to single MSI,
+			 * always look up the virq using the hw_irq as seen from
+			 * CPU0
+			 */
+			hw_irq = hwirq_to_canonical_hwirq(hw_irq);
+			virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+			WARN_ON(!virq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+		}
+		grp_select &= ~(1 << msir_index);
+
+		if (!grp_select) {
+			/*
+			 * We handled all interrupts happened in this group,
+			 * resample this group MSI_INTx register in case
+			 * something else has been made pending in the meantime
+			 */
+			grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+		}
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+	for (i = 0; i < NR_HW_IRQS; i++) {
+		virq = msi->msi_groups[i].gic_irq;
+		if (virq != 0) {
+			irq_set_chained_handler(virq, NULL);
+			irq_set_handler_data(virq, NULL);
+		}
+	}
+	kfree(msi->msi_groups);
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	xgene_free_domains(msi);
+
+	return 0;
+}
+
+static int xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	cpumask_var_t mask;
+	int i;
+	int err;
+
+	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq,
+					xgene_msi_isr);
+		err = irq_set_handler_data(msi_group->gic_irq, msi_group);
+		if (err) {
+			pr_err("failed to register GIC IRQ handler\n");
+			return -EINVAL;
+		}
+		/*
+		 * Statically allocate MSI GIC IRQs to each CPU core
+		 * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+		 * to each core.
+		 */
+		if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+			cpumask_clear(mask);
+			cpumask_set_cpu(cpu, mask);
+			err = irq_set_affinity(msi_group->gic_irq, mask);
+			if (err)
+				pr_err("failed to set affinity for GIC IRQ");
+			free_cpumask_var(mask);
+		} else {
+			pr_err("failed to alloc CPU mask for affinity\n");
+			err = -EINVAL;
+		}
+
+		if (err) {
+			irq_set_chained_handler(msi_group->gic_irq, NULL);
+			irq_set_handler_data(msi_group->gic_irq, NULL);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+	struct xgene_msi *msi = &xgene_msi_ctrl;
+	struct xgene_msi_group *msi_group;
+	int i;
+
+	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+		msi_group = &msi->msi_groups[i];
+		if (!msi_group->gic_irq)
+			continue;
+
+		irq_set_chained_handler(msi_group->gic_irq, NULL);
+		irq_set_handler_data(msi_group->gic_irq, NULL);
+	}
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+				  unsigned long action, void *hcpu)
+{
+	unsigned cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		xgene_msi_hwirq_alloc(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		xgene_msi_hwirq_free(cpu);
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+	.notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi"},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct xgene_msi *xgene_msi;
+	unsigned int cpu;
+	int virt_msir;
+	u32 msi_val, msi_idx;
+
+	xgene_msi = &xgene_msi_ctrl;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	xgene_msi->msi_addr = res->start;
+
+	xgene_msi->num_cpus = num_possible_cpus();
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	rc = xgene_allocate_domains(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+		goto error;
+	}
+
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		virt_msir = platform_get_irq(pdev, irq_index);
+		if (virt_msir < 0) {
+			dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+				irq_index);
+			rc = -EINVAL;
+			goto error;
+		}
+		xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
+		xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
+		xgene_msi->msi_groups[irq_index].msi = xgene_msi;
+	}
+
+	/*
+	 * MSInIRx registers are read-to-clear, before registering interrupt
+	 * handlers, reading all of them to clear all spurious interrupts
+	 * that may occur before the driver is probed.
+	 */
+	for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+		for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
+			msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
+						    msi_idx);
+		/* Read MSIINTn to confirm */
+		msi_val = xgene_msi_int_read(xgene_msi, irq_index);
+		if (msi_val) {
+			dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
+			rc = -EINVAL;
+			goto error;
+		}
+	}
+
+	cpu_notifier_register_begin();
+
+	for_each_online_cpu(cpu)
+		if (xgene_msi_hwirq_alloc(cpu)) {
+			dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
+			cpu_notifier_register_done();
+			goto error;
+		}
+
+	rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+		cpu_notifier_register_done();
+		goto error;
+	}
+
+	cpu_notifier_register_done();
+
+	xgene_msi->mchip.of_node = pdev->dev.of_node;
+	rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error_notifier;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+
+error_notifier:
+	unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index ee082c0..a9b4f16 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
-- 
1.9.1

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

* [PATCH v10 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
  2015-05-29 18:24                                                               ` Duc Dang
@ 2015-05-29 18:24                                                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-29 18:24 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau,
	Marc Zyngier, Thomas Gleixner, Jason Cooper, Mark Rutland
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index c8d3e0e..d8f3a1c 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -374,6 +374,28 @@
 			};
 		};
 
+		msi: msi@79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie@1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -395,6 +417,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie@1f2c0000 {
@@ -418,6 +441,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie@1f2d0000 {
@@ -441,6 +465,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie@1f500000 {
@@ -464,6 +489,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie@1f510000 {
@@ -487,6 +513,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial@1c020000 {
-- 
1.9.1


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

* [PATCH v10 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node
@ 2015-05-29 18:24                                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-29 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index c8d3e0e..d8f3a1c 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -374,6 +374,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -395,6 +417,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -418,6 +441,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -441,6 +465,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -464,6 +489,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -487,6 +513,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
-- 
1.9.1

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

* [PATCH v10 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
  2015-05-29 18:24                                                               ` Duc Dang
@ 2015-05-29 18:24                                                               ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-29 18:24 UTC (permalink / raw)
  To: Bjorn Helgaas, Arnd Bergmann, Grant Likely, Liviu Dudau,
	Marc Zyngier, Thomas Gleixner, Jason Cooper, Mark Rutland
  Cc: linux-pci, linux-arm-kernel, linux-kernel, Tanmay Inamdar,
	Loc Ho, Feng Kan, Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 781e099..59aa7b3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7564,6 +7564,14 @@ L:	linux-pci@vger.kernel.org
 S:	Orphan
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci@vger.kernel.org
+L:	linux-arm-kernel@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia@lists.infradead.org
-- 
1.9.1


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

* [PATCH v10 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver
@ 2015-05-29 18:24                                                               ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-05-29 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds information of maintainers for APM X-Gene v1 PCIe
MSI/MSIX termination driver.

Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 781e099..59aa7b3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7564,6 +7564,14 @@ L:	linux-pci at vger.kernel.org
 S:	Orphan
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <dhdang@apm.com>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
-- 
1.9.1

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

* Re: [PATCH v10 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-05-29 18:24                                                               ` Duc Dang
@ 2015-06-05 21:05                                                                 ` Bjorn Helgaas
  -1 siblings, 0 replies; 230+ messages in thread
From: Bjorn Helgaas @ 2015-06-05 21:05 UTC (permalink / raw)
  To: Duc Dang
  Cc: Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier,
	Thomas Gleixner, Jason Cooper, Mark Rutland, linux-pci,
	linux-arm-kernel, linux-kernel, Tanmay Inamdar, Loc Ho, Feng Kan

On Fri, May 29, 2015 at 11:24:28AM -0700, Duc Dang wrote:
> This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
> This MSI block supports 2048 MSI termination ports coalesced into 16 physical
> HW IRQ lines and shared across all 5 PCIe ports. Starting from version 5 of
> this patch series, the total MSI vectors this driver supports is reduced to 256
> to maintain the correct set_affinity behavior for each MSI.
> 
> v10 changes:
> 	1. Remove inline in helper functions
> 	2. Simplify hwirq_to_cpu implementation
> 
> v9 changes:
>         1. Add more helpers for manipulating MSI messages
>         2. Improve code readability
>         3. Make PCI_XGENE_MSI selectable
> 
> v8 changes:
>         1. Add helper to read MSI registers
>         2. Resample interrupt status of the same group after handling interrupts
>            in that group
>         3. Error handling in case fail to allocate CPU mask for affinity setting
> 
> v7 changes:
>         1. Add more error handling cases
>         2. Clear spurious interrupts that may happen during driver probe
>         3. Not using free_irq for chained irqs
>         4. Improve GIC IRQ number look up in chained handler
> 
> v6 changes:
>         1. Correctly allocate MSI with bitmap_find_next_zero_area
>         2. Add notifier for CPU hotplug case
>         3. Driver clean up
>         4. Add more detailed information into device tree binding document
>            and move the device tree binding patch before the driver/dts patch
> 
> v5 changes:
>         1. Implement set_affinity for each MSI by statically allocating 2
> 	   MSI GIC IRQs for each X-Gene CPU core and moving MSI vectors around
> 	   these GIC IRQs to steer them to target CPU core. As a consequence,
> 	   the total MSI vectors that X-Gene v1 supports is reduced to 256.
> 
> v4 changes:
>         1. Remove affinity setting for each MSI
>         2. Add description about register layout, MSI termination address
> 	   and data
>         3. Correct total number of MSI vectors to 2048
>         4. Clean up error messages
>         5. Remove unused module code
> 
> v3 changes:
>         1. Implement MSI support using PCI MSI IRQ domain
>         2. Only use msi_controller to store IRQ domain
> 
> v2 changes:
>         1. Use msi_controller structure
>         2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
> 
>  .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
>  MAINTAINERS                                        |   8 +
>  arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
>  drivers/pci/host/Kconfig                           |  10 +
>  drivers/pci/host/Makefile                          |   1 +
>  drivers/pci/host/pci-xgene-msi.c                   | 595 +++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c                       |  21 +
>  7 files changed, 730 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c

I applied these to pci/host-xgene for v4.2, thanks!

I squashed the doc, driver, and maintainer updates into a single patch with
the following changelog:

PCI: xgene: Add APM X-Gene v1 PCIe MSI/MSIX termination driver

APM X-Gene v1 SoC supports its own implementation of MSI, which is not 
compliant to GIC V2M specification for MSI Termination.

There is a single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
This MSI block supports 2048 MSI termination ports coalesced into 16
physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support
set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically
allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores).  To steer MSI 
interrupt to target CPU, MSI vector is moved around these HW IRQs lines.
With this approach, the total MSI vectors this driver supports is reduced
to 256.

[bhelgaas: squash doc, driver, maintainer update]
Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>


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

* [PATCH v10 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-06-05 21:05                                                                 ` Bjorn Helgaas
  0 siblings, 0 replies; 230+ messages in thread
From: Bjorn Helgaas @ 2015-06-05 21:05 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, May 29, 2015 at 11:24:28AM -0700, Duc Dang wrote:
> This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
> to GIC V2M specification for MSI Termination.
> 
> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
> This MSI block supports 2048 MSI termination ports coalesced into 16 physical
> HW IRQ lines and shared across all 5 PCIe ports. Starting from version 5 of
> this patch series, the total MSI vectors this driver supports is reduced to 256
> to maintain the correct set_affinity behavior for each MSI.
> 
> v10 changes:
> 	1. Remove inline in helper functions
> 	2. Simplify hwirq_to_cpu implementation
> 
> v9 changes:
>         1. Add more helpers for manipulating MSI messages
>         2. Improve code readability
>         3. Make PCI_XGENE_MSI selectable
> 
> v8 changes:
>         1. Add helper to read MSI registers
>         2. Resample interrupt status of the same group after handling interrupts
>            in that group
>         3. Error handling in case fail to allocate CPU mask for affinity setting
> 
> v7 changes:
>         1. Add more error handling cases
>         2. Clear spurious interrupts that may happen during driver probe
>         3. Not using free_irq for chained irqs
>         4. Improve GIC IRQ number look up in chained handler
> 
> v6 changes:
>         1. Correctly allocate MSI with bitmap_find_next_zero_area
>         2. Add notifier for CPU hotplug case
>         3. Driver clean up
>         4. Add more detailed information into device tree binding document
>            and move the device tree binding patch before the driver/dts patch
> 
> v5 changes:
>         1. Implement set_affinity for each MSI by statically allocating 2
> 	   MSI GIC IRQs for each X-Gene CPU core and moving MSI vectors around
> 	   these GIC IRQs to steer them to target CPU core. As a consequence,
> 	   the total MSI vectors that X-Gene v1 supports is reduced to 256.
> 
> v4 changes:
>         1. Remove affinity setting for each MSI
>         2. Add description about register layout, MSI termination address
> 	   and data
>         3. Correct total number of MSI vectors to 2048
>         4. Clean up error messages
>         5. Remove unused module code
> 
> v3 changes:
>         1. Implement MSI support using PCI MSI IRQ domain
>         2. Only use msi_controller to store IRQ domain
> 
> v2 changes:
>         1. Use msi_controller structure
>         2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
> 
>  .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
>  MAINTAINERS                                        |   8 +
>  arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
>  drivers/pci/host/Kconfig                           |  10 +
>  drivers/pci/host/Makefile                          |   1 +
>  drivers/pci/host/pci-xgene-msi.c                   | 595 +++++++++++++++++++++
>  drivers/pci/host/pci-xgene.c                       |  21 +
>  7 files changed, 730 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
>  create mode 100644 drivers/pci/host/pci-xgene-msi.c

I applied these to pci/host-xgene for v4.2, thanks!

I squashed the doc, driver, and maintainer updates into a single patch with
the following changelog:

PCI: xgene: Add APM X-Gene v1 PCIe MSI/MSIX termination driver

APM X-Gene v1 SoC supports its own implementation of MSI, which is not 
compliant to GIC V2M specification for MSI Termination.

There is a single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
This MSI block supports 2048 MSI termination ports coalesced into 16
physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support
set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically
allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores).  To steer MSI 
interrupt to target CPU, MSI vector is moved around these HW IRQs lines.
With this approach, the total MSI vectors this driver supports is reduced
to 256.

[bhelgaas: squash doc, driver, maintainer update]
Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>

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

* Re: [PATCH v10 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
  2015-06-05 21:05                                                                 ` Bjorn Helgaas
@ 2015-06-05 21:11                                                                   ` Duc Dang
  -1 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-06-05 21:11 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: Arnd Bergmann, Grant Likely, Liviu Dudau, Marc Zyngier,
	Thomas Gleixner, Jason Cooper, Mark Rutland, linux-pci,
	linux-arm, linux-kernel, Tanmay Inamdar, Loc Ho, Feng Kan

On Fri, Jun 5, 2015 at 2:05 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
> On Fri, May 29, 2015 at 11:24:28AM -0700, Duc Dang wrote:
>> This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
>> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
>> to GIC V2M specification for MSI Termination.
>>
>> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
>> This MSI block supports 2048 MSI termination ports coalesced into 16 physical
>> HW IRQ lines and shared across all 5 PCIe ports. Starting from version 5 of
>> this patch series, the total MSI vectors this driver supports is reduced to 256
>> to maintain the correct set_affinity behavior for each MSI.
>>
>> v10 changes:
>>       1. Remove inline in helper functions
>>       2. Simplify hwirq_to_cpu implementation
>>
>> v9 changes:
>>         1. Add more helpers for manipulating MSI messages
>>         2. Improve code readability
>>         3. Make PCI_XGENE_MSI selectable
>>
>> v8 changes:
>>         1. Add helper to read MSI registers
>>         2. Resample interrupt status of the same group after handling interrupts
>>            in that group
>>         3. Error handling in case fail to allocate CPU mask for affinity setting
>>
>> v7 changes:
>>         1. Add more error handling cases
>>         2. Clear spurious interrupts that may happen during driver probe
>>         3. Not using free_irq for chained irqs
>>         4. Improve GIC IRQ number look up in chained handler
>>
>> v6 changes:
>>         1. Correctly allocate MSI with bitmap_find_next_zero_area
>>         2. Add notifier for CPU hotplug case
>>         3. Driver clean up
>>         4. Add more detailed information into device tree binding document
>>            and move the device tree binding patch before the driver/dts patch
>>
>> v5 changes:
>>         1. Implement set_affinity for each MSI by statically allocating 2
>>          MSI GIC IRQs for each X-Gene CPU core and moving MSI vectors around
>>          these GIC IRQs to steer them to target CPU core. As a consequence,
>>          the total MSI vectors that X-Gene v1 supports is reduced to 256.
>>
>> v4 changes:
>>         1. Remove affinity setting for each MSI
>>         2. Add description about register layout, MSI termination address
>>          and data
>>         3. Correct total number of MSI vectors to 2048
>>         4. Clean up error messages
>>         5. Remove unused module code
>>
>> v3 changes:
>>         1. Implement MSI support using PCI MSI IRQ domain
>>         2. Only use msi_controller to store IRQ domain
>>
>> v2 changes:
>>         1. Use msi_controller structure
>>         2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
>>
>>  .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
>>  MAINTAINERS                                        |   8 +
>>  arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
>>  drivers/pci/host/Kconfig                           |  10 +
>>  drivers/pci/host/Makefile                          |   1 +
>>  drivers/pci/host/pci-xgene-msi.c                   | 595 +++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c                       |  21 +
>>  7 files changed, 730 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>
> I applied these to pci/host-xgene for v4.2, thanks!
>
> I squashed the doc, driver, and maintainer updates into a single patch with
> the following changelog:
>
> PCI: xgene: Add APM X-Gene v1 PCIe MSI/MSIX termination driver
>
> APM X-Gene v1 SoC supports its own implementation of MSI, which is not
> compliant to GIC V2M specification for MSI Termination.
>
> There is a single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
> This MSI block supports 2048 MSI termination ports coalesced into 16
> physical HW IRQ lines and shared across all 5 PCIe ports.
>
> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support
> set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically
> allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores).  To steer MSI
> interrupt to target CPU, MSI vector is moved around these HW IRQs lines.
> With this approach, the total MSI vectors this driver supports is reduced
> to 256.
>
> [bhelgaas: squash doc, driver, maintainer update]
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
>
Thanks, Bjorn and Marc.

Regards,
Duc Dang.

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

* [PATCH v10 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver
@ 2015-06-05 21:11                                                                   ` Duc Dang
  0 siblings, 0 replies; 230+ messages in thread
From: Duc Dang @ 2015-06-05 21:11 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jun 5, 2015 at 2:05 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
> On Fri, May 29, 2015 at 11:24:28AM -0700, Duc Dang wrote:
>> This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC.
>> APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant
>> to GIC V2M specification for MSI Termination.
>>
>> There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
>> This MSI block supports 2048 MSI termination ports coalesced into 16 physical
>> HW IRQ lines and shared across all 5 PCIe ports. Starting from version 5 of
>> this patch series, the total MSI vectors this driver supports is reduced to 256
>> to maintain the correct set_affinity behavior for each MSI.
>>
>> v10 changes:
>>       1. Remove inline in helper functions
>>       2. Simplify hwirq_to_cpu implementation
>>
>> v9 changes:
>>         1. Add more helpers for manipulating MSI messages
>>         2. Improve code readability
>>         3. Make PCI_XGENE_MSI selectable
>>
>> v8 changes:
>>         1. Add helper to read MSI registers
>>         2. Resample interrupt status of the same group after handling interrupts
>>            in that group
>>         3. Error handling in case fail to allocate CPU mask for affinity setting
>>
>> v7 changes:
>>         1. Add more error handling cases
>>         2. Clear spurious interrupts that may happen during driver probe
>>         3. Not using free_irq for chained irqs
>>         4. Improve GIC IRQ number look up in chained handler
>>
>> v6 changes:
>>         1. Correctly allocate MSI with bitmap_find_next_zero_area
>>         2. Add notifier for CPU hotplug case
>>         3. Driver clean up
>>         4. Add more detailed information into device tree binding document
>>            and move the device tree binding patch before the driver/dts patch
>>
>> v5 changes:
>>         1. Implement set_affinity for each MSI by statically allocating 2
>>          MSI GIC IRQs for each X-Gene CPU core and moving MSI vectors around
>>          these GIC IRQs to steer them to target CPU core. As a consequence,
>>          the total MSI vectors that X-Gene v1 supports is reduced to 256.
>>
>> v4 changes:
>>         1. Remove affinity setting for each MSI
>>         2. Add description about register layout, MSI termination address
>>          and data
>>         3. Correct total number of MSI vectors to 2048
>>         4. Clean up error messages
>>         5. Remove unused module code
>>
>> v3 changes:
>>         1. Implement MSI support using PCI MSI IRQ domain
>>         2. Only use msi_controller to store IRQ domain
>>
>> v2 changes:
>>         1. Use msi_controller structure
>>         2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs
>>
>>  .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
>>  MAINTAINERS                                        |   8 +
>>  arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
>>  drivers/pci/host/Kconfig                           |  10 +
>>  drivers/pci/host/Makefile                          |   1 +
>>  drivers/pci/host/pci-xgene-msi.c                   | 595 +++++++++++++++++++++
>>  drivers/pci/host/pci-xgene.c                       |  21 +
>>  7 files changed, 730 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
>>  create mode 100644 drivers/pci/host/pci-xgene-msi.c
>
> I applied these to pci/host-xgene for v4.2, thanks!
>
> I squashed the doc, driver, and maintainer updates into a single patch with
> the following changelog:
>
> PCI: xgene: Add APM X-Gene v1 PCIe MSI/MSIX termination driver
>
> APM X-Gene v1 SoC supports its own implementation of MSI, which is not
> compliant to GIC V2M specification for MSI Termination.
>
> There is a single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.
> This MSI block supports 2048 MSI termination ports coalesced into 16
> physical HW IRQ lines and shared across all 5 PCIe ports.
>
> As there are only 16 HW IRQs to serve 2048 MSI vectors, to support
> set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically
> allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores).  To steer MSI
> interrupt to target CPU, MSI vector is moved around these HW IRQs lines.
> With this approach, the total MSI vectors this driver supports is reduced
> to 256.
>
> [bhelgaas: squash doc, driver, maintainer update]
> Signed-off-by: Duc Dang <dhdang@apm.com>
> Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
>
Thanks, Bjorn and Marc.

Regards,
Duc Dang.

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

end of thread, other threads:[~2015-06-05 21:11 UTC | newest]

Thread overview: 230+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-06 16:15 [PATCH 0/4] PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver Duc Dang
2015-01-06 16:15 ` Duc Dang
2015-01-06 16:15 ` [PATCH 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe " Duc Dang
2015-01-06 16:15   ` Duc Dang
2015-01-06 19:33   ` Arnd Bergmann
2015-01-06 19:33     ` Arnd Bergmann
2015-01-12 18:53     ` Duc Dang
2015-01-12 18:53       ` Duc Dang
2015-01-12 19:44       ` Arnd Bergmann
2015-01-12 19:44         ` Arnd Bergmann
2015-03-04 19:39         ` [PATCH v2 0/4] PCI: X-Gene: Add APM X-Gene v1 " Duc Dang
2015-03-04 19:39           ` Duc Dang
2015-03-18 17:43           ` Duc Dang
2015-03-18 17:43             ` Duc Dang
2015-03-19 20:49             ` Bjorn Helgaas
2015-03-19 20:49               ` Bjorn Helgaas
2015-03-19 20:59               ` Duc Dang
2015-03-19 20:59                 ` Duc Dang
2015-03-19 21:08                 ` Bjorn Helgaas
2015-03-19 21:08                   ` Bjorn Helgaas
2015-03-04 19:39         ` [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe " Duc Dang
2015-03-04 19:39           ` Duc Dang
2015-03-18 18:05           ` Marc Zyngier
2015-03-18 18:05             ` Marc Zyngier
2015-03-18 18:29             ` Duc Dang
2015-03-18 18:29               ` Duc Dang
2015-03-18 18:52               ` Marc Zyngier
2015-03-18 18:52                 ` Marc Zyngier
2015-04-07 19:56                 ` Duc Dang
2015-04-07 19:56                   ` Duc Dang
2015-04-08  7:44                   ` Marc Zyngier
2015-04-08  7:44                     ` Marc Zyngier
2015-04-09 17:05                     ` [PATCH v3 0/4] PCI: X-Gene: Add APM X-Gene v1 " Duc Dang
2015-04-09 17:05                       ` Duc Dang
2015-04-09 17:05                     ` [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe " Duc Dang
2015-04-09 17:05                       ` Duc Dang
2015-04-09 20:11                       ` Bjorn Helgaas
2015-04-09 20:11                         ` Bjorn Helgaas
2015-04-09 21:52                         ` Duc Dang
2015-04-09 21:52                           ` Duc Dang
2015-04-09 22:39                           ` Bjorn Helgaas
2015-04-09 22:39                             ` Bjorn Helgaas
2015-04-09 23:26                             ` Duc Dang
2015-04-09 23:26                               ` Duc Dang
2015-04-10 17:20                       ` Marc Zyngier
2015-04-10 17:20                         ` Marc Zyngier
2015-04-10 17:20                         ` Marc Zyngier
2015-04-10 23:42                         ` Duc Dang
2015-04-10 23:42                           ` Duc Dang
2015-04-10 23:42                           ` Duc Dang
2015-04-11 12:06                           ` Marc Zyngier
2015-04-11 12:06                             ` Marc Zyngier
2015-04-14 18:20                             ` Duc Dang
2015-04-14 18:20                               ` Duc Dang
2015-04-15  8:16                               ` Marc Zyngier
2015-04-15  8:16                                 ` Marc Zyngier
2015-04-17  9:50                                 ` [PATCH v4 0/4] PCI: X-Gene: Add APM X-Gene v1 " Duc Dang
2015-04-17  9:50                                   ` Duc Dang
2015-04-17  9:50                                 ` [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe " Duc Dang
2015-04-17  9:50                                   ` Duc Dang
2015-04-17 14:10                                   ` Arnd Bergmann
2015-04-17 14:10                                     ` Arnd Bergmann
2015-04-19 18:40                                     ` Duc Dang
2015-04-19 18:40                                       ` Duc Dang
2015-04-19 19:55                                       ` Arnd Bergmann
2015-04-19 19:55                                         ` Arnd Bergmann
2015-04-20 18:49                                         ` Feng Kan
2015-04-20 18:49                                           ` Feng Kan
2015-04-20 18:49                                           ` Feng Kan
2015-04-21  7:16                                           ` Arnd Bergmann
2015-04-21  7:16                                             ` Arnd Bergmann
2015-04-17  9:50                                 ` [PATCH v4 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node Duc Dang
2015-04-17  9:50                                   ` Duc Dang
2015-04-17  9:50                                 ` [PATCH v4 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node Duc Dang
2015-04-17  9:50                                   ` Duc Dang
2015-04-17  9:50                                 ` [PATCH v4 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver Duc Dang
2015-04-17  9:50                                   ` Duc Dang
2015-04-17 10:00                                 ` [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver Duc Dang
2015-04-17 10:00                                   ` Duc Dang
2015-04-17 10:17                                   ` Marc Zyngier
2015-04-17 10:17                                     ` Marc Zyngier
2015-04-17 12:37                                     ` Duc Dang
2015-04-17 12:37                                       ` Duc Dang
2015-04-17 12:45                                       ` Marc Zyngier
2015-04-17 12:45                                         ` Marc Zyngier
2015-04-20 18:51                                         ` Feng Kan
2015-04-20 18:51                                           ` Feng Kan
2015-04-21  8:32                                           ` Marc Zyngier
2015-04-21  8:32                                             ` Marc Zyngier
2015-04-21  4:04                                         ` [PATCH v5 0/4]PCI: X-Gene: Add APM X-Gene v1 " Duc Dang
2015-04-21  4:04                                           ` Duc Dang
2015-04-22  3:02                                           ` Jon Masters
2015-04-22  3:02                                             ` Jon Masters
2015-04-22  3:02                                           ` Jon Masters
2015-04-22  3:02                                             ` Jon Masters
2015-04-21  4:04                                         ` [PATCH v5 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe " Duc Dang
2015-04-21  4:04                                           ` Duc Dang
2015-04-21 15:08                                           ` Marc Zyngier
2015-04-21 15:08                                             ` Marc Zyngier
2015-04-21 15:08                                             ` Marc Zyngier
2015-04-22  6:15                                             ` [PATCH v6 0/4]PCI: X-Gene: Add APM X-Gene v1 " Duc Dang
2015-04-22  6:15                                               ` Duc Dang
2015-04-22  6:15                                             ` [PATCH v6 1/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node Duc Dang
2015-04-22  6:15                                               ` Duc Dang
2015-04-22  6:15                                             ` [PATCH v6 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver Duc Dang
2015-04-22  6:15                                               ` Duc Dang
2015-04-22 12:50                                               ` Marc Zyngier
2015-04-22 12:50                                                 ` Marc Zyngier
2015-04-22 12:50                                                 ` Marc Zyngier
2015-05-18  9:55                                                 ` [PATCH v7 0/4] PCI: X-Gene: Add APM X-Gene v1 " Duc Dang
2015-05-18  9:55                                                   ` Duc Dang
2015-05-18  9:55                                                 ` [PATCH v7 1/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node Duc Dang
2015-05-18  9:55                                                   ` Duc Dang
2015-05-18  9:55                                                 ` [PATCH v7 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver Duc Dang
2015-05-18  9:55                                                   ` Duc Dang
2015-05-20  9:16                                                   ` Marc Zyngier
2015-05-20  9:16                                                     ` Marc Zyngier
2015-05-20  9:16                                                     ` Marc Zyngier
2015-05-22 18:41                                                     ` [PATCH v8 0/4] PCI: X-Gene: Add APM X-Gene v1 " Duc Dang
2015-05-22 18:41                                                       ` Duc Dang
2015-05-22 18:41                                                     ` [PATCH v8 1/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node Duc Dang
2015-05-22 18:41                                                       ` Duc Dang
2015-05-22 18:41                                                     ` [PATCH v8 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver Duc Dang
2015-05-22 18:41                                                       ` Duc Dang
2015-05-25 11:52                                                       ` Marc Zyngier
2015-05-25 11:52                                                         ` Marc Zyngier
2015-05-25 11:52                                                         ` Marc Zyngier
2015-05-27 18:27                                                         ` [PATCH v9 0/4]PCI: X-Gene: Add APM X-Gene v1 " Duc Dang
2015-05-27 18:27                                                           ` Duc Dang
2015-05-27 18:27                                                         ` [PATCH v9 1/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node Duc Dang
2015-05-27 18:27                                                           ` Duc Dang
2015-05-27 18:27                                                         ` [PATCH v9 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver Duc Dang
2015-05-27 18:27                                                           ` Duc Dang
2015-05-28  8:05                                                           ` Marc Zyngier
2015-05-28  8:05                                                             ` Marc Zyngier
2015-05-28  8:05                                                             ` Marc Zyngier
2015-05-28 17:16                                                             ` Duc Dang
2015-05-28 17:16                                                               ` Duc Dang
2015-05-28 17:16                                                               ` Duc Dang
2015-05-29 18:24                                                             ` [PATCH v10 0/4] PCI: X-Gene: Add APM X-Gene v1 " Duc Dang
2015-05-29 18:24                                                               ` Duc Dang
2015-06-05 21:05                                                               ` Bjorn Helgaas
2015-06-05 21:05                                                                 ` Bjorn Helgaas
2015-06-05 21:11                                                                 ` Duc Dang
2015-06-05 21:11                                                                   ` Duc Dang
2015-05-29 18:24                                                             ` [PATCH v10 1/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node Duc Dang
2015-05-29 18:24                                                               ` Duc Dang
2015-05-29 18:24                                                             ` [PATCH v10 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver Duc Dang
2015-05-29 18:24                                                               ` Duc Dang
2015-05-29 18:24                                                             ` [PATCH v10 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node Duc Dang
2015-05-29 18:24                                                               ` Duc Dang
2015-05-29 18:24                                                             ` [PATCH v10 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver Duc Dang
2015-05-29 18:24                                                               ` Duc Dang
2015-05-27 18:27                                                         ` [PATCH v9 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node Duc Dang
2015-05-27 18:27                                                           ` Duc Dang
2015-05-27 18:27                                                         ` [PATCH v9 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver Duc Dang
2015-05-27 18:27                                                           ` Duc Dang
2015-05-27 18:31                                                         ` [PATCH v8 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver Duc Dang
2015-05-27 18:31                                                           ` Duc Dang
2015-05-27 18:31                                                           ` Duc Dang
2015-05-22 18:41                                                     ` [PATCH v8 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node Duc Dang
2015-05-22 18:41                                                       ` Duc Dang
2015-05-22 18:41                                                     ` [PATCH v8 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver Duc Dang
2015-05-22 18:41                                                       ` Duc Dang
2015-05-22 18:43                                                     ` [PATCH v7 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver Duc Dang
2015-05-22 18:43                                                       ` Duc Dang
2015-05-22 18:43                                                       ` Duc Dang
2015-05-18  9:55                                                 ` [PATCH v7 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node Duc Dang
2015-05-18  9:55                                                   ` Duc Dang
2015-05-18  9:55                                                 ` [PATCH v7 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver Duc Dang
2015-05-18  9:55                                                   ` Duc Dang
2015-05-18 10:12                                                 ` [PATCH v6 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver Duc Dang
2015-05-18 10:12                                                   ` Duc Dang
2015-05-18 10:12                                                   ` Duc Dang
2015-04-22  6:15                                             ` [PATCH v6 3/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node Duc Dang
2015-04-22  6:15                                               ` Duc Dang
2015-04-22  6:15                                             ` [PATCH v6 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver Duc Dang
2015-04-22  6:15                                               ` Duc Dang
2015-04-21  4:04                                         ` [PATCH v5 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node Duc Dang
2015-04-21  4:04                                           ` Duc Dang
2015-04-21 15:19                                           ` Marc Zyngier
2015-04-21 15:19                                             ` Marc Zyngier
2015-04-21 15:19                                             ` Marc Zyngier
2015-04-21 18:01                                             ` Duc Dang
2015-04-21 18:01                                               ` Duc Dang
2015-04-21 18:01                                               ` Duc Dang
2015-04-21  4:04                                         ` [PATCH v5 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node Duc Dang
2015-04-21  4:04                                           ` Duc Dang
2015-04-21 15:42                                           ` Mark Rutland
2015-04-21 15:42                                             ` Mark Rutland
2015-04-21 15:42                                             ` Mark Rutland
2015-04-21 17:37                                             ` Duc Dang
2015-04-21 17:37                                               ` Duc Dang
2015-04-21 17:37                                               ` Duc Dang
2015-04-21  4:04                                         ` [PATCH v5 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver Duc Dang
2015-04-21  4:04                                           ` Duc Dang
2015-04-11  0:16                         ` [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver Feng Kan
2015-04-11  0:16                           ` Feng Kan
2015-04-11  0:16                           ` Feng Kan
2015-04-11 12:18                           ` Marc Zyngier
2015-04-11 12:18                             ` Marc Zyngier
2015-04-11 14:50                           ` Arnd Bergmann
2015-04-11 14:50                             ` Arnd Bergmann
2015-04-11 14:50                             ` Arnd Bergmann
2015-04-10 18:13                       ` Paul Bolle
2015-04-10 18:13                         ` Paul Bolle
2015-04-10 23:55                         ` Duc Dang
2015-04-10 23:55                           ` Duc Dang
2015-04-09 17:05                     ` [PATCH v3 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node Duc Dang
2015-04-09 17:05                       ` Duc Dang
2015-04-09 17:05                     ` [PATCH v3 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node Duc Dang
2015-04-09 17:05                       ` Duc Dang
2015-04-09 17:05                     ` [PATCH v3 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver Duc Dang
2015-04-09 17:05                       ` Duc Dang
2015-04-09 17:20                     ` [PATCH v2 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver Duc Dang
2015-04-09 17:20                       ` Duc Dang
2015-03-04 19:39         ` [PATCH v2 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node Duc Dang
2015-03-04 19:39           ` Duc Dang
2015-03-04 19:39         ` [PATCH v2 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node Duc Dang
2015-03-04 19:39           ` Duc Dang
2015-03-04 19:40         ` [PATCH v2 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver Duc Dang
2015-03-04 19:40           ` Duc Dang
2015-01-06 16:15 ` [PATCH 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node Duc Dang
2015-01-06 16:15   ` Duc Dang
2015-01-06 16:15 ` [PATCH 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node Duc Dang
2015-01-06 16:15   ` Duc Dang
2015-01-06 19:34   ` Arnd Bergmann
2015-01-06 19:34     ` Arnd Bergmann
2015-01-06 16:15 ` [PATCH 4/4] PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver Duc Dang
2015-01-06 16:15   ` Duc Dang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.