linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* multiple MSI interrupts on FSL-MPIC
@ 2014-02-20 20:53 Sebastian Andrzej Siewior
  2014-02-20 20:53 ` [PATCH 1/2] irqdomain: add support for creating a continous mapping Sebastian Andrzej Siewior
  2014-02-20 20:53 ` [PATCH 2/2] powerpc: msi: fsl: add support for multiple MSI interrupts Sebastian Andrzej Siewior
  0 siblings, 2 replies; 11+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-02-20 20:53 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Scott Wood, linuxppc-dev

This series adds support for multiple MSI interrupts on FSL's MPIC. The
code has been tested on v3.2 with custom FPGA device on PCIe and then
forward ported. Patch #1 has been created from scratch during porting,
patch #2 almost applied as-it.

Sebastian

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

* [PATCH 1/2] irqdomain: add support for creating a continous mapping
  2014-02-20 20:53 multiple MSI interrupts on FSL-MPIC Sebastian Andrzej Siewior
@ 2014-02-20 20:53 ` Sebastian Andrzej Siewior
  2014-02-20 21:06   ` Scott Wood
  2014-02-20 20:53 ` [PATCH 2/2] powerpc: msi: fsl: add support for multiple MSI interrupts Sebastian Andrzej Siewior
  1 sibling, 1 reply; 11+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-02-20 20:53 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Scott Wood, Sebastian Andrzej Siewior, linuxppc-dev, Thomas Gleixner

A MSI device may have multiple interrupts. That means that the
interrupts numbers should be continuos so that pdev->irq refers to the
first interrupt, pdev->irq + 1 to the second and so on.
This patch adds support for continuous allocation of virqs for a range
of hwirqs. The function is based on irq_create_mapping() but due to the
number argument there is very little in common now.

Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 include/linux/irqdomain.h |  2 ++
 kernel/irq/irqdomain.c    | 61 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index c983ed1..21d0635 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -175,6 +175,8 @@ extern void irq_domain_associate_many(struct irq_domain *domain,
 
 extern unsigned int irq_create_mapping(struct irq_domain *host,
 				       irq_hw_number_t hwirq);
+extern unsigned int irq_create_mapping_block(struct irq_domain *host,
+		irq_hw_number_t hwirq, unsigned int num);
 extern void irq_dispose_mapping(unsigned int virq);
 
 /**
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index cf68bb3..323d417 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -433,6 +433,67 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
 EXPORT_SYMBOL_GPL(irq_create_mapping);
 
 /**
+ * irq_create_mapping_block() - Map multiple hardware interrupts
+ * @domain: domain owning this hardware interrupt or NULL for default domain
+ * @hwirq: hardware irq number in that domain space
+ * @num: number of interrupts
+ *
+ * Maps a hwirq to a newly allocated virq. Num should be greater than 1 so num
+ * hwirqs (hwirq … hwirq + num - 1) will be mapped which and virq will be
+ * continuous. Returns the first linux virq number.
+ *
+ * If the sense/trigger is to be specified, set_irq_type() should be called
+ * on the number returned from that call.
+ */
+unsigned int irq_create_mapping_block(struct irq_domain *domain,
+		irq_hw_number_t hwirq, unsigned int num)
+{
+	int virq;
+	int i;
+
+	pr_debug("%s(0x%p, 0x%lx) %d\n", __func__, domain, hwirq, num);
+
+	if (num < 2)
+		return 0;
+
+	/* Look for default domain if nececssary */
+	if (domain == NULL) {
+		WARN(1, "%s(, %lx) called with NULL domain\n", __func__, hwirq);
+		return 0;
+	}
+	for (i = 0; i < num; i++) {
+		/* Check if mapping already exists */
+		virq = irq_find_mapping(domain, hwirq);
+		if (virq != NO_IRQ) {
+			if (i == 0) {
+				pr_debug("-> existing mapping on virq %d\n",
+						virq);
+				return virq;
+			}
+			pr_err("irq: hwirq %ld has no mapping but hwirq %ld "
+				"maps to virq %d. This can't be a block\n",
+				hwirq, hwirq + i, virq);
+			return -EINVAL;
+		}
+	}
+
+	/* Allocate a virtual interrupt number */
+	virq = irq_alloc_descs_from(1, num, of_node_to_nid(domain->of_node));
+	if (virq <= 0) {
+		pr_debug("-> virq allocation failed\n");
+		return 0;
+	}
+
+	irq_domain_associate_many(domain, virq, hwirq, num);
+
+	pr_debug("irqs %lu…%lu on domain %s mapped to virtual irqs %u…%u\n",
+		hwirq, hwirq + num - 1, of_node_full_name(domain->of_node),
+		virq, virq + num - 1);
+
+	return virq;
+}
+
+/**
  * irq_create_strict_mappings() - Map a range of hw irqs to fixed linux irqs
  * @domain: domain owning the interrupt range
  * @irq_base: beginning of linux IRQ range
-- 
1.9.0.rc3

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

* [PATCH 2/2] powerpc: msi: fsl: add support for multiple MSI interrupts
  2014-02-20 20:53 multiple MSI interrupts on FSL-MPIC Sebastian Andrzej Siewior
  2014-02-20 20:53 ` [PATCH 1/2] irqdomain: add support for creating a continous mapping Sebastian Andrzej Siewior
@ 2014-02-20 20:53 ` Sebastian Andrzej Siewior
  1 sibling, 0 replies; 11+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-02-20 20:53 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Gavin Shan, Arnd Bergmann, Minghuan Lian, Alexey Kardashevskiy,
	Alistair Popple, Sebastian Andrzej Siewior, Brian King,
	Anton Blanchard, Scott Wood, linuxppc-dev

This patch pushes the check for nvec > 1 && MSI into the check function
of each MSI driver except for FSL's MSI where the functionality is
added.

Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Gavin Shan <shangw@linux.vnet.ibm.com>
Cc: Alexey Kardashevskiy <aik@ozlabs.ru>
Cc: Alistair Popple <alistair@popple.id.au>
Cc: Brian King <brking@linux.vnet.ibm.com>
Cc: Anton Blanchard <anton@samba.org>
Cc: Scott Wood <scottwood@freescale.com>
Cc: Minghuan Lian <Minghuan.Lian@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 arch/powerpc/kernel/msi.c              |  4 ----
 arch/powerpc/platforms/cell/axon_msi.c |  3 +++
 arch/powerpc/platforms/powernv/pci.c   |  2 ++
 arch/powerpc/platforms/pseries/msi.c   |  3 +++
 arch/powerpc/platforms/wsp/msi.c       |  8 ++++++++
 arch/powerpc/sysdev/fsl_msi.c          | 29 +++++++++++++++++++++--------
 arch/powerpc/sysdev/mpic_pasemi_msi.c  |  3 ++-
 arch/powerpc/sysdev/mpic_u3msi.c       |  2 ++
 arch/powerpc/sysdev/ppc4xx_msi.c       |  2 ++
 9 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/arch/powerpc/kernel/msi.c b/arch/powerpc/kernel/msi.c
index 8bbc12d..46b1470 100644
--- a/arch/powerpc/kernel/msi.c
+++ b/arch/powerpc/kernel/msi.c
@@ -20,10 +20,6 @@ int arch_msi_check_device(struct pci_dev* dev, int nvec, int type)
 		return -ENOSYS;
 	}
 
-	/* PowerPC doesn't support multiple MSI yet */
-	if (type == PCI_CAP_ID_MSI && nvec > 1)
-		return 1;
-
 	if (ppc_md.msi_check_device) {
 		pr_debug("msi: Using platform check routine.\n");
 		return ppc_md.msi_check_device(dev, nvec, type);
diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c
index 85825b5..6e592ed 100644
--- a/arch/powerpc/platforms/cell/axon_msi.c
+++ b/arch/powerpc/platforms/cell/axon_msi.c
@@ -204,6 +204,9 @@ static int axon_msi_check_device(struct pci_dev *dev, int nvec, int type)
 	if (!find_msi_translator(dev))
 		return -ENODEV;
 
+	if (type == PCI_CAP_ID_MSI && nvec > 1)
+		return 1;
+
 	return 0;
 }
 
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
index 95633d7..1d08040 100644
--- a/arch/powerpc/platforms/powernv/pci.c
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -54,6 +54,8 @@ static int pnv_msi_check_device(struct pci_dev* pdev, int nvec, int type)
 
 	if (pdn && pdn->force_32bit_msi && !phb->msi32_support)
 		return -ENODEV;
+	if (type == PCI_CAP_ID_MSI && nvec > 1)
+		return 1;
 
 	return (phb && phb->msi_bmp.bitmap) ? 0 : -ENODEV;
 }
diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c
index 0c882e8..ad5e766 100644
--- a/arch/powerpc/platforms/pseries/msi.c
+++ b/arch/powerpc/platforms/pseries/msi.c
@@ -339,6 +339,9 @@ static int rtas_msi_check_device(struct pci_dev *pdev, int nvec, int type)
 {
 	int quota, rc;
 
+	if (type == PCI_CAP_ID_MSI && nvec > 1)
+		return 1;
+
 	if (type == PCI_CAP_ID_MSIX)
 		rc = check_req_msix(pdev, nvec);
 	else
diff --git a/arch/powerpc/platforms/wsp/msi.c b/arch/powerpc/platforms/wsp/msi.c
index 380882f..0cabd46 100644
--- a/arch/powerpc/platforms/wsp/msi.c
+++ b/arch/powerpc/platforms/wsp/msi.c
@@ -21,6 +21,13 @@
 #define MSI_ADDR_32		0xFFFF0000ul
 #define MSI_ADDR_64		0x1000000000000000ul
 
+static int wsp_msi_check_device(struct pci_dev *dev, int nvec, int type)
+{
+	if (type == PCI_CAP_ID_MSI && nvec > 1)
+		return 1;
+	return 0;
+}
+
 int wsp_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
 {
 	struct pci_controller *phb;
@@ -98,5 +105,6 @@ void wsp_setup_phb_msi(struct pci_controller *phb)
 	out_be64(phb->cfg_data + PCIE_REG_IODA_DATA0, 1ull << 63);
 
 	ppc_md.setup_msi_irqs = wsp_setup_msi_irqs;
+	ppc_md.msi_check_device = wsp_msi_check_device;
 	ppc_md.teardown_msi_irqs = wsp_teardown_msi_irqs;
 }
diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c
index 77efbae..f07840f 100644
--- a/arch/powerpc/sysdev/fsl_msi.c
+++ b/arch/powerpc/sysdev/fsl_msi.c
@@ -123,13 +123,19 @@ static void fsl_teardown_msi_irqs(struct pci_dev *pdev)
 	struct fsl_msi *msi_data;
 
 	list_for_each_entry(entry, &pdev->msi_list, list) {
+		int num;
+		int i;
+
 		if (entry->irq == NO_IRQ)
 			continue;
 		msi_data = irq_get_chip_data(entry->irq);
 		irq_set_msi_desc(entry->irq, NULL);
+		num = 1 << entry->msi_attrib.multiple;
 		msi_bitmap_free_hwirqs(&msi_data->bitmap,
-				       virq_to_hw(entry->irq), 1);
-		irq_dispose_mapping(entry->irq);
+				       virq_to_hw(entry->irq), num);
+
+		for (i = 0; i < num; i++)
+			irq_dispose_mapping(entry->irq + i);
 	}
 
 	return;
@@ -172,6 +178,7 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
 	struct msi_desc *entry;
 	struct msi_msg msg;
 	struct fsl_msi *msi_data;
+	int i;
 
 	/*
 	 * If the PCI node has an fsl,msi property, then we need to use it
@@ -207,7 +214,8 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
 			if (phandle && (phandle != msi_data->phandle))
 				continue;
 
-			hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1);
+			hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap,
+					nvec);
 			if (hwirq >= 0)
 				break;
 		}
@@ -218,17 +226,22 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
 			goto out_free;
 		}
 
-		virq = irq_create_mapping(msi_data->irqhost, hwirq);
-
+		virq = irq_create_mapping_block(msi_data->irqhost, hwirq, nvec);
 		if (virq == NO_IRQ) {
 			dev_err(&pdev->dev, "fail mapping hwirq %i\n", hwirq);
-			msi_bitmap_free_hwirqs(&msi_data->bitmap, hwirq, 1);
+			msi_bitmap_free_hwirqs(&msi_data->bitmap, hwirq, nvec);
 			rc = -ENOSPC;
 			goto out_free;
 		}
+		entry->msi_attrib.multiple = get_count_order(nvec);
 		/* chip_data is msi_data via host->hostdata in host->map() */
-		irq_set_msi_desc(virq, entry);
-
+		for (i = nvec - 1; i >= 0; i--) {
+			/*
+			 * write the virq mapping last so entry->irq will point
+			 * to first virq
+			 */
+			irq_set_msi_desc(virq + i, entry);
+		}
 		fsl_compose_msi_msg(pdev, hwirq, &msg, msi_data);
 		write_msi_msg(virq, &msg);
 	}
diff --git a/arch/powerpc/sysdev/mpic_pasemi_msi.c b/arch/powerpc/sysdev/mpic_pasemi_msi.c
index 38e6238..3d36f7e 100644
--- a/arch/powerpc/sysdev/mpic_pasemi_msi.c
+++ b/arch/powerpc/sysdev/mpic_pasemi_msi.c
@@ -67,7 +67,8 @@ static int pasemi_msi_check_device(struct pci_dev *pdev, int nvec, int type)
 {
 	if (type == PCI_CAP_ID_MSIX)
 		pr_debug("pasemi_msi: MSI-X untested, trying anyway\n");
-
+	if (type == PCI_CAP_ID_MSI && nvec > 1)
+		return 1;
 	return 0;
 }
 
diff --git a/arch/powerpc/sysdev/mpic_u3msi.c b/arch/powerpc/sysdev/mpic_u3msi.c
index 9a7aa0e..f6f86ac 100644
--- a/arch/powerpc/sysdev/mpic_u3msi.c
+++ b/arch/powerpc/sysdev/mpic_u3msi.c
@@ -109,6 +109,8 @@ static int u3msi_msi_check_device(struct pci_dev *pdev, int nvec, int type)
 {
 	if (type == PCI_CAP_ID_MSIX)
 		pr_debug("u3msi: MSI-X untested, trying anyway.\n");
+	if (type == PCI_CAP_ID_MSI && nvec > 1)
+		return 1;
 
 	/* If we can't find a magic address then MSI ain't gonna work */
 	if (find_ht_magic_addr(pdev, 0) == 0 &&
diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c
index 43948da..af1efeb 100644
--- a/arch/powerpc/sysdev/ppc4xx_msi.c
+++ b/arch/powerpc/sysdev/ppc4xx_msi.c
@@ -140,6 +140,8 @@ static int ppc4xx_msi_check_device(struct pci_dev *pdev, int nvec, int type)
 		__func__, nvec, type);
 	if (type == PCI_CAP_ID_MSIX)
 		pr_debug("ppc4xx msi: MSI-X untested, trying anyway.\n");
+	if (type == PCI_CAP_ID_MSI && nvec > 1)
+		return 1;
 
 	return 0;
 }
-- 
1.9.0.rc3

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

* Re: [PATCH 1/2] irqdomain: add support for creating a continous mapping
  2014-02-20 20:53 ` [PATCH 1/2] irqdomain: add support for creating a continous mapping Sebastian Andrzej Siewior
@ 2014-02-20 21:06   ` Scott Wood
  2014-02-21  8:04     ` Sebastian Andrzej Siewior
  2014-02-21  8:57     ` [PATCH 1/2 v2] " Sebastian Andrzej Siewior
  0 siblings, 2 replies; 11+ messages in thread
From: Scott Wood @ 2014-02-20 21:06 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior; +Cc: Thomas Gleixner, linuxppc-dev

On Thu, 2014-02-20 at 21:53 +0100, Sebastian Andrzej Siewior wrote:
> A MSI device may have multiple interrupts. That means that the
> interrupts numbers should be continuos so that pdev->irq refers to the
> first interrupt, pdev->irq + 1 to the second and so on.
> This patch adds support for continuous allocation of virqs for a range
> of hwirqs. The function is based on irq_create_mapping() but due to the
> number argument there is very little in common now.

Would it make sense to turn irq_create_mapping() into a call to
irq_create_mapping_block() with num = 1?

-Scott

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

* Re: [PATCH 1/2] irqdomain: add support for creating a continous mapping
  2014-02-20 21:06   ` Scott Wood
@ 2014-02-21  8:04     ` Sebastian Andrzej Siewior
  2014-02-21  8:57     ` [PATCH 1/2 v2] " Sebastian Andrzej Siewior
  1 sibling, 0 replies; 11+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-02-21  8:04 UTC (permalink / raw)
  To: Scott Wood; +Cc: Thomas Gleixner, linuxppc-dev

On 02/20/2014 10:06 PM, Scott Wood wrote:
> On Thu, 2014-02-20 at 21:53 +0100, Sebastian Andrzej Siewior wrote:
>> A MSI device may have multiple interrupts. That means that the
>> interrupts numbers should be continuos so that pdev->irq refers to the
>> first interrupt, pdev->irq + 1 to the second and so on.
>> This patch adds support for continuous allocation of virqs for a range
>> of hwirqs. The function is based on irq_create_mapping() but due to the
>> number argument there is very little in common now.
> 
> Would it make sense to turn irq_create_mapping() into a call to
> irq_create_mapping_block() with num = 1?

There are few things different and I didn't like it. Now I that I look
at it again I've found a bug the way irq_find_mapping() is called.
Let me redo it with your suggestion and we will see if it makes sense.

> -Scott
> 
> 
Sebastian

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

* [PATCH 1/2 v2] irqdomain: add support for creating a continous mapping
  2014-02-20 21:06   ` Scott Wood
  2014-02-21  8:04     ` Sebastian Andrzej Siewior
@ 2014-02-21  8:57     ` Sebastian Andrzej Siewior
  2014-03-14 11:18       ` Thomas Gleixner
  1 sibling, 1 reply; 11+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-02-21  8:57 UTC (permalink / raw)
  To: Scott Wood; +Cc: Thomas Gleixner, linuxppc-dev

A MSI device may have multiple interrupts. That means that the
interrupts numbers should be continuos so that pdev->irq refers to the
first interrupt, pdev->irq + 1 to the second and so on.
This patch adds support for continuous allocation of virqs for a range
of hwirqs. The function is based on irq_create_mapping() but due to the
number argument there is very little in common now.

Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
Scott, this is what you suggested. I must admit, it does not look that
bad. It is just compile tested.

v1.v2:
    - use irq_create_mapping_block() for irq_create_mapping()

 include/linux/irqdomain.h | 10 ++++--
 kernel/irq/irqdomain.c    | 87 ++++++++++++++++++++++++++++++++++---------=
----
 2 files changed, 72 insertions(+), 25 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index c983ed1..8b09a6b 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -171,12 +171,18 @@ extern int irq_domain_associate(struct irq_domain *do=
main, unsigned int irq,
 					irq_hw_number_t hwirq);
 extern void irq_domain_associate_many(struct irq_domain *domain,
 				      unsigned int irq_base,
 				      irq_hw_number_t hwirq_base, int count);
=20
-extern unsigned int irq_create_mapping(struct irq_domain *host,
-				       irq_hw_number_t hwirq);
+extern unsigned int irq_create_mapping_block(struct irq_domain *host,
+		irq_hw_number_t hwirq, unsigned int num);
+static inline unsigned int irq_create_mapping(struct irq_domain *host,
+		irq_hw_number_t hwirq)
+{
+	return irq_create_mapping_block(host, hwirq, 1);
+}
+
 extern void irq_dispose_mapping(unsigned int virq);
=20
 /**
  * irq_linear_revmap() - Find a linux irq from a hw irq number.
  * @domain: domain owning this hardware interrupt
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index cf68bb3..cdc6627 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -372,67 +372,108 @@ unsigned int irq_create_direct_mapping(struct irq_do=
main *domain)
=20
 	return virq;
 }
 EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
=20
+static int irq_check_continuous_mapping(struct irq_domain *domain,
+		irq_hw_number_t hwirq, unsigned int num)
+{
+	int virq;
+	int i;
+
+	virq =3D irq_find_mapping(domain, hwirq);
+
+	for (i =3D 1; i < num; i++) {
+		unsigned int next;
+
+		next =3D irq_find_mapping(domain, hwirq + i);
+		if (next =3D=3D virq + i)
+			continue;
+
+		pr_err("irq: invalid partial mapping. First hwirq %lu maps to "
+				"%d and \n", hwirq, virq);
+		pr_err("irq: +%d hwirq (%lu) maps to %d but should be %d.\n",
+				i, hwirq + i, next, virq + i);
+		return -EINVAL;
+	}
+
+	pr_debug("-> existing mapping on virq %d\n", virq);
+	return virq;
+}
+
 /**
- * irq_create_mapping() - Map a hardware interrupt into linux irq space
+ * irq_create_mapping_block() - Map multiple hardware interrupts
  * @domain: domain owning this hardware interrupt or NULL for default doma=
in
  * @hwirq: hardware irq number in that domain space
+ * @num: number of interrupts
+ *
+ * Maps a hwirq to a newly allocated virq. If num is greater than 1 then n=
um
+ * hwirqs (hwirq =E2=80=A6 hwirq + num - 1) will be mapped and virq will b=
e  continuous.
+ * Returns the first linux virq number.
  *
- * Only one mapping per hardware interrupt is permitted. Returns a linux
- * irq number.
  * If the sense/trigger is to be specified, set_irq_type() should be called
  * on the number returned from that call.
  */
-unsigned int irq_create_mapping(struct irq_domain *domain,
-				irq_hw_number_t hwirq)
+unsigned int irq_create_mapping_block(struct irq_domain *domain,
+		irq_hw_number_t hwirq, unsigned int num)
 {
-	unsigned int hint;
 	int virq;
+	int i;
+	int node;
+	unsigned int hint;
=20
-	pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
+	pr_debug("%s(0x%p, 0x%lx, %d)\n", __func__, domain, hwirq, num);
=20
 	/* Look for default domain if nececssary */
-	if (domain =3D=3D NULL)
+	if (!domain && num =3D=3D 1)
 		domain =3D irq_default_domain;
+
 	if (domain =3D=3D NULL) {
 		WARN(1, "%s(, %lx) called with NULL domain\n", __func__, hwirq);
 		return 0;
 	}
 	pr_debug("-> using domain @%p\n", domain);
=20
 	/* Check if mapping already exists */
-	virq =3D irq_find_mapping(domain, hwirq);
-	if (virq) {
-		pr_debug("-> existing mapping on virq %d\n", virq);
-		return virq;
+	for (i =3D 0; i < num; i++) {
+		virq =3D irq_find_mapping(domain, hwirq + i);
+		if (virq !=3D NO_IRQ) {
+			if (i =3D=3D 0)
+				return irq_check_continuous_mapping(domain,
+						hwirq, num);
+			pr_err("irq: hwirq %ld has no mapping but hwirq %ld "
+				"maps to virq %d. This can't be a block\n",
+				hwirq, hwirq + i, virq);
+			return -EINVAL;
+		}
 	}
=20
+	node =3D of_node_to_nid(domain->of_node);
 	/* Allocate a virtual interrupt number */
 	hint =3D hwirq % nr_irqs;
 	if (hint =3D=3D 0)
 		hint++;
-	virq =3D irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node));
-	if (virq <=3D 0)
-		virq =3D irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
+	virq =3D irq_alloc_descs_from(hint, num, node);
+	if (virq <=3D 0 && hint !=3D 1)
+		virq =3D irq_alloc_descs_from(1, num, node);
 	if (virq <=3D 0) {
 		pr_debug("-> virq allocation failed\n");
 		return 0;
 	}
=20
-	if (irq_domain_associate(domain, virq, hwirq)) {
-		irq_free_desc(virq);
-		return 0;
+	irq_domain_associate_many(domain, virq, hwirq, num);
+	if (num =3D=3D 1) {
+		pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
+			hwirq, of_node_full_name(domain->of_node), virq);
+		return virq;
 	}
-
-	pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
-		hwirq, of_node_full_name(domain->of_node), virq);
-
+	pr_debug("irqs %lu=E2=80=A6%lu on domain %s mapped to virtual irqs %u=E2=
=80=A6%u\n",
+		hwirq, hwirq + num - 1, of_node_full_name(domain->of_node),
+			virq, virq + num - 1);
 	return virq;
 }
-EXPORT_SYMBOL_GPL(irq_create_mapping);
+EXPORT_SYMBOL_GPL(irq_create_mapping_block);
=20
 /**
  * irq_create_strict_mappings() - Map a range of hw irqs to fixed linux ir=
qs
  * @domain: domain owning the interrupt range
  * @irq_base: beginning of linux IRQ range
--=20
1.9.0.rc3

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

* Re: [PATCH 1/2 v2] irqdomain: add support for creating a continous mapping
  2014-02-21  8:57     ` [PATCH 1/2 v2] " Sebastian Andrzej Siewior
@ 2014-03-14 11:18       ` Thomas Gleixner
  0 siblings, 0 replies; 11+ messages in thread
From: Thomas Gleixner @ 2014-03-14 11:18 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior; +Cc: Scott Wood, linuxppc-dev

[-- Attachment #1: Type: TEXT/PLAIN, Size: 4896 bytes --]

On Fri, 21 Feb 2014, Sebastian Andrzej Siewior wrote:

> A MSI device may have multiple interrupts. That means that the
> interrupts numbers should be continuos so that pdev->irq refers to the
> first interrupt, pdev->irq + 1 to the second and so on.
> This patch adds support for continuous allocation of virqs for a range
> of hwirqs. The function is based on irq_create_mapping() but due to the
> number argument there is very little in common now.
> 
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> ---
> Scott, this is what you suggested. I must admit, it does not look that
> bad. It is just compile tested.

Is it tested for real as well?
 
> +static int irq_check_continuous_mapping(struct irq_domain *domain,
> +		irq_hw_number_t hwirq, unsigned int num)
> +{
> +	int virq;
> +	int i;
> +
> +	virq = irq_find_mapping(domain, hwirq);
> +
> +	for (i = 1; i < num; i++) {
> +		unsigned int next;
> +
> +		next = irq_find_mapping(domain, hwirq + i);
> +		if (next == virq + i)
> +			continue;
> +
> +		pr_err("irq: invalid partial mapping. First hwirq %lu maps to "
> +				"%d and \n", hwirq, virq);
> +		pr_err("irq: +%d hwirq (%lu) maps to %d but should be %d.\n",
> +				i, hwirq + i, next, virq + i);
> +		return -EINVAL;
> +	}
> +
> +	pr_debug("-> existing mapping on virq %d\n", virq);
> +	return virq;
> +}
> +
>  /**
> - * irq_create_mapping() - Map a hardware interrupt into linux irq space
> + * irq_create_mapping_block() - Map multiple hardware interrupts
>   * @domain: domain owning this hardware interrupt or NULL for default domain
>   * @hwirq: hardware irq number in that domain space
> + * @num: number of interrupts
> + *
> + * Maps a hwirq to a newly allocated virq. If num is greater than 1 then num
> + * hwirqs (hwirq … hwirq + num - 1) will be mapped and virq will be  continuous.
> + * Returns the first linux virq number.
>   *
> - * Only one mapping per hardware interrupt is permitted. Returns a linux
> - * irq number.
>   * If the sense/trigger is to be specified, set_irq_type() should be called
>   * on the number returned from that call.
>   */
> -unsigned int irq_create_mapping(struct irq_domain *domain,
> -				irq_hw_number_t hwirq)
> +unsigned int irq_create_mapping_block(struct irq_domain *domain,
> +		irq_hw_number_t hwirq, unsigned int num)
>  {
> -	unsigned int hint;
>  	int virq;
> +	int i;
> +	int node;
> +	unsigned int hint;

What's wrong with

	unsigned int hint;
  	int virq, i, node;

?
  
> -	pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
> +	pr_debug("%s(0x%p, 0x%lx, %d)\n", __func__, domain, hwirq, num);
>  
>  	/* Look for default domain if nececssary */
> -	if (domain == NULL)
> +	if (!domain && num == 1)
>  		domain = irq_default_domain;
> +
>  	if (domain == NULL) {
>  		WARN(1, "%s(, %lx) called with NULL domain\n", __func__, hwirq);
>  		return 0;
>  	}
>  	pr_debug("-> using domain @%p\n", domain);
>  
>  	/* Check if mapping already exists */
> -	virq = irq_find_mapping(domain, hwirq);
> -	if (virq) {
> -		pr_debug("-> existing mapping on virq %d\n", virq);
> -		return virq;
> +	for (i = 0; i < num; i++) {
> +		virq = irq_find_mapping(domain, hwirq + i);
> +		if (virq != NO_IRQ) {
> +			if (i == 0)
> +				return irq_check_continuous_mapping(domain,
> +						hwirq, num);

So what is the loop for? If i == 0 and virq != NO_IRQ you return. That
does not make sense at all.
  
> +			pr_err("irq: hwirq %ld has no mapping but hwirq %ld "
> +				"maps to virq %d. This can't be a block\n",
> +				hwirq, hwirq + i, virq);
> +			return -EINVAL;
> +		}
>  	}
>  
> +	node = of_node_to_nid(domain->of_node);
>  	/* Allocate a virtual interrupt number */
>  	hint = hwirq % nr_irqs;
>  	if (hint == 0)
>  		hint++;
> -	virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node));
> -	if (virq <= 0)
> -		virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
> +	virq = irq_alloc_descs_from(hint, num, node);
> +	if (virq <= 0 && hint != 1)
> +		virq = irq_alloc_descs_from(1, num, node);
>  	if (virq <= 0) {
>  		pr_debug("-> virq allocation failed\n");
>  		return 0;
>  	}
>  
> -	if (irq_domain_associate(domain, virq, hwirq)) {
> -		irq_free_desc(virq);
> -		return 0;
> +	irq_domain_associate_many(domain, virq, hwirq, num);

So irq_domain_associate can fail, but irq_domain_associate_many cannot ?

> +	if (num == 1) {
> +		pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
> +			hwirq, of_node_full_name(domain->of_node), virq);
> +		return virq;
>  	}
> -
> -	pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
> -		hwirq, of_node_full_name(domain->of_node), virq);
> -
> +	pr_debug("irqs %lu…%lu on domain %s mapped to virtual irqs %u…%u\n",
> +		hwirq, hwirq + num - 1, of_node_full_name(domain->of_node),
> +			virq, virq + num - 1);

A single pr_debug is sufficient, hmm?

Thanks,

	tglx

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

* Re: [PATCH 1/2] irqdomain: add support for creating a continous mapping
  2014-11-03 22:18   ` Scott Wood
  2014-11-04  8:35     ` Sebastian Andrzej Siewior
@ 2014-11-05 16:55     ` Johannes Thumshirn
  1 sibling, 0 replies; 11+ messages in thread
From: Johannes Thumshirn @ 2014-11-05 16:55 UTC (permalink / raw)
  To: Scott Wood; +Cc: Johannes Thumshirn, Sebastian Andrzej Siewior, linuxppc-dev

On Mon, Nov 03, 2014 at 04:18:51PM -0600, Scott Wood wrote:
> On Mon, 2014-11-03 at 17:18 +0100, Johannes Thumshirn wrote:
> > A MSI device may have multiple interrupts. That means that the
> > interrupts numbers should be continuos so that pdev->irq refers to the
> > first interrupt, pdev->irq + 1 to the second and so on.
> > This patch adds support for continuous allocation of virqs for a range
> > of hwirqs. The function is based on irq_create_mapping() but due to the
> > number argument there is very little in common now.
> >
> > Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> > Signed-off-by: Johannes Thumshirn <johannes.thumshirn@men.de>
> > ---
> >  include/linux/irqdomain.h | 10 ++++--
> >  kernel/irq/irqdomain.c    | 85 ++++++++++++++++++++++++++++++++++++-----------
> >  2 files changed, 73 insertions(+), 22 deletions(-)
>
> This is changing core kernel code and thus LKML should be CCed, as well
> as Ben Herrenschmidt who is the maintainer of kernel/irq/irqdomain.c.
>
> Also please respond to feedback in
> http://patchwork.ozlabs.org/patch/322497/
>
> Is it really necessary for the virqs to be contiguous?  How is the
> availability of multiple MSIs communicated to the driver?  Is there an
> example of a driver that currently uses multiple MSIs?
>
> >  /**
> > - * irq_create_mapping() - Map a hardware interrupt into linux irq space
> > + * irq_create_mapping_block() - Map multiple hardware interrupts
> >   * @domain: domain owning this hardware interrupt or NULL for default domain
> >   * @hwirq: hardware irq number in that domain space
> > + * @num: number of interrupts
> > + *
> > + * Maps a hwirq to a newly allocated virq. If num is greater than 1 then num
> > + * hwirqs (hwirq ... hwirq + num - 1) will be mapped and virq will be
> > + * continuous.
> > + * Returns the first linux virq number.
> >   *
> > - * Only one mapping per hardware interrupt is permitted. Returns a linux
> > - * irq number.
> >   * If the sense/trigger is to be specified, set_irq_type() should be called
> >   * on the number returned from that call.
> >   */
> > -unsigned int irq_create_mapping(struct irq_domain *domain,
> > +unsigned int irq_create_mapping_block(struct irq_domain *domain,
> >  				irq_hw_number_t hwirq)
> >  {
>
> Where is the num parameter?  How does this even build?

F**k this was an error from porting the patch from 3.4.x to 3.18.

>
> >  	unsigned int hint;
> >  	int virq;
> > +	int node;
> > +	int i;
> >
> > -	pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
> > +	pr_debug("%s(0x%p, 0x%lx, %d)\n", __func__, domain, hwirq, num);
> >
> >  	/* Look for default domain if nececssary */
> > -	if (domain == NULL)
> > +	if (!domain && num == 1)
> >  		domain = irq_default_domain;
> > +
> >  	if (domain == NULL) {
> >  		WARN(1, "%s(, %lx) called with NULL domain\n", __func__, hwirq);
> >  		return 0;
> > @@ -403,35 +437,46 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
> >  	pr_debug("-> using domain @%p\n", domain);
> >
> >  	/* Check if mapping already exists */
> > -	virq = irq_find_mapping(domain, hwirq);
> > -	if (virq) {
> > -		pr_debug("-> existing mapping on virq %d\n", virq);
> > -		return virq;
> > +	for (i = 0; i < num; i++) {
> > +		virq = irq_find_mapping(domain, hwirq + i);
> > +		if (virq != NO_IRQ) {
>
> Please don't introduce new uses of NO_IRQ.  irq_find_mapping() returns
> zero on failure.  Some architectures (e.g. ARM) define NO_IRQ as
> something other than zero, which will cause this to break.

OK

>
> > +			if (i == 0)
> > +				return irq_check_continuous_mapping(domain,
> > +								hwirq, num);
> > +			pr_err("irq: hwirq %ld has no mapping but hwirq %ld "
> > +				"maps to virq %d. This can't be a block\n",
> > +				hwirq, hwirq + i, virq);
> > +			return -EINVAL;
> > +		}
> >  	}
>
> Explain how you'd get into this error state, and how you'd avoid doing
> so.

According to Thomas Gleixner (http://patchwork.ozlabs.org/patch/322497/) the
whole loop is nonsense, so I'll have to rework it anyways.

>
> > +	node = of_node_to_nid(domain->of_node);
> > +
> >  	/* Allocate a virtual interrupt number */
> >  	hint = hwirq % nr_irqs;
> >  	if (hint == 0)
> >  		hint++;
> > -	virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node));
> > -	if (virq <= 0)
> > -		virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
> > +	virq = irq_alloc_desc_from(hint, node);
> > +	if (virq <= 0 && hint != 1)
> > +		virq = irq_alloc_desc_from(1, node);
>
> Factoring out node seems irrelevant to, and obscures, what you're doing
> which is adding a chcek for hint.  Why is a hint value of 1 special?
>

OK

> You're also still allocating only one virq, unlike in
> http://patchwork.ozlabs.org/patch/322497/
>
> -Scott
>
>

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

* Re: [PATCH 1/2] irqdomain: add support for creating a continous mapping
  2014-11-03 22:18   ` Scott Wood
@ 2014-11-04  8:35     ` Sebastian Andrzej Siewior
  2014-11-05 16:55     ` Johannes Thumshirn
  1 sibling, 0 replies; 11+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-11-04  8:35 UTC (permalink / raw)
  To: Scott Wood, Johannes Thumshirn; +Cc: linuxppc-dev

On 11/03/2014 11:18 PM, Scott Wood wrote:
> Is it really necessary for the virqs to be contiguous?  How is the
> availability of multiple MSIs communicated to the driver?  Is there an
> example of a driver that currently uses multiple MSIs?

I used this in PCI and after enabling 2^x, I allocated an continuous
block starting at virq. There is no way of communicating this in any
other way. The first irq number is saved in pci_dev->irq and the
remaining have to follow.

Take a look at  drivers/ata/ahci.c and you will see:
- pci_msi_vec_count() for number of irqs
- pci_enable_msi_block() to allocate the number of irqs
- pci_enable_msi() (no X)
- ahci_host_activate() does request_threaded_irq() for pdev->irq and
  loops "number of ports" times which matches number of number of irqs
  (or is less then).

Sebastian

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

* Re: [PATCH 1/2] irqdomain: add support for creating a continous mapping
  2014-11-03 16:18 ` [PATCH 1/2] irqdomain: add support for creating a continous mapping Johannes Thumshirn
@ 2014-11-03 22:18   ` Scott Wood
  2014-11-04  8:35     ` Sebastian Andrzej Siewior
  2014-11-05 16:55     ` Johannes Thumshirn
  0 siblings, 2 replies; 11+ messages in thread
From: Scott Wood @ 2014-11-03 22:18 UTC (permalink / raw)
  To: Johannes Thumshirn; +Cc: Sebastian Andrzej Siewior, linuxppc-dev

On Mon, 2014-11-03 at 17:18 +0100, Johannes Thumshirn wrote:
> A MSI device may have multiple interrupts. That means that the
> interrupts numbers should be continuos so that pdev->irq refers to the
> first interrupt, pdev->irq + 1 to the second and so on.
> This patch adds support for continuous allocation of virqs for a range
> of hwirqs. The function is based on irq_create_mapping() but due to the
> number argument there is very little in common now.
> 
> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> Signed-off-by: Johannes Thumshirn <johannes.thumshirn@men.de>
> ---
>  include/linux/irqdomain.h | 10 ++++--
>  kernel/irq/irqdomain.c    | 85 ++++++++++++++++++++++++++++++++++++-----------
>  2 files changed, 73 insertions(+), 22 deletions(-)

This is changing core kernel code and thus LKML should be CCed, as well
as Ben Herrenschmidt who is the maintainer of kernel/irq/irqdomain.c.

Also please respond to feedback in
http://patchwork.ozlabs.org/patch/322497/

Is it really necessary for the virqs to be contiguous?  How is the
availability of multiple MSIs communicated to the driver?  Is there an
example of a driver that currently uses multiple MSIs?

>  /**
> - * irq_create_mapping() - Map a hardware interrupt into linux irq space
> + * irq_create_mapping_block() - Map multiple hardware interrupts
>   * @domain: domain owning this hardware interrupt or NULL for default domain
>   * @hwirq: hardware irq number in that domain space
> + * @num: number of interrupts
> + *
> + * Maps a hwirq to a newly allocated virq. If num is greater than 1 then num
> + * hwirqs (hwirq ... hwirq + num - 1) will be mapped and virq will be
> + * continuous.
> + * Returns the first linux virq number.
>   *
> - * Only one mapping per hardware interrupt is permitted. Returns a linux
> - * irq number.
>   * If the sense/trigger is to be specified, set_irq_type() should be called
>   * on the number returned from that call.
>   */
> -unsigned int irq_create_mapping(struct irq_domain *domain,
> +unsigned int irq_create_mapping_block(struct irq_domain *domain,
>  				irq_hw_number_t hwirq)
>  {

Where is the num parameter?  How does this even build?

>  	unsigned int hint;
>  	int virq;
> +	int node;
> +	int i;
>  
> -	pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
> +	pr_debug("%s(0x%p, 0x%lx, %d)\n", __func__, domain, hwirq, num);
>  
>  	/* Look for default domain if nececssary */
> -	if (domain == NULL)
> +	if (!domain && num == 1)
>  		domain = irq_default_domain;
> +
>  	if (domain == NULL) {
>  		WARN(1, "%s(, %lx) called with NULL domain\n", __func__, hwirq);
>  		return 0;
> @@ -403,35 +437,46 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
>  	pr_debug("-> using domain @%p\n", domain);
>  
>  	/* Check if mapping already exists */
> -	virq = irq_find_mapping(domain, hwirq);
> -	if (virq) {
> -		pr_debug("-> existing mapping on virq %d\n", virq);
> -		return virq;
> +	for (i = 0; i < num; i++) {
> +		virq = irq_find_mapping(domain, hwirq + i);
> +		if (virq != NO_IRQ) {

Please don't introduce new uses of NO_IRQ.  irq_find_mapping() returns
zero on failure.  Some architectures (e.g. ARM) define NO_IRQ as
something other than zero, which will cause this to break.

> +			if (i == 0)
> +				return irq_check_continuous_mapping(domain,
> +								hwirq, num);
> +			pr_err("irq: hwirq %ld has no mapping but hwirq %ld "
> +				"maps to virq %d. This can't be a block\n",
> +				hwirq, hwirq + i, virq);
> +			return -EINVAL;
> +		}
>  	}

Explain how you'd get into this error state, and how you'd avoid doing
so.

> +	node = of_node_to_nid(domain->of_node);
> +
>  	/* Allocate a virtual interrupt number */
>  	hint = hwirq % nr_irqs;
>  	if (hint == 0)
>  		hint++;
> -	virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node));
> -	if (virq <= 0)
> -		virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
> +	virq = irq_alloc_desc_from(hint, node);
> +	if (virq <= 0 && hint != 1)
> +		virq = irq_alloc_desc_from(1, node);

Factoring out node seems irrelevant to, and obscures, what you're doing
which is adding a chcek for hint.  Why is a hint value of 1 special?

You're also still allocating only one virq, unlike in
http://patchwork.ozlabs.org/patch/322497/

-Scott

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

* [PATCH 1/2] irqdomain: add support for creating a continous mapping
  2014-11-03 16:18 [PATCH 0/2] Support multiple MSI interrupts on FSL-MPIC Johannes Thumshirn
@ 2014-11-03 16:18 ` Johannes Thumshirn
  2014-11-03 22:18   ` Scott Wood
  0 siblings, 1 reply; 11+ messages in thread
From: Johannes Thumshirn @ 2014-11-03 16:18 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior, Scott Wood, Michael Ellerman
  Cc: Johannes Thumshirn, linuxppc-dev

A MSI device may have multiple interrupts. That means that the
interrupts numbers should be continuos so that pdev->irq refers to the
first interrupt, pdev->irq + 1 to the second and so on.
This patch adds support for continuous allocation of virqs for a range
of hwirqs. The function is based on irq_create_mapping() but due to the
number argument there is very little in common now.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Johannes Thumshirn <johannes.thumshirn@men.de>
---
 include/linux/irqdomain.h | 10 ++++--
 kernel/irq/irqdomain.c    | 85 ++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 73 insertions(+), 22 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index b0f9d16..75662f3 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -175,8 +175,14 @@ extern void irq_domain_associate_many(struct irq_domain *domain,
 extern void irq_domain_disassociate(struct irq_domain *domain,
 				    unsigned int irq);
 
-extern unsigned int irq_create_mapping(struct irq_domain *host,
-				       irq_hw_number_t hwirq);
+extern unsigned int irq_create_mapping_block(struct irq_domain *host,
+				irq_hw_number_t hwirq, unsigned int num);
+static inline unsigned int irq_create_mapping(struct irq_domain *host,
+					irq_hw_number_t hwirq)
+{
+	return irq_create_mapping_block(host, hwirq, 1);
+}
+
 extern void irq_dispose_mapping(unsigned int virq);
 
 /**
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 6534ff6..fba488f 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -375,27 +375,61 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain)
 }
 EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
 
+static int irq_check_continuous_mapping(struct irq_domain *domain,
+					irq_hw_number_t hwirq, unsigned int num)
+{
+	int virq;
+	int i;
+
+	virq = irq_find_mapping(domain, hwirq);
+
+	for (i = 1; i < num; i++) {
+		unsigned int next;
+
+		next = irq_find_mapping(domain, hwirq + i);
+		if (next == virq + i)
+			continue;
+
+		pr_err("irq: invalid partial mapping. First hwirq %lu maps to "
+			"%d and\n", hwirq, virq);
+		pr_err("irq: +%d hwirq (%lu) maps to %d but should be %d.\n",
+			i, hwirq + i, next, virq + i);
+		return -EINVAL;
+	}
+
+	pr_debug("-> existing mapping on virq %d\n", virq);
+	return virq;
+}
+
+
 /**
- * irq_create_mapping() - Map a hardware interrupt into linux irq space
+ * irq_create_mapping_block() - Map multiple hardware interrupts
  * @domain: domain owning this hardware interrupt or NULL for default domain
  * @hwirq: hardware irq number in that domain space
+ * @num: number of interrupts
+ *
+ * Maps a hwirq to a newly allocated virq. If num is greater than 1 then num
+ * hwirqs (hwirq ... hwirq + num - 1) will be mapped and virq will be
+ * continuous.
+ * Returns the first linux virq number.
  *
- * Only one mapping per hardware interrupt is permitted. Returns a linux
- * irq number.
  * If the sense/trigger is to be specified, set_irq_type() should be called
  * on the number returned from that call.
  */
-unsigned int irq_create_mapping(struct irq_domain *domain,
+unsigned int irq_create_mapping_block(struct irq_domain *domain,
 				irq_hw_number_t hwirq)
 {
 	unsigned int hint;
 	int virq;
+	int node;
+	int i;
 
-	pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
+	pr_debug("%s(0x%p, 0x%lx, %d)\n", __func__, domain, hwirq, num);
 
 	/* Look for default domain if nececssary */
-	if (domain == NULL)
+	if (!domain && num == 1)
 		domain = irq_default_domain;
+
 	if (domain == NULL) {
 		WARN(1, "%s(, %lx) called with NULL domain\n", __func__, hwirq);
 		return 0;
@@ -403,35 +437,46 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
 	pr_debug("-> using domain @%p\n", domain);
 
 	/* Check if mapping already exists */
-	virq = irq_find_mapping(domain, hwirq);
-	if (virq) {
-		pr_debug("-> existing mapping on virq %d\n", virq);
-		return virq;
+	for (i = 0; i < num; i++) {
+		virq = irq_find_mapping(domain, hwirq + i);
+		if (virq != NO_IRQ) {
+			if (i == 0)
+				return irq_check_continuous_mapping(domain,
+								hwirq, num);
+			pr_err("irq: hwirq %ld has no mapping but hwirq %ld "
+				"maps to virq %d. This can't be a block\n",
+				hwirq, hwirq + i, virq);
+			return -EINVAL;
+		}
 	}
 
+	node = of_node_to_nid(domain->of_node);
+
 	/* Allocate a virtual interrupt number */
 	hint = hwirq % nr_irqs;
 	if (hint == 0)
 		hint++;
-	virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node));
-	if (virq <= 0)
-		virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
+	virq = irq_alloc_desc_from(hint, node);
+	if (virq <= 0 && hint != 1)
+		virq = irq_alloc_desc_from(1, node);
 	if (virq <= 0) {
 		pr_debug("-> virq allocation failed\n");
 		return 0;
 	}
 
-	if (irq_domain_associate(domain, virq, hwirq)) {
-		irq_free_desc(virq);
-		return 0;
+	irq_domain_associate_many(domain, virq, hwirq, num);
+	if (num == 1) {
+		pr_debug("irq %lu on domain %s mapped to virtual irq %u\n"
+			 hwirq, of_node_full_name(domain->of_node), virq);
+		return virq;
 	}
 
-	pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
-		hwirq, of_node_full_name(domain->of_node), virq);
-
+	pr_debug("irqs %lu...%lu on domain %s mapped to virtual irqs %u..%u\n",
+		 hwirq, hwirq + num - 1, of_node_full_name(domain->of_node),
+		 virq, virq + num - 1);
 	return virq;
 }
-EXPORT_SYMBOL_GPL(irq_create_mapping);
+EXPORT_SYMBOL_GPL(irq_create_mapping_block);
 
 /**
  * irq_create_strict_mappings() - Map a range of hw irqs to fixed linux irqs
-- 
1.9.1

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

end of thread, other threads:[~2014-11-05 16:56 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-02-20 20:53 multiple MSI interrupts on FSL-MPIC Sebastian Andrzej Siewior
2014-02-20 20:53 ` [PATCH 1/2] irqdomain: add support for creating a continous mapping Sebastian Andrzej Siewior
2014-02-20 21:06   ` Scott Wood
2014-02-21  8:04     ` Sebastian Andrzej Siewior
2014-02-21  8:57     ` [PATCH 1/2 v2] " Sebastian Andrzej Siewior
2014-03-14 11:18       ` Thomas Gleixner
2014-02-20 20:53 ` [PATCH 2/2] powerpc: msi: fsl: add support for multiple MSI interrupts Sebastian Andrzej Siewior
2014-11-03 16:18 [PATCH 0/2] Support multiple MSI interrupts on FSL-MPIC Johannes Thumshirn
2014-11-03 16:18 ` [PATCH 1/2] irqdomain: add support for creating a continous mapping Johannes Thumshirn
2014-11-03 22:18   ` Scott Wood
2014-11-04  8:35     ` Sebastian Andrzej Siewior
2014-11-05 16:55     ` Johannes Thumshirn

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