On Mar 27, 2012, at 7:17 AM, Varun Sethi wrote:

> All SOC device error interrupts are muxed and delivered to the core as a 
> single
> MPIC error interrupt. Currently all the device drivers requiring access to 
> device
> errors have to register for the MPIC error interrupt as a shared interrupt.
> 
> With this patch we add interrupt demuxing capability in the mpic driver, 
> allowing
> device drivers to register for their individual error interrupts. This is 
> achieved
> by handling error interrupts in a cascaded fashion.
> 
> MPIC error interrupt is handled by the "error_int_handler", which 
> subsequently demuxes
> it using the EISR and delivers it to the respective drivers. 
> 
> The error interrupt capability is dependent on the MPIC EIMR register, which 
> was
> introduced in FSL MPIC version 4.1 (P4080 rev2). So, error interrupt demuxing 
> capability
> is dependent on the MPIC version and can be used for versions >= 4.1.
> 
> Signed-off-by: Varun Sethi <varun.se...@freescale.com>
> ---

This isn't quite right.  Doing the 'request_irq' on behalf of the driver isn't 
treating the error interrupts as a real cascade, in addition to how you are 
hooking things up.

I think you need to add proper irq_domain_ops, etc.  Thus when we map the virq 
the proper thing can happen.

I'd also suggest maybe thinking about pulling this code into an mpic_fsl.c

> arch/powerpc/include/asm/mpic.h |   18 +++++
> arch/powerpc/sysdev/mpic.c      |  155 ++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 171 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h
> index 3929b4b..db51015 100644
> --- a/arch/powerpc/include/asm/mpic.h
> +++ b/arch/powerpc/include/asm/mpic.h
> @@ -114,12 +114,21 @@
> #define MPIC_FSL_BRR1                 0x00000
> #define       MPIC_FSL_BRR1_VER                       0x0000ffff
> 
> +/*
> + * Error interrupt registers
> + */
> +
> +#define MPIC_ERR_INT_BASE    0x3900
> +#define MPIC_ERR_INT_EISR    0x0000
> +#define MPIC_ERR_INT_EIMR    0x0010
> +
> #define MPIC_MAX_IRQ_SOURCES  2048
> #define MPIC_MAX_CPUS         32
> #define MPIC_MAX_ISU          32
> 
> #define MPIC_MAX_TIMER    8
> #define MPIC_MAX_IPI      4
> +#define MPIC_MAX_ERR      32

Should probably be 64

> /*
>  * Tsi108 implementation of MPIC has many differences from the original one
> @@ -273,6 +282,7 @@ struct mpic
>       struct irq_chip         hc_ipi;
> #endif
>       struct irq_chip         hc_tm;
> +     struct irq_chip         hc_err;
>       const char              *name;
>       /* Flags */
>       unsigned int            flags;
> @@ -289,6 +299,8 @@ struct mpic
>       /* vector numbers used for internal sources (ipi/timers) */
>       unsigned int            ipi_vecs[MPIC_MAX_IPI];
>       unsigned int            timer_vecs[MPIC_MAX_TIMER];
> +     /* vector numbers used for FSL MPIC error interrupts */
> +     unsigned int            err_int_vecs[MPIC_MAX_ERR];
> 
>       /* Spurious vector to program into unused sources */
>       unsigned int            spurious_vec;
> @@ -311,6 +323,10 @@ struct mpic
>       struct mpic_reg_bank    tmregs;
>       struct mpic_reg_bank    cpuregs[MPIC_MAX_CPUS];
>       struct mpic_reg_bank    isus[MPIC_MAX_ISU];
> +     struct mpic_reg_bank    err_regs;
> +
> +     /* error interrupt config */
> +     u32                     err_int_config_done;
> 
>       /* Protected sources */
>       unsigned long           *protected;
> @@ -376,6 +392,8 @@ struct mpic
> #define MPIC_NO_RESET                 0x00004000
> /* Freescale MPIC (compatible includes "fsl,mpic") */
> #define MPIC_FSL                      0x00008000
> +/* Freescale MPIC supports EIMR (error interrupt mask register)*/
> +#define MPIC_FSL_HAS_EIMR            0x00010000
> 
> /* MPIC HW modification ID */
> #define MPIC_REGSET_MASK              0xf0000000

> diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
> index c4da1d5..b0ff465 100644
> --- a/arch/powerpc/sysdev/mpic.c
> +++ b/arch/powerpc/sysdev/mpic.c
> @@ -221,6 +221,17 @@ static inline void _mpic_ipi_write(struct mpic *mpic, 
> unsigned int ipi, u32 valu
>       _mpic_write(mpic->reg_type, &mpic->gregs, offset, value);
> }
> 
> +static inline u32 _mpic_err_read(struct mpic *mpic, unsigned int err_reg)
> +{
> +     return _mpic_read(mpic->reg_type, &mpic->err_regs, err_reg);
> +}
> +
> +static inline void _mpic_err_write(struct mpic *mpic, unsigned int err_reg,
> +                                u32 value)
> +{
> +     _mpic_write(mpic->reg_type, &mpic->err_regs, err_reg, value);
> +}
> +
> static inline unsigned int mpic_tm_offset(struct mpic *mpic, unsigned int tm)
> {
>       return (tm >> 2) * MPIC_TIMER_GROUP_STRIDE +
> @@ -295,6 +306,8 @@ static inline void _mpic_irq_write(struct mpic *mpic, 
> unsigned int src_no,
> #define mpic_ipi_write(i,v)   _mpic_ipi_write(mpic,(i),(v))
> #define mpic_tm_read(i)               _mpic_tm_read(mpic,(i))
> #define mpic_tm_write(i,v)    _mpic_tm_write(mpic,(i),(v))
> +#define mpic_err_read(i)     _mpic_err_read(mpic, (i))
> +#define mpic_err_write(i, v) _mpic_err_write(mpic, (i), (v))
> #define mpic_cpu_read(i)      _mpic_cpu_read(mpic,(i))
> #define mpic_cpu_write(i,v)   _mpic_cpu_write(mpic,(i),(v))
> #define mpic_irq_read(s,r)    _mpic_irq_read(mpic,(s),(r))
> @@ -821,6 +834,86 @@ static void mpic_mask_tm(struct irq_data *d)
>       mpic_tm_read(src);
> }
> 
> +static void mpic_mask_err(struct irq_data *d)
> +{
> +     u32 eimr;
> +     struct mpic *mpic = mpic_from_irq_data(d);
> +     unsigned int src = virq_to_hw(d->irq) - mpic->err_int_vecs[0];
> +     unsigned int err_reg_offset = MPIC_INFO(ERR_INT_EIMR);
> +
> +     eimr = mpic_err_read(err_reg_offset);
> +     eimr |= (0x80000000 >> src);

This seems funny, add a comment about src # and bit # being opposite.  Maybe do:

        eimr |= (1 << (31 - src));

> +     mpic_err_write(err_reg_offset, eimr);
> +}
> +
> +static void mpic_unmask_err(struct irq_data *d)
> +{
> +     u32 eimr;
> +     struct mpic *mpic = mpic_from_irq_data(d);
> +     unsigned int src = virq_to_hw(d->irq) - mpic->err_int_vecs[0];
> +     unsigned int err_reg_offset = MPIC_INFO(ERR_INT_EIMR);
> +
> +     eimr = mpic_err_read(err_reg_offset);
> +     eimr &= ~(0x80000000 >> src);

same as above

> +     mpic_err_write(err_reg_offset, eimr);
> +}
> +
> +static irqreturn_t error_int_handler(int irq, void *data)
> +{
> +     struct mpic *mpic = (struct mpic *) data;
> +     unsigned int eisr_offset = MPIC_INFO(ERR_INT_EISR);
> +     unsigned int eimr_offset = MPIC_INFO(ERR_INT_EIMR);
> +     u32 eisr, eimr;
> +     int errint;
> +     unsigned int cascade_irq;
> +
> +     eisr = mpic_err_read(eisr_offset);
> +     eimr = mpic_err_read(eimr_offset);
> +
> +     if (!(eisr & ~eimr))
> +             return IRQ_NONE;
> +
> +     while (eisr) {
> +             errint = __ffs(eisr);
> +             cascade_irq = irq_linear_revmap(mpic->irqhost,
> +                              mpic->err_int_vecs[31 - errint]);

Should 31, be replaced w/MPIC_MAX_ERR - 1 ?

> +             WARN_ON(cascade_irq == NO_IRQ);
> +             if (cascade_irq != NO_IRQ) {
> +                     generic_handle_irq(cascade_irq);
> +             } else {
> +                     eimr |=  1 << errint;
> +                     mpic_err_write(eimr_offset, eimr);
> +             }
> +             eisr &= ~(1 << errint);
> +     }
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static int mpic_err_int_init(struct mpic *mpic, irq_hw_number_t irqnum)
> +{
> +     unsigned int virq;
> +     unsigned int offset = MPIC_INFO(ERR_INT_EIMR);
> +     int ret;
> +
> +     virq = irq_create_mapping(mpic->irqhost, irqnum);
> +     if (virq == NO_IRQ) {
> +             pr_err("Error interrupt setup failed\n");
> +             return -ENOSPC;
> +     }
> +
> +     mpic_err_write(offset, ~0);
> +
> +     ret = request_irq(virq, error_int_handler, IRQF_NO_THREAD,
> +                 "mpic-error-int", mpic);
> +     if (ret) {
> +             pr_err("Failed to register error interrupt handler\n");
> +             return ret;
> +     }
> +
> +     return 0;
> +}
> +
> int mpic_set_affinity(struct irq_data *d, const struct cpumask *cpumask,
>                     bool force)
> {
> @@ -947,6 +1040,12 @@ static struct irq_chip mpic_ipi_chip = {
> };
> #endif /* CONFIG_SMP */
> 
> +static struct irq_chip mpic_err_chip = {
> +     .irq_disable    = mpic_mask_err,
> +     .irq_mask       = mpic_mask_err,
> +     .irq_unmask     = mpic_unmask_err,
> +};
> +
> static struct irq_chip mpic_tm_chip = {
>       .irq_mask       = mpic_mask_tm,
>       .irq_unmask     = mpic_unmask_tm,
> @@ -984,8 +1083,19 @@ static int mpic_host_map(struct irq_host *h, unsigned 
> int virq,
>       if (mpic->protected && test_bit(hw, mpic->protected))
>               return -EINVAL;
> 
> +     if ((mpic->flags & MPIC_FSL_HAS_EIMR) &&
> +         hw >= mpic->err_int_vecs[0]) {
> +             WARN_ON(mpic->flags & MPIC_SECONDARY);
> +
> +             DBG("mpic: mapping as Error Interrupt\n");
> +             irq_set_chip_data(virq, mpic);
> +             irq_set_chip_and_handler(virq, &mpic->hc_err,
> +                                      handle_simple_irq);
> +             return 0;
> +     }
> #ifdef CONFIG_SMP
> -     else if (hw >= mpic->ipi_vecs[0]) {
> +     if (hw >= mpic->ipi_vecs[0] &&
> +         hw <= mpic->ipi_vecs[MPIC_MAX_IPI - 1]) {
>               WARN_ON(mpic->flags & MPIC_SECONDARY);
> 
>               DBG("mpic: mapping as IPI\n");
> @@ -1066,7 +1176,23 @@ static int mpic_host_xlate(struct irq_host *h, struct 
> device_node *ct,
>                */
>               switch (intspec[2]) {
>               case 0:
> -             case 1: /* no EISR/EIMR support for now, treat as shared IRQ */
> +                     break;
> +             case 1:
> +                     if (!(mpic->flags & MPIC_FSL_HAS_EIMR))
> +                             break;
> +
> +                     if (intspec[3] >= ARRAY_SIZE(mpic->err_int_vecs))
> +                             return -EINVAL;
> +
> +                     if (!mpic->err_int_config_done) {
> +                             int ret;
> +                             ret = mpic_err_int_init(mpic, intspec[0]);
> +                             if (ret)
> +                                     return ret;
> +                             mpic->err_int_config_done = 1;
> +                     }
> +
> +                     *out_hwirq = mpic->err_int_vecs[intspec[3]];
>                       break;
>               case 2:
>                       if (intspec[0] >= ARRAY_SIZE(mpic->ipi_vecs))
> @@ -1140,6 +1266,11 @@ static void mpic_alloc_int_sources(struct mpic *mpic, 
> int intvec_top)
> 
>       intvec = intvec_top;
> 
> +     if (mpic->flags & MPIC_FSL_HAS_EIMR) {
> +             for (i = MPIC_MAX_ERR - 1; i >= 0; i--)
> +                     mpic->err_int_vecs[i] = --intvec;
> +     }
> +
>       for (i = MPIC_MAX_IPI - 1; i >= 0; i--)
>               mpic->ipi_vecs[i] = --intvec;
> 
> @@ -1284,6 +1415,8 @@ struct mpic * __init mpic_alloc(struct device_node 
> *node,
>       mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 
> 0x1000);
> 
>       if (mpic->flags & MPIC_FSL) {
> +             u32 brr1, version;
> +
>               /*
>                * Yes, Freescale really did put global registers in the
>                * magic per-cpu area -- and they don't even show up in the
> @@ -1291,6 +1424,24 @@ struct mpic * __init mpic_alloc(struct device_node 
> *node,
>                */
>               mpic_map(mpic, mpic->paddr, &mpic->thiscpuregs,
>                        MPIC_CPU_THISBASE, 0x1000);
> +
> +             brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs,
> +                             MPIC_FSL_BRR1);
> +             version = brr1 & MPIC_FSL_BRR1_VER;
> +
> +             /* Error interrupt mask register (EIMR) is required for
> +              * handling individual device error interrupts. EIMR
> +              * was added in MPIC version 4.1.
> +              */
> +             if (version >= 0x401) {
> +                     mpic->hc_err = mpic_err_chip;
> +                     mpic->hc_err.name = mpic->name;
> +                     /* Map error interrupt registers */
> +                     mpic_map(mpic, mpic->paddr, &mpic->err_regs,
> +                              MPIC_INFO(ERR_INT_BASE), 0x1000);
> +                     mpic->flags |= MPIC_FSL_HAS_EIMR;
> +             }
> +
>       }
> 
>       /* Reset */
> -- 
> 1.7.2.2
> 
> 
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev

_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to