[RFC 3/5] powerpc/mpic: Add support for non-contiguous irq ranges

Bharat Bhushan Bharat.Bhushan at nxp.com
Fri Jul 27 19:47:59 AEST 2018


Freescale MPIC h/w may not support all interrupt sources reported
by hardware, "last-interrupt-source" or platform. On these platforms
a misconfigured device tree that assigns one of the reserved
interrupts leaves a non-functioning system without warning.

This patch adds "supported-irq-ranges" property in device tree to
provide the range of supported source of interrupts. If a reserved
interrupt used then it will not be programming h/w, which it does
currently, and through warning.

Signed-off-by: Bharat Bhushan <Bharat.Bhushan at nxp.com>
---
 .../devicetree/bindings/powerpc/fsl/mpic.txt       |   8 ++
 arch/powerpc/include/asm/mpic.h                    |   9 ++
 arch/powerpc/sysdev/mpic.c                         | 113 +++++++++++++++++++--
 3 files changed, 121 insertions(+), 9 deletions(-)

diff --git a/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt b/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt
index dc57446..bd6da54 100644
--- a/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt
+++ b/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt
@@ -77,6 +77,14 @@ PROPERTIES
           in the global feature registers.  If specified, this field will
           override the value read from MPIC_GREG_FEATURE_LAST_SRC.
 
+ - supported-irq-ranges
+      Usage: optional
+      Value type: <prop-encoded-array>
+      Definition: This encodes arbitrary number of start-irq and end-irq
+          pairs, both including. Interrupt source supported by an MPIC
+          may not be contigous, in that case this property will be used
+          to pass supported source of interrupt ranges.
+
 INTERRUPT SPECIFIER DEFINITION
 
   Interrupt specifiers consists of 4 cells encoded as
diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h
index fad8ddd..4080c98 100644
--- a/arch/powerpc/include/asm/mpic.h
+++ b/arch/powerpc/include/asm/mpic.h
@@ -252,6 +252,11 @@ struct mpic_irq_save {
 #endif
 };
 
+struct mpic_irq_range {
+	u32	start_irq;
+	u32	end_irq;
+};
+
 /* The instance data of a given MPIC */
 struct mpic
 {
@@ -281,6 +286,10 @@ struct mpic
 	/* Number of sources */
 	unsigned int		num_sources;
 
+	/* Supported source ranges */
+	unsigned int num_ranges;
+	struct mpic_irq_range	*irq_ranges;
+
 	/* vector numbers used for internal sources (ipi/timers) */
 	unsigned int		ipi_vecs[4];
 	unsigned int		timer_vecs[8];
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index d503887..cbf3a51 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -155,6 +155,23 @@ struct bus_type mpic_subsys = {
 
 #endif /* CONFIG_MPIC_WEIRD */
 
+static int mpic_irq_source_invalid(struct mpic *mpic, unsigned int irq)
+{
+	int i;
+
+	for (i = 0; i < mpic->num_ranges; i++) {
+		if ((irq >= mpic->irq_ranges[i].start_irq) &&
+			(irq <= mpic->irq_ranges[i].end_irq))
+			return 0;
+	}
+
+	/* if not supported irq-ranges then check for num_sources */
+	if (!mpic->num_ranges && irq < mpic->num_sources)
+		return 0;
+
+	return -EINVAL;
+}
+
 static inline unsigned int mpic_processor_id(struct mpic *mpic)
 {
 	unsigned int cpu = 0;
@@ -873,8 +890,10 @@ int mpic_set_irq_type(struct irq_data *d, unsigned int flow_type)
 	DBG("mpic: set_irq_type(mpic:@%p,virq:%d,src:0x%x,type:0x%x)\n",
 	    mpic, d->irq, src, flow_type);
 
-	if (src >= mpic->num_sources)
+	if (mpic_irq_source_invalid(mpic, src)) {
+		WARN(1, "mpic: Reserved IRQ source %d\n", src);
 		return -EINVAL;
+	}
 
 	vold = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI));
 
@@ -933,8 +952,10 @@ void mpic_set_vector(unsigned int virq, unsigned int vector)
 	DBG("mpic: set_vector(mpic:@%p,virq:%d,src:%d,vector:0x%x)\n",
 	    mpic, virq, src, vector);
 
-	if (src >= mpic->num_sources)
+	if (mpic_irq_source_invalid(mpic, src)) {
+		WARN(1, "mpic: Reserved IRQ source %d\n", src);
 		return;
+	}
 
 	vecpri = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI));
 	vecpri = vecpri & ~MPIC_INFO(VECPRI_VECTOR_MASK);
@@ -950,8 +971,10 @@ static void mpic_set_destination(unsigned int virq, unsigned int cpuid)
 	DBG("mpic: set_destination(mpic:@%p,virq:%d,src:%d,cpuid:0x%x)\n",
 	    mpic, virq, src, cpuid);
 
-	if (src >= mpic->num_sources)
+	if (mpic_irq_source_invalid(mpic, src)) {
+		WARN(1, "mpic: Reserved IRQ source %d\n", src);
 		return;
+	}
 
 	mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid);
 }
@@ -1038,7 +1061,7 @@ static int mpic_host_map(struct irq_domain *h, unsigned int virq,
 	if (mpic_map_error_int(mpic, virq, hw))
 		return 0;
 
-	if (hw >= mpic->num_sources) {
+	if (mpic_irq_source_invalid(mpic, hw)) {
 		pr_warn("mpic: Mapping of source 0x%x failed, source out of range !\n",
 			(unsigned int)hw);
 		return -EINVAL;
@@ -1210,6 +1233,52 @@ u32 fsl_mpic_primary_get_version(void)
 	return 0;
 }
 
+static u32 mpic_last_irq_from_ranges(struct mpic *mpic)
+{
+	int i;
+	u32 last_irq = 0;
+
+	for (i = 0; i < mpic->num_ranges; i++)
+		if (last_irq < mpic->irq_ranges[i].end_irq)
+			last_irq = mpic->irq_ranges[i].end_irq;
+
+	return last_irq;
+}
+
+static int __init mpic_init_irq_ranges(struct mpic *mpic)
+{
+	const u32 *irq_ranges;
+	u32 len, count;
+	int i;
+
+	irq_ranges = of_get_property(mpic->node, "supported-irq-ranges", &len);
+	if (irq_ranges == NULL) {
+		pr_info("%s : supported-irq-ranges not found in mpic(%p)\n",
+			__func__, mpic->node);
+		return -1;
+	}
+
+	if (len % (2 * sizeof(u32)) != 0) {
+		pr_info("%s : incorrect irq ranges in mpic(%p)\n",
+			__func__, mpic->node);
+		return -1;
+	}
+
+	count = len / (2 * sizeof(u32));
+	mpic->irq_ranges = kcalloc(count, sizeof(struct mpic_irq_range),
+				   GFP_KERNEL);
+	if (mpic->irq_ranges == NULL)
+		return -1;
+
+	mpic->num_ranges = count;
+	for (i = 0; i < count; i++) {
+		mpic->irq_ranges[i].start_irq = *irq_ranges++;
+		mpic->irq_ranges[i].end_irq = *irq_ranges++;
+	}
+
+	return 0;
+}
+
 static int mpic_get_last_irq_source(struct mpic *mpic,
 				    unsigned int irq_count,
 				    unsigned int isu_size)
@@ -1219,14 +1288,18 @@ static int mpic_get_last_irq_source(struct mpic *mpic,
 
 	/* Current priority order for getting last irq:
 	 *  1) irq_count from platform
-	 *  2) "last-interrupt-source" from device tree
-	 *  3) isu_size from platform
-	 *  4) MPIC h/w GREG_FEATURE_0 register
+	 *  2) "supported-irq-ranges" from device tree
+	 *  3) "last-interrupt-source" from device tree
+	 *  4) isu_size from platform
+	 *  5) MPIC h/w GREG_FEATURE_0 register
 	 */
 
 	if (irq_count)
 		return (irq_count - 1);
 
+	if (!mpic_init_irq_ranges(mpic))
+		return mpic_last_irq_from_ranges(mpic);
+
 	if (!of_property_read_u32(mpic->node, "last-interrupt-source",
 				  &last_irq)) {
 		return last_irq;
@@ -1632,6 +1705,10 @@ void __init mpic_init(struct mpic *mpic)
 			u32 vecpri = MPIC_VECPRI_MASK | i |
 				(8 << MPIC_VECPRI_PRIORITY_SHIFT);
 
+			/* Skip if source irq not valid */
+			if (mpic_irq_source_invalid(mpic, i))
+				continue;
+
 			/* check if protected */
 			if (mpic->protected && test_bit(i, mpic->protected))
 				continue;
@@ -1732,9 +1809,14 @@ void mpic_setup_this_cpu(void)
 	 * values of irq_desc[].affinity in irq.c.
  	 */
 	if (distribute_irqs && !(mpic->flags & MPIC_SINGLE_DEST_CPU)) {
-	 	for (i = 0; i < mpic->num_sources ; i++)
+		for (i = 0; i < mpic->num_sources ; i++) {
+			/* Skip if irq source is not valid */
+			if (mpic_irq_source_invalid(mpic, i))
+				continue;
+
 			mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION),
 				mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) | msk);
+		}
 	}
 
 	/* Set current processor priority to 0 */
@@ -1772,9 +1854,14 @@ void mpic_teardown_this_cpu(int secondary)
 	raw_spin_lock_irqsave(&mpic_lock, flags);
 
 	/* let the mpic know we don't want intrs.  */
-	for (i = 0; i < mpic->num_sources ; i++)
+	for (i = 0; i < mpic->num_sources ; i++) {
+		/* Skip if irq not valid */
+		if (mpic_irq_source_invalid(mpic, i))
+			continue;
+
 		mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION),
 			mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) & ~msk);
+	}
 
 	/* Set current processor priority to max */
 	mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf);
@@ -1958,6 +2045,10 @@ static void mpic_suspend_one(struct mpic *mpic)
 	int i;
 
 	for (i = 0; i < mpic->num_sources; i++) {
+		/* Skip if irq source not valid */
+		if (mpic_irq_source_invalid(mpic, i))
+			continue;
+
 		mpic->save_data[i].vecprio =
 			mpic_irq_read(i, MPIC_INFO(IRQ_VECTOR_PRI));
 		mpic->save_data[i].dest =
@@ -1982,6 +2073,10 @@ static void mpic_resume_one(struct mpic *mpic)
 	int i;
 
 	for (i = 0; i < mpic->num_sources; i++) {
+		/* Skip if irq source not valid */
+		if (mpic_irq_source_invalid(mpic, i))
+			continue;
+
 		mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI),
 			       mpic->save_data[i].vecprio);
 		mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION),
-- 
1.9.3



More information about the Linuxppc-dev mailing list