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@xxxxxxx> --- .../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 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html