On Fri, Jul 08, 2011 at 06:20:19PM +0800, Haojian Zhuang wrote:
> Parse irq sepcifier from DT and translate it to Linux irq number.
> 
> Signed-off-by: Haojian Zhuang <haojian.zhu...@marvell.com>
> ---
>  arch/arm/mach-mmp/Makefile |    2 +
>  arch/arm/mach-mmp/common.h |    1 +
>  arch/arm/mach-mmp/intc.c   |  245 
> ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 248 insertions(+), 0 deletions(-)
>  create mode 100644 arch/arm/mach-mmp/intc.c
> 
> diff --git a/arch/arm/mach-mmp/Makefile b/arch/arm/mach-mmp/Makefile
> index 5c68382..e7862ea 100644
> --- a/arch/arm/mach-mmp/Makefile
> +++ b/arch/arm/mach-mmp/Makefile
> @@ -4,6 +4,8 @@
>  
>  obj-y                                += common.o clock.o devices.o time.o
>  
> +obj-$(CONFIG_OF_IRQ)         += intc.o
> +
>  # SoC support
>  obj-$(CONFIG_CPU_PXA168)     += pxa168.o irq-pxa168.o
>  obj-$(CONFIG_CPU_PXA910)     += pxa910.o irq-pxa168.o
> diff --git a/arch/arm/mach-mmp/common.h b/arch/arm/mach-mmp/common.h
> index ec8d65d..1c563c2 100644
> --- a/arch/arm/mach-mmp/common.h
> +++ b/arch/arm/mach-mmp/common.h
> @@ -6,3 +6,4 @@ extern void timer_init(int irq);
>  
>  extern void __init icu_init_irq(void);
>  extern void __init mmp_map_io(void);
> +extern void __init mmp_init_intc(void);
> diff --git a/arch/arm/mach-mmp/intc.c b/arch/arm/mach-mmp/intc.c
> new file mode 100644
> index 0000000..48ad84b
> --- /dev/null
> +++ b/arch/arm/mach-mmp/intc.c
> @@ -0,0 +1,245 @@
> +/*
> + *  linux/arch/arm/mach-mmp/intc.c
> + *
> + *  Generic IRQ handling
> + *
> + *  Author:  Haojian Zhuang <haojian.zhu...@marvell.com>
> + *  Copyright:       Marvell International Ltd. 2011
> + *
> + *  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.
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/slab.h>
> +
> +struct mmp_intc_info {
> +     unsigned int            mask;
> +     unsigned int            phy_base;
> +     void __iomem            *virt_base;
> +     void __iomem            *mux_status;
> +     void __iomem            *mux_mask;
> +     void __iomem            *mfp_edge;
> +     unsigned int            mfp_edge_index; /* index in irq domain */
> +     unsigned int            irq_base;
> +};
> +
> +static void mux_irq_unmask(struct irq_data *d)
> +{
> +     struct mmp_intc_info *info = irq_data_get_irq_chip_data(d);
> +     unsigned int data, irq_offs;
> +
> +     irq_offs = d->irq - info->irq_base;
> +     if (info->mfp_edge && (info->mfp_edge_index == irq_offs)) {
> +             data = __raw_readl(info->mfp_edge);
> +             __raw_writel(data | (1 << 6), info->mfp_edge);
> +             __raw_writel(data, info->mfp_edge);
> +     }
> +     data = __raw_readl(info->mux_mask) & ~(1 << (d->irq - info->irq_base));
> +     __raw_writel(data, info->mux_mask);
> +}
> +
> +static void mux_irq_mask(struct irq_data *d)
> +{
> +     struct mmp_intc_info *info = irq_data_get_irq_chip_data(d);
> +     unsigned int data;
> +
> +     data = __raw_readl(info->mux_mask) | (1 << (d->irq - info->irq_base));
> +     __raw_writel(data, info->mux_mask);
> +}
> +
> +static struct irq_chip mux_irq_chip = {
> +     .name           = "mmp mux intc",
> +     .irq_unmask     = mux_irq_unmask,
> +     .irq_mask       = mux_irq_mask,
> +     .irq_ack        = mux_irq_mask,
> +};
> +
> +static void mmp_irq_demux_handler(unsigned int irq, struct irq_desc *desc)
> +{
> +     struct mmp_intc_info *info = irq_get_handler_data(irq);
> +     unsigned long status, mask, n;
> +
> +     mask = __raw_readl(info->mux_mask);
> +     while (1) {
> +             status = __raw_readl(info->mux_status) & ~mask;
> +             if (status == 0)
> +                     break;
> +             n = find_first_bit(&status, BITS_PER_LONG);
> +             while (n < BITS_PER_LONG) {
> +                     generic_handle_irq(info->irq_base + n);
> +                     n = find_next_bit(&status, BITS_PER_LONG, n + 1);
> +             }
> +     }
> +}
> +
> +static void mux_init_intc(struct mmp_intc_info *mmp_info)
> +{
> +     struct device_node *np;
> +     struct mmp_intc_info *mux_info;
> +     const __be32 *nr, *status, *edge;
> +     unsigned int addr = 0, offs = 0;
> +     int cascade, irq_nr, i;
> +
> +     if (mmp_info->virt_base == NULL)
> +             goto out;
> +
> +     for (np = NULL; (np = of_find_all_nodes(np)) != NULL;) {
> +             if (!of_device_is_compatible(np, "mrvl,mux-intc"))
> +                     continue;

for_each_compatible_node()

> +             if (of_get_property(np, "interrupt-controller", NULL) == NULL)
> +                     continue;
> +             nr = of_get_property(np, "sub-interrupts", NULL);
> +             if (nr == NULL) {
> +                     printk(KERN_WARNING "sub-interrupts property "
> +                             "is missed\n");
> +                     continue;
> +             }
> +             irq_nr = be32_to_cpu(*nr);

There's a new function you can use here that makes reading single property
values easier.  of_platform_read_u32() is queued up in linux-next.

> +             status = of_get_property(np, "status-mask", NULL);
> +             if (status == NULL) {
> +                     printk(KERN_WARNING "status-mask property is missed\n");
> +                     continue;
> +             }
> +             edge = of_get_property(np, "mfp-edge-interrupt", NULL);
> +
> +             mux_info = kzalloc(sizeof(struct mmp_intc_info), GFP_KERNEL);

kzalloc(sizeof(*mux_info), GFP_KERNEL);

> +             if (mux_info == NULL)
> +                     goto out;
> +             mux_info->virt_base = mmp_info->virt_base;
> +             mux_info->mux_status = be32_to_cpu(*status++)
> +                                     + mux_info->virt_base;
> +             mux_info->mux_mask = be32_to_cpu(*status)
> +                                     + mux_info->virt_base;
> +             if (edge) {
> +                     addr = be32_to_cpu(*edge) & PAGE_MASK;
> +                     offs = be32_to_cpu(*edge) - addr;
> +                     mux_info->mfp_edge = ioremap(addr, PAGE_SIZE) + offs;
> +                     mux_info->mfp_edge_index = be32_to_cpu(*++edge);
> +             }
> +
> +             /* allocate new irq */
> +             cascade = irq_of_parse_and_map(np, 0);
> +             mux_info->irq_base = irq_alloc_descs(-1, 0, irq_nr, 0);
> +             irq_domain_add_simple(np, mux_info->irq_base);
> +
> +             i = mux_info->irq_base;
> +             for (; i < mux_info->irq_base + irq_nr; i++) {
> +                     irq_set_chip_data(i, mux_info);
> +                     mux_irq_mask(irq_get_irq_data(i));
> +                     irq_set_chip_and_handler(i, &mux_irq_chip,
> +                                     handle_level_irq);
> +                     set_irq_flags(i, IRQF_VALID);
> +             }
> +
> +             irq_set_handler_data(cascade, mux_info);
> +             irq_set_chained_handler(cascade, mmp_irq_demux_handler);
> +     }
> +out:
> +     return;
> +}

It looks like this function is parsing a new device tree binding.  Any
new bindings needs to be documented in Documentation/devicetree/bindings.

> +
> +static void mmp_irq_unmask(struct irq_data *d)
> +{
> +     struct mmp_intc_info *info = irq_data_get_irq_chip_data(d);
> +
> +     /* ICU_INT_CONF */
> +     __raw_writel(info->mask, info->virt_base + (d->irq << 2));
> +}
> +
> +static void mmp_irq_mask(struct irq_data *d)
> +{
> +     struct mmp_intc_info *info = irq_data_get_irq_chip_data(d);
> +
> +     __raw_writel(0, info->virt_base + (d->irq << 2));
> +}
> +
> +static struct irq_chip mmp_irq_chip = {
> +     .name           = "mmp-intc",
> +     .irq_unmask     = mmp_irq_unmask,
> +     .irq_mask       = mmp_irq_mask,
> +     .irq_ack        = mmp_irq_mask,
> +};

Can you use IRQ_GENERIC_CHIP?

> +
> +void __init mmp_init_intc(void)
> +{
> +     struct mmp_intc_info *info;
> +     struct device_node *np;
> +     const __be32 *enable_mask, *base, *cell, *nr;
> +     int i, irq_nr, phy_base;
> +
> +     np = of_find_compatible_node(NULL, NULL, "mrvl,mmp-intc");
> +
> +     BUG_ON(!np);
> +
> +     of_node_get(np);
> +     if (of_get_property(np, "interrupt-controller", NULL) == NULL)
> +             goto out;
> +     cell = of_get_property(np, "#interrupt-cells", NULL);
> +     if (cell == NULL) {
> +             printk(KERN_ERR "mmp-intc: Device node %s missing "
> +                     "interrupt-cells property\n", np->full_name);
> +             goto out;
> +     }
> +     if (be32_to_cpu(*cell) != 1) {
> +             printk(KERN_ERR "mmp-intc: interrupt-cells property is "
> +                     "incorrect:%d\n", be32_to_cpu(*cell));
> +             goto out;
> +     }
> +
> +     nr = of_get_property(np, "sub-interrupts", NULL);
> +     if (nr == NULL) {
> +             printk(KERN_ERR "mmp-intc: interrupts property is missed\n");
> +             goto out;
> +     }
> +     irq_nr = be32_to_cpu(*nr);
> +
> +     base = of_get_property(np, "reg", NULL);
> +     if (base == NULL) {
> +             printk(KERN_ERR "intc: Device node %s missing reg property\n",
> +                     np->full_name);
> +             goto out;
> +     }
> +     phy_base = of_translate_address(np, base);

Use of_address_to_resource().

> +
> +     info = kzalloc(sizeof(struct mmp_intc_info), GFP_KERNEL);
> +     if (info == NULL)
> +             goto out;
> +
> +     enable_mask = of_get_property(np, "enable_mask", NULL);
> +     if (enable_mask == NULL) {
> +             printk(KERN_ERR "interrupt controller: Device node %s "
> +                     "missing interrupt property\n", np->full_name);
> +             goto out_mem;
> +     }
> +     info->mask = be32_to_cpu(*enable_mask);
> +
> +     /* phy_base: 0, phy_size:64 */
> +     info->phy_base = phy_base;
> +     info->virt_base = ioremap(info->phy_base, PAGE_SIZE);
> +
> +     /* allocate new irq */
> +     info->irq_base = irq_alloc_descs(-1, 0, irq_nr, 0);
> +     irq_domain_add_simple(np, 0);

Excellent! Good use of dynamic irqs.

> +
> +     for (i = info->irq_base; i < info->irq_base + irq_nr; i++) {
> +             irq_set_chip_data(i, info);
> +             mmp_irq_mask(irq_get_irq_data(i));
> +             irq_set_chip_and_handler(i, &mmp_irq_chip, handle_level_irq);
> +             set_irq_flags(i, IRQF_VALID);
> +     }
> +     mux_init_intc(info);
> +     of_node_put(np);
> +     return;
> +out_mem:
> +     kfree(info);
> +out:
> +     of_node_put(np);
> +     return;
> +}

There's a lot of duplicate code here between the primary and cascaded
irq controllers.  It can probably be consolidated.

g.

_______________________________________________
devicetree-discuss mailing list
devicetree-discuss@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/devicetree-discuss

Reply via email to