diff options
Diffstat (limited to 'drivers/irqchip/irq-bcm7120-l2.c')
-rw-r--r-- | drivers/irqchip/irq-bcm7120-l2.c | 193 |
1 files changed, 134 insertions, 59 deletions
diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index 8eec8e1201d9..3ba5cc780fcb 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c @@ -14,6 +14,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/kconfig.h> +#include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_irq.h> @@ -34,15 +35,21 @@ #define IRQSTAT 0x04 #define MAX_WORDS 4 +#define MAX_MAPPINGS (MAX_WORDS * 2) #define IRQS_PER_WORD 32 struct bcm7120_l2_intc_data { unsigned int n_words; - void __iomem *base[MAX_WORDS]; + void __iomem *map_base[MAX_MAPPINGS]; + void __iomem *pair_base[MAX_WORDS]; + int en_offset[MAX_WORDS]; + int stat_offset[MAX_WORDS]; struct irq_domain *domain; bool can_wake; u32 irq_fwd_mask[MAX_WORDS]; u32 irq_map_mask[MAX_WORDS]; + int num_parent_irqs; + const __be32 *map_mask_prop; }; static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) @@ -61,7 +68,8 @@ static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) int hwirq; irq_gc_lock(gc); - pending = irq_reg_readl(gc, IRQSTAT) & gc->mask_cache; + pending = irq_reg_readl(gc, b->stat_offset[idx]) & + gc->mask_cache; irq_gc_unlock(gc); for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) { @@ -76,27 +84,30 @@ static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) static void bcm7120_l2_intc_suspend(struct irq_data *d) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct irq_chip_type *ct = irq_data_get_chip_type(d); struct bcm7120_l2_intc_data *b = gc->private; irq_gc_lock(gc); if (b->can_wake) - irq_reg_writel(gc, gc->mask_cache | gc->wake_active, IRQEN); + irq_reg_writel(gc, gc->mask_cache | gc->wake_active, + ct->regs.mask); irq_gc_unlock(gc); } static void bcm7120_l2_intc_resume(struct irq_data *d) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct irq_chip_type *ct = irq_data_get_chip_type(d); /* Restore the saved mask */ irq_gc_lock(gc); - irq_reg_writel(gc, gc->mask_cache, IRQEN); + irq_reg_writel(gc, gc->mask_cache, ct->regs.mask); irq_gc_unlock(gc); } static int bcm7120_l2_intc_init_one(struct device_node *dn, struct bcm7120_l2_intc_data *data, - int irq, const __be32 *map_mask) + int irq) { int parent_irq; unsigned int idx; @@ -110,9 +121,15 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn, /* For multiple parent IRQs with multiple words, this looks like: * <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...> */ - for (idx = 0; idx < data->n_words; idx++) - data->irq_map_mask[idx] |= - be32_to_cpup(map_mask + irq * data->n_words + idx); + for (idx = 0; idx < data->n_words; idx++) { + if (data->map_mask_prop) { + data->irq_map_mask[idx] |= + be32_to_cpup(data->map_mask_prop + + irq * data->n_words + idx); + } else { + data->irq_map_mask[idx] = 0xffffffff; + } + } irq_set_handler_data(parent_irq, data); irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle); @@ -120,68 +137,107 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn, return 0; } -int __init bcm7120_l2_intc_of_init(struct device_node *dn, - struct device_node *parent) +static int __init bcm7120_l2_intc_iomap_7120(struct device_node *dn, + struct bcm7120_l2_intc_data *data) +{ + int ret; + + data->map_base[0] = of_iomap(dn, 0); + if (!data->map_base[0]) { + pr_err("unable to map registers\n"); + return -ENOMEM; + } + + data->pair_base[0] = data->map_base[0]; + data->en_offset[0] = IRQEN; + data->stat_offset[0] = IRQSTAT; + data->n_words = 1; + + ret = of_property_read_u32_array(dn, "brcm,int-fwd-mask", + data->irq_fwd_mask, data->n_words); + if (ret != 0 && ret != -EINVAL) { + /* property exists but has the wrong number of words */ + pr_err("invalid brcm,int-fwd-mask property\n"); + return -EINVAL; + } + + data->map_mask_prop = of_get_property(dn, "brcm,int-map-mask", &ret); + if (!data->map_mask_prop || + (ret != (sizeof(__be32) * data->num_parent_irqs * data->n_words))) { + pr_err("invalid brcm,int-map-mask property\n"); + return -EINVAL; + } + + return 0; +} + +static int __init bcm7120_l2_intc_iomap_3380(struct device_node *dn, + struct bcm7120_l2_intc_data *data) +{ + unsigned int gc_idx; + + for (gc_idx = 0; gc_idx < MAX_WORDS; gc_idx++) { + unsigned int map_idx = gc_idx * 2; + void __iomem *en = of_iomap(dn, map_idx + 0); + void __iomem *stat = of_iomap(dn, map_idx + 1); + void __iomem *base = min(en, stat); + + data->map_base[map_idx + 0] = en; + data->map_base[map_idx + 1] = stat; + + if (!base) + break; + + data->pair_base[gc_idx] = base; + data->en_offset[gc_idx] = en - base; + data->stat_offset[gc_idx] = stat - base; + } + + if (!gc_idx) { + pr_err("unable to map registers\n"); + return -EINVAL; + } + + data->n_words = gc_idx; + return 0; +} + +int __init bcm7120_l2_intc_probe(struct device_node *dn, + struct device_node *parent, + int (*iomap_regs_fn)(struct device_node *, + struct bcm7120_l2_intc_data *), + const char *intc_name) { unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; struct bcm7120_l2_intc_data *data; struct irq_chip_generic *gc; struct irq_chip_type *ct; - const __be32 *map_mask; - int num_parent_irqs; - int ret = 0, len; + int ret = 0; unsigned int idx, irq, flags; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - for (idx = 0; idx < MAX_WORDS; idx++) { - data->base[idx] = of_iomap(dn, idx); - if (!data->base[idx]) - break; - data->n_words = idx + 1; - } - if (!data->n_words) { - pr_err("failed to remap intc L2 registers\n"); - ret = -ENOMEM; - goto out_unmap; - } - - /* Enable all interrupts specified in the interrupt forward mask; - * disable all others. If the property doesn't exist (-EINVAL), - * assume all zeroes. - */ - ret = of_property_read_u32_array(dn, "brcm,int-fwd-mask", - data->irq_fwd_mask, data->n_words); - if (ret == 0 || ret == -EINVAL) { - for (idx = 0; idx < data->n_words; idx++) - __raw_writel(data->irq_fwd_mask[idx], - data->base[idx] + IRQEN); - } else { - /* property exists but has the wrong number of words */ - pr_err("invalid int-fwd-mask property\n"); - ret = -EINVAL; - goto out_unmap; - } - - num_parent_irqs = of_irq_count(dn); - if (num_parent_irqs <= 0) { + data->num_parent_irqs = of_irq_count(dn); + if (data->num_parent_irqs <= 0) { pr_err("invalid number of parent interrupts\n"); ret = -ENOMEM; goto out_unmap; } - map_mask = of_get_property(dn, "brcm,int-map-mask", &len); - if (!map_mask || - (len != (sizeof(*map_mask) * num_parent_irqs * data->n_words))) { - pr_err("invalid brcm,int-map-mask property\n"); - ret = -EINVAL; + ret = iomap_regs_fn(dn, data); + if (ret < 0) goto out_unmap; + + for (idx = 0; idx < data->n_words; idx++) { + __raw_writel(data->irq_fwd_mask[idx], + data->pair_base[idx] + + data->en_offset[idx]); } - for (irq = 0; irq < num_parent_irqs; irq++) { - ret = bcm7120_l2_intc_init_one(dn, data, irq, map_mask); + for (irq = 0; irq < data->num_parent_irqs; irq++) { + ret = bcm7120_l2_intc_init_one(dn, data, irq); if (ret) goto out_unmap; } @@ -215,11 +271,12 @@ int __init bcm7120_l2_intc_of_init(struct device_node *dn, gc = irq_get_domain_generic_chip(data->domain, irq); gc->unused = 0xffffffff & ~data->irq_map_mask[idx]; - gc->reg_base = data->base[idx]; gc->private = data; ct = gc->chip_types; - ct->regs.mask = IRQEN; + gc->reg_base = data->pair_base[idx]; + ct->regs.mask = data->en_offset[idx]; + ct->chip.irq_mask = irq_gc_mask_clr_bit; ct->chip.irq_unmask = irq_gc_mask_set_bit; ct->chip.irq_ack = irq_gc_noop; @@ -236,20 +293,38 @@ int __init bcm7120_l2_intc_of_init(struct device_node *dn, } } - pr_info("registered BCM7120 L2 intc (mem: 0x%p, parent IRQ(s): %d)\n", - data->base[0], num_parent_irqs); + pr_info("registered %s intc (mem: 0x%p, parent IRQ(s): %d)\n", + intc_name, data->map_base[0], data->num_parent_irqs); return 0; out_free_domain: irq_domain_remove(data->domain); out_unmap: - for (idx = 0; idx < MAX_WORDS; idx++) { - if (data->base[idx]) - iounmap(data->base[idx]); + for (idx = 0; idx < MAX_MAPPINGS; idx++) { + if (data->map_base[idx]) + iounmap(data->map_base[idx]); } kfree(data); return ret; } + +int __init bcm7120_l2_intc_probe_7120(struct device_node *dn, + struct device_node *parent) +{ + return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_7120, + "BCM7120 L2"); +} + +int __init bcm7120_l2_intc_probe_3380(struct device_node *dn, + struct device_node *parent) +{ + return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_3380, + "BCM3380 L2"); +} + IRQCHIP_DECLARE(bcm7120_l2_intc, "brcm,bcm7120-l2-intc", - bcm7120_l2_intc_of_init); + bcm7120_l2_intc_probe_7120); + +IRQCHIP_DECLARE(bcm3380_l2_intc, "brcm,bcm3380-l2-intc", + bcm7120_l2_intc_probe_3380); |