Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1275111 > unrolled thread

[PATCH v2 0/7] Adding core support for wire-MSI bridges

Started byMarc Zyngier <marc.zyngier@arm.com>
First post2015-11-23 09:30 +0100
Last post2015-11-23 09:30 +0100
Articles 8 — 1 participant

Back to article view | Back to linux.kernel


Contents

  [PATCH v2 0/7] Adding core support for wire-MSI bridges Marc Zyngier <marc.zyngier@arm.com> - 2015-11-23 09:30 +0100
    [PATCH v2 7/7] irqchip: [Example] dummy wired interrupt/MSI bridge driver Marc Zyngier <marc.zyngier@arm.com> - 2015-11-23 09:30 +0100
    [PATCH v2 5/7] genirq/msi: Add msi_domain_populate_irqs Marc Zyngier <marc.zyngier@arm.com> - 2015-11-23 09:30 +0100
    [PATCH v2 3/7] irqdomain: Make irq_domain_alloc_irqs_recursive available Marc Zyngier <marc.zyngier@arm.com> - 2015-11-23 09:30 +0100
    [PATCH v2 2/7] platform-msi: Factor out allocation/free of private data Marc Zyngier <marc.zyngier@arm.com> - 2015-11-23 09:30 +0100
    [PATCH v2 6/7] platform-msi: Allow creation of a MSI-based stacked irq domain Marc Zyngier <marc.zyngier@arm.com> - 2015-11-23 09:30 +0100
    [PATCH v2 1/7] platform-msi: Allow MSIs to be allocated in chunks Marc Zyngier <marc.zyngier@arm.com> - 2015-11-23 09:30 +0100
    [PATCH v2 4/7] genirq/msi: Make the .prepare callback reusable Marc Zyngier <marc.zyngier@arm.com> - 2015-11-23 09:30 +0100

#1275111 — [PATCH v2 0/7] Adding core support for wire-MSI bridges

FromMarc Zyngier <marc.zyngier@arm.com>
Date2015-11-23 09:30 +0100
Subject[PATCH v2 0/7] Adding core support for wire-MSI bridges
Message-ID<qxUFz-7Ir-3@gated-at.bofh.it>
There seems to be a new class of interrupt controller out there whose
sole purpose (apart from making everybody's life a nightmare) is to
turn wired interrupts into MSIs (see [1] for a prime example of the
what this actually is).

Instead of considering that the MSIs allocated to a device are for the
direct use of that device, we can turn this set of MSIs into a irq
domain, and use this domain to back a stacked irqchip implemented by
the device. When combined with a GICv3 and its ITS, we end-up with the
following stack:

              wire                MSI
Client-device ----> Bridge-Device ----> Platform-MSI -> ITS -> GICv3

where we have a 1:1 mapping between wired interrupts and MSIs.

This requires some (slightly ugly) surgery in both the generic MSI and
platform MSI layers, but the amount of code is actually relatively
small (about +220 LoC so far).

On top of that, we add a dummy driver for a such a bridge, hoping that
this will give enough information to driver authors so that they can
use this new feature. An even more stupid client driver is provided to
show the interrupt stack allocation:

     dummydev dummy-dev: Probing
     dummydev dummy-dev: Allocated IRQ35
     dummydev dummy-dev: Probing OK
     dummydev dummy-dev: IRQ35 hwirq 5 domain msichip_domain_ops
     dummydev dummy-dev: IRQ35 hwirq 0 domain msi_domain_ops
     dummydev dummy-dev: IRQ35 hwirq 8192 domain its_domain_ops
     dummydev dummy-dev: IRQ35 hwirq 8192 domain gic_irq_domain_ops

Of course, it goes without saying that this last patch is not to be
merged...

While this does work, it is far from being perfect:

- It relies on not declaring this bridge with IRQCHIP_DECLARE in order
  to prevent the DT layer from allocating interrupts behind our back.

- There are probe ordering issues between the bridge and the wired
  interrupt device, leading to the use of -EPROBE_DEFER.

The last two points could be addressed directly in the OF layer, as
this is a generic device ordering issue (and people are already
working on that).

* From v1:
  - Added the error handling that was completely absent from the original
    prototype
  - Allow interrupts to be freed
  - Added API documentation
  - Rebased on top of 4.4-rc1

[1] https://lkml.org/lkml/2015/11/22/244

Marc Zyngier (7):
  platform-msi: Allow MSIs to be allocated in chunks
  platform-msi: Factor out allocation/free of private data
  irqdomain: Make irq_domain_alloc_irqs_recursive available
  genirq/msi: Make the .prepare callback reusable
  genirq/msi: Add msi_domain_populate_irqs
  platform-msi: Allow creation of a MSI-based stacked irq domain
  irqchip: [Example] dummy wired interrupt/MSI bridge driver

 drivers/base/platform-msi.c   | 254 ++++++++++++++++++++++++++++++---------
 drivers/irqchip/Kconfig       |   7 ++
 drivers/irqchip/Makefile      |   1 +
 drivers/irqchip/irq-msichip.c | 271 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/irqdomain.h     |   3 +
 include/linux/msi.h           |  18 +++
 kernel/irq/irqdomain.c        |   6 +-
 kernel/irq/msi.c              |  58 ++++++++-
 8 files changed, 558 insertions(+), 60 deletions(-)
 create mode 100644 drivers/irqchip/irq-msichip.c

-- 
2.1.4

--
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/

[toc] | [next] | [standalone]


#1275112 — [PATCH v2 7/7] irqchip: [Example] dummy wired interrupt/MSI bridge driver

FromMarc Zyngier <marc.zyngier@arm.com>
Date2015-11-23 09:30 +0100
Subject[PATCH v2 7/7] irqchip: [Example] dummy wired interrupt/MSI bridge driver
Message-ID<qxUFz-7Ir-7@gated-at.bofh.it>
In reply to#1275111
In order to demonstrate how to put together a wire/MSI bridge,
add a dummy driver that doesn't do anything at all, except
for allocating interrupts.

It comes together with an even more stupid client driver that
allocates an interrupt and dump the hierarchy of that interrupt.

This is not a candidate for merge, hence the:

Not-Quite-Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 drivers/irqchip/Kconfig       |   7 ++
 drivers/irqchip/Makefile      |   1 +
 drivers/irqchip/irq-msichip.c | 271 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 279 insertions(+)
 create mode 100644 drivers/irqchip/irq-msichip.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 4d7294e..73e38e6 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -193,3 +193,10 @@ config IRQ_MXS
 	def_bool y if MACH_ASM9260 || ARCH_MXS
 	select IRQ_DOMAIN
 	select STMP_DEVICE
+
+config DUMMY_MSICHIP
+	bool "Dummy irqchip on MSI"
+	depends on GENERIC_MSI_IRQ_DOMAIN
+	help
+	  Dummy stacked irqchip on top of platform MSI
+
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 177f78f..682e7dc 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -55,3 +55,4 @@ obj-$(CONFIG_RENESAS_H8S_INTC)		+= irq-renesas-h8s.o
 obj-$(CONFIG_ARCH_SA1100)		+= irq-sa11x0.o
 obj-$(CONFIG_INGENIC_IRQ)		+= irq-ingenic.o
 obj-$(CONFIG_IMX_GPCV2)			+= irq-imx-gpcv2.o
+obj-$(CONFIG_DUMMY_MSICHIP)		+= irq-msichip.o
diff --git a/drivers/irqchip/irq-msichip.c b/drivers/irqchip/irq-msichip.c
new file mode 100644
index 0000000..e0ff470
--- /dev/null
+++ b/drivers/irqchip/irq-msichip.c
@@ -0,0 +1,271 @@
+/*
+ * Non-functionnal example for a wired irq <-> MSI bridge
+ *
+ * Copyright (C) 2015 ARM Limited, All Rights Reserved.
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * DT fragment to represent the MSI bridge:
+
+	intc: msichip {
+		compatible = "dummy,msichip";
+		num-msis = 32;
+		interrupt-controller;
+		interrupt-parent = <&gic>;
+		#interrupt-cells = <0x2>;
+		msi-parent = <&its 1234>;
+	};
+
+ * DT fragment to represent the device connected to the bridge:
+
+	dummy-dev {
+		compatible = "dummy,device";
+		interrupt-parent = <&intc>;
+		interrupts = <0x5 0x1>;
+	};
+
+ * When "dummy,device" gets probed, it dumps the hierarchy for the
+ * interrupt it has allocated:
+
+	dummydev dummy-dev: Allocated IRQ35
+	dummydev dummy-dev: Probing OK
+	dummydev dummy-dev: IRQ35 hwirq 5 domain msichip_domain_ops
+	dummydev dummy-dev: IRQ35 hwirq 0 domain msi_domain_ops
+	dummydev dummy-dev: IRQ35 hwirq 8192 domain its_domain_ops
+	dummydev dummy-dev: IRQ35 hwirq 8192 domain gic_irq_domain_ops
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+static void msichip_mask(struct irq_data *data)
+{
+	/* Do something */
+}
+
+static void msichip_unmask(struct irq_data *data)
+{
+	/* Do something */
+}
+
+static void msichip_eoi(struct irq_data *data)
+{
+	/* Do something */
+}
+
+static int msichip_set_type(struct irq_data *data, unsigned int type)
+{
+	/* Do something */
+	return 0;
+}
+
+static int msichip_retrigger(struct irq_data *data)
+{
+	/* Do something */
+	return 0;
+}
+
+static int msichip_set_affinity(struct irq_data *data,
+				const struct cpumask *dest, bool force)
+{
+	/* Do something */
+	return 0;
+}
+
+static struct irq_chip msichip_chip = {
+	.name			= "MSICHIP",
+	.irq_mask		= msichip_mask,
+	.irq_unmask		= msichip_unmask,
+	.irq_eoi		= msichip_eoi,
+	.irq_set_type		= msichip_set_type,
+	.irq_retrigger		= msichip_retrigger,
+	.irq_set_affinity	= msichip_set_affinity,
+};
+
+static int msichip_domain_translate(struct irq_domain *d,
+				    struct irq_fwspec *fwspec,
+				    unsigned long *hwirq,
+				    unsigned int *type)
+{
+	if (is_of_node(fwspec->fwnode)) {
+		if (fwspec->param_count != 2)
+			return -EINVAL;
+
+		*hwirq = fwspec->param[0];
+		*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int msichip_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				   unsigned int nr_irqs, void *arg)
+{
+	int i, err;
+	irq_hw_number_t hwirq;
+	unsigned int type;
+	struct irq_fwspec *fwspec = arg;
+	void *data;
+
+	err = msichip_domain_translate(domain, fwspec, &hwirq, &type);
+	if (err)
+		return err;
+
+	err = platform_msi_domain_alloc(domain, virq, nr_irqs);
+	if (err)
+		return err;
+
+	data = platform_msi_get_host_data(domain);
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+					      &msichip_chip, data);
+
+	return 0;
+}
+
+static const struct irq_domain_ops msichip_domain_ops = {
+	.translate	= msichip_domain_translate,
+	.alloc		= msichip_domain_alloc,
+	.free		= platform_msi_domain_free,
+};
+
+struct msichip_data {
+	/* Add whatever you fancy here */
+	struct platform_device *pdev;
+};
+
+static void msichip_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
+{
+	/* Do the right thing */
+}
+
+static int msichip_probe(struct platform_device *pdev)
+{
+	struct irq_domain *domain;
+	struct msichip_data *msichip_data;
+	u32 num_msis;
+
+	dev_info(&pdev->dev, "Probing\n");
+	msichip_data = kzalloc(sizeof(*msichip_data), GFP_KERNEL);
+	if (!msichip_data)
+		return -ENOMEM;
+
+	msichip_data->pdev = pdev;
+
+	/* If there is no "num-msi" property, assume 64... */
+	if (of_property_read_u32(pdev->dev.of_node, "num-msis", &num_msis) < 0)
+		num_msis = 64;
+
+	dev_info(&pdev->dev, "allocating %d MSIs\n", num_msis);
+
+	domain = platform_msi_create_device_domain(&pdev->dev, num_msis,
+						   msichip_write_msi_msg,
+						   &msichip_domain_ops,
+						   msichip_data);
+
+	if (!domain){
+		kfree(msichip_data);
+		return -ENOMEM;
+	}
+
+	dev_info(&pdev->dev, "Probing OK\n");
+	return 0;
+}
+
+static const struct of_device_id msichip_of_match[] = {
+	{ .compatible = "dummy,msichip", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, msichip_of_match);
+
+static struct platform_driver msichip_driver = {
+	.driver = {
+		.name		= "msichip",
+		.of_match_table	= msichip_of_match,
+	},
+	.probe			= msichip_probe,
+};
+/* Do not define this as an irqchip */
+module_platform_driver(msichip_driver);
+
+
+
+/* Driver for a dummy device connected to the MSI bridge */
+static irqreturn_t dummydev_handler(int irq, void *dummy)
+{
+	return IRQ_HANDLED;
+}
+
+static void dummydev_dump_hierarchy(struct device *dev, int irq)
+{
+	struct irq_data *data = irq_get_irq_data(irq);
+
+	while(data) {
+		dev_info(dev, "IRQ%d hwirq %d domain %ps\n",
+			 data->irq, (int)data->hwirq, data->domain->ops);
+		data = data->parent_data;
+	}
+}
+
+static int dummydev_probe(struct platform_device *pdev)
+{
+	int irq;
+
+	dev_info(&pdev->dev, "Probing\n");
+	irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+	if (!irq) {
+		dev_err(&pdev->dev, "irq allocation failed, defering\n");
+		return -EPROBE_DEFER;
+	}
+
+	dev_info(&pdev->dev, "Allocated IRQ%d\n", irq);
+
+	if (request_irq(irq, dummydev_handler, 0, "dummydev", pdev))
+		return -EINVAL;
+
+	dev_info(&pdev->dev, "Probing OK\n");
+
+	dummydev_dump_hierarchy(&pdev->dev, irq);
+	return 0;
+}
+
+static const struct of_device_id dummydev_of_match[] = {
+	{ .compatible = "dummy,device", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, dummydev_of_match);
+
+static struct platform_driver dummydev_driver = {
+	.driver = {
+		.name		= "dummydev",
+		.of_match_table	= dummydev_of_match,
+	},
+	.probe			= dummydev_probe,
+};
+
+module_platform_driver(dummydev_driver);
-- 
2.1.4

--
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/

[toc] | [prev] | [next] | [standalone]


#1275113 — [PATCH v2 5/7] genirq/msi: Add msi_domain_populate_irqs

FromMarc Zyngier <marc.zyngier@arm.com>
Date2015-11-23 09:30 +0100
Subject[PATCH v2 5/7] genirq/msi: Add msi_domain_populate_irqs
Message-ID<qxUFz-7Ir-9@gated-at.bofh.it>
In reply to#1275111
To be able to allocate interrupts from the MSI layer down,
add a new msi_domain_populate_irqs entry point.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 include/linux/msi.h |  2 ++
 kernel/irq/msi.c    | 40 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index 1c0bb2c..cee102b 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -283,6 +283,8 @@ void platform_msi_domain_free_irqs(struct device *dev);
 /* When an MSI domain is used as an intermediate domain */
 int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
 			    int nvec, msi_alloc_info_t *args);
+int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev,
+			     int virq, int nvec, msi_alloc_info_t *args);
 #endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */
 
 #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index 9a85613..15b249e 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -266,6 +266,46 @@ int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
 	return ret;
 }
 
+int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev,
+			     int virq, int nvec, msi_alloc_info_t *arg)
+{
+	struct msi_domain_info *info = domain->host_data;
+	struct msi_domain_ops *ops = info->ops;
+	struct msi_desc *desc;
+	int ret = 0;
+
+	for_each_msi_entry(desc, dev) {
+		/* Don't even try the multi-MSI brain damage. */
+		if (WARN_ON(!desc->irq || desc->nvec_used != 1)) {
+			ret = -EINVAL;
+			break;
+		}
+
+		if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
+			continue;
+
+		ops->set_desc(arg, desc);
+		/* Assumes the domain mutex is held! */
+		ret = irq_domain_alloc_irqs_recursive(domain, virq, 1, arg);
+		if (ret)
+			break;
+
+		irq_set_msi_desc_off(virq, 0, desc);
+	}
+
+	if (ret) {
+		/* Mop up the damage */
+		for_each_msi_entry(desc, dev) {
+			if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
+				continue;
+
+			irq_domain_free_irqs_common(domain, desc->irq, 1);
+		}
+	}
+
+	return ret;
+}
+
 /**
  * msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain
  * @domain:	The domain to allocate from
-- 
2.1.4

--
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/

[toc] | [prev] | [next] | [standalone]


#1275114 — [PATCH v2 3/7] irqdomain: Make irq_domain_alloc_irqs_recursive available

FromMarc Zyngier <marc.zyngier@arm.com>
Date2015-11-23 09:30 +0100
Subject[PATCH v2 3/7] irqdomain: Make irq_domain_alloc_irqs_recursive available
Message-ID<qxUFz-7Ir-5@gated-at.bofh.it>
In reply to#1275111
We are soon going to need the MSI layer to call into the domain
allocators. Instead of open coding this, make the standard
irq_domain_alloc_irqs_recursive function available to the MSI
layer.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 include/linux/irqdomain.h | 3 +++
 kernel/irq/irqdomain.c    | 6 +++---
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index d5e5c5b..cf96c6a 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -367,6 +367,9 @@ static inline int irq_domain_alloc_irqs(struct irq_domain *domain,
 	return __irq_domain_alloc_irqs(domain, -1, nr_irqs, node, arg, false);
 }
 
+extern int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
+					   unsigned int irq_base,
+					   unsigned int nr_irqs, void *arg);
 extern int irq_domain_set_hwirq_and_chip(struct irq_domain *domain,
 					 unsigned int virq,
 					 irq_hw_number_t hwirq,
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 22aa961..1c9973e 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -1125,9 +1125,9 @@ static void irq_domain_free_irqs_recursive(struct irq_domain *domain,
 	}
 }
 
-static int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
-					   unsigned int irq_base,
-					   unsigned int nr_irqs, void *arg)
+int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
+				    unsigned int irq_base,
+				    unsigned int nr_irqs, void *arg)
 {
 	int ret = 0;
 	struct irq_domain *parent = domain->parent;
-- 
2.1.4

--
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/

[toc] | [prev] | [next] | [standalone]


#1275115 — [PATCH v2 2/7] platform-msi: Factor out allocation/free of private data

FromMarc Zyngier <marc.zyngier@arm.com>
Date2015-11-23 09:30 +0100
Subject[PATCH v2 2/7] platform-msi: Factor out allocation/free of private data
Message-ID<qxUFz-7Ir-15@gated-at.bofh.it>
In reply to#1275111
As we're going to have multiple paths to allocate/free the
platform-msi private data, factor this out into separate
utility functions.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 drivers/base/platform-msi.c | 94 +++++++++++++++++++++++++--------------------
 1 file changed, 53 insertions(+), 41 deletions(-)

diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c
index 6148c78..44b8c0d 100644
--- a/drivers/base/platform-msi.c
+++ b/drivers/base/platform-msi.c
@@ -189,21 +189,11 @@ struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
 	return domain;
 }
 
-/**
- * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
- * @dev:		The device for which to allocate interrupts
- * @nvec:		The number of interrupts to allocate
- * @write_msi_msg:	Callback to write an interrupt message for @dev
- *
- * Returns:
- * Zero for success, or an error code in case of failure
- */
-int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
-				   irq_write_msi_msg_t write_msi_msg)
+static struct platform_msi_priv_data *
+platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
+			     irq_write_msi_msg_t write_msi_msg)
 {
-	struct platform_msi_priv_data *priv_data;
-	int err;
-
+	struct platform_msi_priv_data *datap;
 	/*
 	 * Limit the number of interrupts to 256 per device. Should we
 	 * need to bump this up, DEV_ID_SHIFT should be adjusted
@@ -211,33 +201,62 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
 	 * capable devices).
 	 */
 	if (!dev->msi_domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
 	if (dev->msi_domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) {
 		dev_err(dev, "Incompatible msi_domain, giving up\n");
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 	}
 
 	/* Already had a helping of MSI? Greed... */
 	if (!list_empty(dev_to_msi_list(dev)))
-		return -EBUSY;
+		return ERR_PTR(-EBUSY);
+
+	datap = kzalloc(sizeof(*datap), GFP_KERNEL);
+	if (!datap)
+		return ERR_PTR(-ENOMEM);
+
+	datap->devid = ida_simple_get(&platform_msi_devid_ida,
+				      0, 1 << DEV_ID_SHIFT, GFP_KERNEL);
+	if (datap->devid < 0) {
+		int err = datap->devid;
+		kfree(datap);
+		return ERR_PTR(err);
+	}
 
-	priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL);
-	if (!priv_data)
-		return -ENOMEM;
+	datap->write_msg = write_msi_msg;
 
-	priv_data->devid = ida_simple_get(&platform_msi_devid_ida,
-					  0, 1 << DEV_ID_SHIFT, GFP_KERNEL);
-	if (priv_data->devid < 0) {
-		err = priv_data->devid;
-		goto out_free_data;
-	}
+	return datap;
+}
+
+static void platform_msi_free_priv_data(struct platform_msi_priv_data *data)
+{
+	ida_simple_remove(&platform_msi_devid_ida, data->devid);
+	kfree(data);
+}
+
+/**
+ * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
+ * @dev:		The device for which to allocate interrupts
+ * @nvec:		The number of interrupts to allocate
+ * @write_msi_msg:	Callback to write an interrupt message for @dev
+ *
+ * Returns:
+ * Zero for success, or an error code in case of failure
+ */
+int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
+				   irq_write_msi_msg_t write_msi_msg)
+{
+	struct platform_msi_priv_data *priv_data;
+	int err;
 
-	priv_data->write_msg = write_msi_msg;
+	priv_data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
+	if (IS_ERR(priv_data))
+		return PTR_ERR(priv_data);
 
 	err = platform_msi_alloc_descs(dev, nvec, priv_data);
 	if (err)
-		goto out_free_id;
+		goto out_free_priv_data;
 
 	err = msi_domain_alloc_irqs(dev->msi_domain, dev, nvec);
 	if (err)
@@ -247,10 +266,8 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
 
 out_free_desc:
 	platform_msi_free_descs(dev, 0, nvec);
-out_free_id:
-	ida_simple_remove(&platform_msi_devid_ida, priv_data->devid);
-out_free_data:
-	kfree(priv_data);
+out_free_priv_data:
+	platform_msi_free_priv_data(priv_data);
 
 	return err;
 }
@@ -261,16 +278,11 @@ out_free_data:
  */
 void platform_msi_domain_free_irqs(struct device *dev)
 {
-	struct msi_desc *desc;
-
-	desc = first_msi_entry(dev);
-	if (desc) {
-		struct platform_msi_priv_data *data;
-
-		data = desc->platform.msi_priv_data;
+	if (!list_empty(dev_to_msi_list(dev))) {
+		struct msi_desc *desc;
 
-		ida_simple_remove(&platform_msi_devid_ida, data->devid);
-		kfree(data);
+		desc = first_msi_entry(dev);
+		platform_msi_free_priv_data(desc->platform.msi_priv_data);
 	}
 
 	msi_domain_free_irqs(dev->msi_domain, dev);
-- 
2.1.4

--
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/

[toc] | [prev] | [next] | [standalone]


#1275119 — [PATCH v2 6/7] platform-msi: Allow creation of a MSI-based stacked irq domain

FromMarc Zyngier <marc.zyngier@arm.com>
Date2015-11-23 09:30 +0100
Subject[PATCH v2 6/7] platform-msi: Allow creation of a MSI-based stacked irq domain
Message-ID<qxUFA-7Ir-25@gated-at.bofh.it>
In reply to#1275111
We almost have all the needed bits requiredable to create a irq domain
on top of a MSI domain.

For this, we enable a few things:
- the virq is stored in the msi_desc
- device, msi_alloc_info and domain-specific data
  are stored in the platform_priv_data structure
- we introduce a new API for platform-msi:

  /* Create a MSI-based domain */
  struct irq_domain *
  platform_msi_create_device_domain(struct device *dev,
                                    unsigned int nvec,
                                    irq_write_msi_msg_t write_msi_msg,
                                    const struct irq_domain_ops *ops,
                                    void *host_data);

  /* Allocate MSIs in an MSI domain */
  int platform_msi_domain_alloc(struct irq_domain *domain,
				unsigned int virq,
				unsigned int nr_irqs);

  /* Free MSIs from an MSI domain */
  void platform_msi_domain_free(struct irq_domain *domain,
				unsigned int virq,
				unsigned int nvec);

  /* Obtain the host data passed to platform_msi_create_device_domain */
  void *platform_msi_get_host_data(struct irq_domain *domain);

platform_msi_create_device_domain() is a hybrid of irqdomain creation
and interrupt allocation, creating a domain backed by the MSIs associated
to a device. IRQs can then be allocated in that domain using
platform_msi_domain_alloc().

This now allows a wired irq to MSI bridge to be created.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 drivers/base/platform-msi.c | 130 +++++++++++++++++++++++++++++++++++++++++++-
 include/linux/msi.h         |  12 ++++
 2 files changed, 140 insertions(+), 2 deletions(-)

diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c
index 44b8c0d..a203896 100644
--- a/drivers/base/platform-msi.c
+++ b/drivers/base/platform-msi.c
@@ -32,6 +32,9 @@
  * and the callback to write the MSI message.
  */
 struct platform_msi_priv_data {
+	struct device		*dev;
+	void 			*host_data;
+	msi_alloc_info_t	arg;
 	irq_write_msi_msg_t	write_msg;
 	int			devid;
 };
@@ -124,8 +127,9 @@ static void platform_msi_free_descs(struct device *dev, int base, int nvec)
 	}
 }
 
-static int platform_msi_alloc_descs(struct device *dev, int nvec,
-				    struct platform_msi_priv_data *data)
+static int platform_msi_alloc_descs_with_irq(struct device *dev, int virq,
+					     int nvec,
+					     struct platform_msi_priv_data *data)
 
 {
 	struct msi_desc *desc;
@@ -145,6 +149,7 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec,
 		desc->platform.msi_priv_data = data;
 		desc->platform.msi_index = base + i;
 		desc->nvec_used = 1;
+		desc->irq = virq ? virq + i : 0;
 
 		list_add_tail(&desc->list, dev_to_msi_list(dev));
 	}
@@ -159,6 +164,13 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec,
 	return 0;
 }
 
+static int platform_msi_alloc_descs(struct device *dev, int nvec,
+				    struct platform_msi_priv_data *data)
+
+{
+	return platform_msi_alloc_descs_with_irq(dev, 0, nvec, data);
+}
+
 /**
  * platform_msi_create_irq_domain - Create a platform MSI interrupt domain
  * @fwnode:		Optional fwnode of the interrupt controller
@@ -225,6 +237,7 @@ platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
 	}
 
 	datap->write_msg = write_msi_msg;
+	datap->dev = dev;
 
 	return datap;
 }
@@ -288,3 +301,116 @@ void platform_msi_domain_free_irqs(struct device *dev)
 	msi_domain_free_irqs(dev->msi_domain, dev);
 	platform_msi_free_descs(dev, 0, MAX_DEV_MSIS);
 }
+
+/**
+ * platform_msi_get_host_data - Query the private data associated with
+ *                              a platform-msi domain
+ * @domain:	The platform-msi domain
+ *
+ * Returns the private data provided when calling
+ * platform_msi_create_device_domain.
+ */
+void *platform_msi_get_host_data(struct irq_domain *domain)
+{
+	struct platform_msi_priv_data *data = domain->host_data;
+	return data->host_data;
+}
+
+/**
+ * platform_msi_create_device_domain - Create a platform-msi domain
+ *
+ * @dev:		The device generating the MSIs
+ * @nvec:		The number of MSIs that need to be allocated
+ * @write_msi_msg:	Callback to write an interrupt message for @dev
+ * @ops:		The hierarchy domain operations to use
+ * @host_data:		Private data associated to this domain
+ *
+ * Returns an irqdomain for @nvec interrupts
+ */
+struct irq_domain *
+platform_msi_create_device_domain(struct device *dev,
+				  unsigned int nvec,
+				  irq_write_msi_msg_t write_msi_msg,
+				  const struct irq_domain_ops *ops,
+				  void *host_data)
+{
+	struct platform_msi_priv_data *data;
+	struct irq_domain *domain;
+	int err;
+
+	data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
+	if (IS_ERR(data))
+		return NULL;
+
+	data->host_data = host_data;
+	domain = irq_domain_create_hierarchy(dev->msi_domain, 0, nvec,
+					     of_node_to_fwnode(dev->of_node),
+					     ops, data);
+	if (!domain)
+		goto free_priv;
+
+	err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg);
+	if (err)
+		goto free_domain;
+
+	return domain;
+
+free_domain:
+	irq_domain_remove(domain);
+free_priv:
+	platform_msi_free_priv_data(data);
+	return NULL;
+}
+
+/**
+ * platform_msi_domain_free - Free interrupts associated with a platform-msi
+ *                            domain
+ *
+ * @domain:	The platform-msi domain
+ * @virq:	The base irq from which to perform the free operation
+ * @nvec:	How many interrupts to free from @virq
+ */
+void platform_msi_domain_free(struct irq_domain *domain, unsigned int virq,
+			      unsigned int nvec)
+{
+	struct platform_msi_priv_data *data = domain->host_data;
+	struct msi_desc *desc;
+	for_each_msi_entry(desc, data->dev) {
+		if (WARN_ON(!desc->irq || desc->nvec_used != 1))
+			return;
+		if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
+			continue;
+
+		irq_domain_free_irqs_common(domain, desc->irq, 1);
+	}
+}
+
+/**
+ * platform_msi_domain_alloc - Allocate interrupts associated with
+ *			       a platform-msi domain
+ *
+ * @domain:	The platform-msi domain
+ * @virq:	The base irq from which to perform the allocate operation
+ * @nvec:	How many interrupts to free from @virq
+ *
+ * Return 0 on success, or an error code on failure. Must be called
+ * with irq_domain_mutex held (which can only be done as part of a
+ * top-level interrupt allocation).
+ */
+int platform_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+			      unsigned int nr_irqs)
+{
+	struct platform_msi_priv_data *data = domain->host_data;
+	int err;
+
+	err = platform_msi_alloc_descs_with_irq(data->dev, virq, nr_irqs, data);
+	if (err)
+		return err;
+
+	err = msi_domain_populate_irqs(domain->parent, data->dev,
+				       virq, nr_irqs, &data->arg);
+	if (err)
+		platform_msi_domain_free(domain, virq, nr_irqs);
+
+	return err;
+}
diff --git a/include/linux/msi.h b/include/linux/msi.h
index cee102b..1c6342a 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -174,6 +174,7 @@ struct msi_controller {
 #include <asm/msi.h>
 
 struct irq_domain;
+struct irq_domain_ops;
 struct irq_chip;
 struct device_node;
 struct fwnode_handle;
@@ -285,6 +286,17 @@ int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
 			    int nvec, msi_alloc_info_t *args);
 int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev,
 			     int virq, int nvec, msi_alloc_info_t *args);
+struct irq_domain *
+platform_msi_create_device_domain(struct device *dev,
+				  unsigned int nvec,
+				  irq_write_msi_msg_t write_msi_msg,
+				  const struct irq_domain_ops *ops,
+				  void *host_data);
+int platform_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+			      unsigned int nr_irqs);
+void platform_msi_domain_free(struct irq_domain *domain, unsigned int virq,
+			      unsigned int nvec);
+void *platform_msi_get_host_data(struct irq_domain *domain);
 #endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */
 
 #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
-- 
2.1.4

--
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/

[toc] | [prev] | [next] | [standalone]


#1275121 — [PATCH v2 1/7] platform-msi: Allow MSIs to be allocated in chunks

FromMarc Zyngier <marc.zyngier@arm.com>
Date2015-11-23 09:30 +0100
Subject[PATCH v2 1/7] platform-msi: Allow MSIs to be allocated in chunks
Message-ID<qxUFB-7Ir-33@gated-at.bofh.it>
In reply to#1275111
MSIs for a given device are normally all allocated in one go.
Make sure the internal code can allocate them one at a time
if required.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 drivers/base/platform-msi.c | 32 ++++++++++++++++++++------------
 1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c
index 5df4575..6148c78 100644
--- a/drivers/base/platform-msi.c
+++ b/drivers/base/platform-msi.c
@@ -25,6 +25,7 @@
 #include <linux/slab.h>
 
 #define DEV_ID_SHIFT	24
+#define MAX_DEV_MSIS	(1 << (32 - DEV_ID_SHIFT))
 
 /*
  * Internal data structure containing a (made up, but unique) devid
@@ -110,13 +111,16 @@ static void platform_msi_update_chip_ops(struct msi_domain_info *info)
 		chip->irq_write_msi_msg = platform_msi_write_msg;
 }
 
-static void platform_msi_free_descs(struct device *dev)
+static void platform_msi_free_descs(struct device *dev, int base, int nvec)
 {
 	struct msi_desc *desc, *tmp;
 
 	list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
-		list_del(&desc->list);
-		free_msi_entry(desc);
+		if (desc->platform.msi_index >= base &&
+		    desc->platform.msi_index < (base + nvec)) {
+			list_del(&desc->list);
+			free_msi_entry(desc);
+		}
 	}
 }
 
@@ -124,17 +128,22 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec,
 				    struct platform_msi_priv_data *data)
 
 {
-	int i;
+	struct msi_desc *desc;
+	int i, base = 0;
 
-	for (i = 0; i < nvec; i++) {
-		struct msi_desc *desc;
+	if (!list_empty(dev_to_msi_list(dev))) {
+		desc = list_last_entry(dev_to_msi_list(dev),
+				       struct msi_desc, list);
+		base = desc->platform.msi_index + 1;
+	}
 
+	for (i = 0; i < nvec; i++) {
 		desc = alloc_msi_entry(dev);
 		if (!desc)
 			break;
 
 		desc->platform.msi_priv_data = data;
-		desc->platform.msi_index = i;
+		desc->platform.msi_index = base + i;
 		desc->nvec_used = 1;
 
 		list_add_tail(&desc->list, dev_to_msi_list(dev));
@@ -142,7 +151,7 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec,
 
 	if (i != nvec) {
 		/* Clean up the mess */
-		platform_msi_free_descs(dev);
+		platform_msi_free_descs(dev, base, nvec);
 
 		return -ENOMEM;
 	}
@@ -201,8 +210,7 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
 	 * accordingly (which would impact the max number of MSI
 	 * capable devices).
 	 */
-	if (!dev->msi_domain || !write_msi_msg || !nvec ||
-	    nvec > (1 << (32 - DEV_ID_SHIFT)))
+	if (!dev->msi_domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS)
 		return -EINVAL;
 
 	if (dev->msi_domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) {
@@ -238,7 +246,7 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
 	return 0;
 
 out_free_desc:
-	platform_msi_free_descs(dev);
+	platform_msi_free_descs(dev, 0, nvec);
 out_free_id:
 	ida_simple_remove(&platform_msi_devid_ida, priv_data->devid);
 out_free_data:
@@ -266,5 +274,5 @@ void platform_msi_domain_free_irqs(struct device *dev)
 	}
 
 	msi_domain_free_irqs(dev->msi_domain, dev);
-	platform_msi_free_descs(dev);
+	platform_msi_free_descs(dev, 0, MAX_DEV_MSIS);
 }
-- 
2.1.4

--
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/

[toc] | [prev] | [next] | [standalone]


#1275123 — [PATCH v2 4/7] genirq/msi: Make the .prepare callback reusable

FromMarc Zyngier <marc.zyngier@arm.com>
Date2015-11-23 09:30 +0100
Subject[PATCH v2 4/7] genirq/msi: Make the .prepare callback reusable
Message-ID<qxUFA-7Ir-29@gated-at.bofh.it>
In reply to#1275111
The .prepare callbacks are so far only called from msi_domain_alloc_irqs.
In order to reuse that code, split that code and create a
msi_domain_prepare_irqs function that the existing code can call into.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 include/linux/msi.h |  4 ++++
 kernel/irq/msi.c    | 18 +++++++++++++++---
 2 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index f71a25e..1c0bb2c 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -279,6 +279,10 @@ struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
 int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
 				   irq_write_msi_msg_t write_msi_msg);
 void platform_msi_domain_free_irqs(struct device *dev);
+
+/* When an MSI domain is used as an intermediate domain */
+int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
+			    int nvec, msi_alloc_info_t *args);
 #endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */
 
 #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index 6b0c0b7..9a85613 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -252,6 +252,20 @@ struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode,
 					   &msi_domain_ops, info);
 }
 
+int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
+			    int nvec, msi_alloc_info_t *arg)
+{
+	struct msi_domain_info *info = domain->host_data;
+	struct msi_domain_ops *ops = info->ops;
+	int ret;
+
+	ret = ops->msi_check(domain, info, dev);
+	if (ret == 0)
+		ret = ops->msi_prepare(domain, dev, nvec, arg);
+
+	return ret;
+}
+
 /**
  * msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain
  * @domain:	The domain to allocate from
@@ -270,9 +284,7 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
 	struct msi_desc *desc;
 	int i, ret, virq = -1;
 
-	ret = ops->msi_check(domain, info, dev);
-	if (ret == 0)
-		ret = ops->msi_prepare(domain, dev, nvec, &arg);
+	ret = msi_domain_prepare_irqs(domain, dev, nvec, &arg);
 	if (ret)
 		return ret;
 
-- 
2.1.4

--
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/

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web