diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-17 21:50:54 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-17 21:50:54 +0200 |
commit | bfaf245022b4b8661af2e35f467cf0e91943c24c (patch) | |
tree | b5a6ee49a047557a791eb897c8c9545a155e36b7 /drivers/irqchip | |
parent | Merge tag 'xtensa-20150416' of git://github.com/czankel/xtensa-linux (diff) | |
parent | Merge branch '4.0-fixes' into mips-for-linux-next (diff) | |
download | linux-bfaf245022b4b8661af2e35f467cf0e91943c24c.tar.xz linux-bfaf245022b4b8661af2e35f467cf0e91943c24c.zip |
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
Pull MIPS updates from Ralf Baechle:
"This is the main pull request for MIPS for Linux 4.1. Most
noteworthy:
- Add more Octeon-optimized crypto functions
- Octeon crypto preemption and locking fixes
- Little endian support for Octeon
- Use correct CSR to soft reset Octeons
- Support LEDs on the Octeon-based DSR-1000N
- Fix PCI interrupt mapping for the Octeon-based DSR-1000N
- Mark prom_free_prom_memory() as __init for a number of systems
- Support for Imagination's Pistachio SOC. This includes arch and
CLK bits. I'd like to merge pinctrl bits later
- Improve parallelism of csum_partial for certain pipelines
- Organize DTB files in subdirs like other architectures
- Implement read_sched_clock for all MIPS platforms other than
Octeon
- Massive series of 38 fixes and cleanups for the FPU emulator /
kernel
- Further FPU remulator work to support new features. This sits on a
separate branch which also has been pulled into the 4.1 KVM branch
- Clean up and fixes for the SEAD3 eval board; remove unused file
- Various updates for Netlogic platforms
- A number of small updates for Loongson 3 platforms
- Increase the memory limit for ATH79 platforms to 256MB
- A fair number of fixes and updates for BCM47xx platforms
- Finish the implementation of XPA support
- MIPS FDC support. No, not floppy controller but Fast Debug Channel :)
- Detect the R16000 used in SGI legacy platforms
- Fix Kconfig dependencies for the SSB bus support"
* 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus: (265 commits)
MIPS: Makefile: Fix MIPS ASE detection code
MIPS: asm: elf: Set O32 default FPU flags
MIPS: BCM47XX: Fix detecting Microsoft MN-700 & Asus WL500G
MIPS: Kconfig: Disable SMP/CPS for 64-bit
MIPS: Hibernate: flush TLB entries earlier
MIPS: smp-cps: cpu_set FPU mask if FPU present
MIPS: lose_fpu(): Disable FPU when MSA enabled
MIPS: ralink: add missing symbol for RALINK_ILL_ACC
MIPS: ralink: Fix bad config symbol in PCI makefile.
SSB: fix Kconfig dependencies
MIPS: Malta: Detect and fix bad memsize values
Revert "MIPS: Avoid pipeline stalls on some MIPS32R2 cores."
MIPS: Octeon: Delete override of cpu_has_mips_r2_exec_hazard.
MIPS: Fix cpu_has_mips_r2_exec_hazard.
MIPS: kernel: entry.S: Set correct ISA level for mips_ihb
MIPS: asm: spinlock: Fix addiu instruction for R10000_LLSC_WAR case
MIPS: r4kcache: Use correct base register for MIPS R6 cache flushes
MIPS: Kconfig: Fix typo for the r2-to-r6 emulator kernel parameter
MIPS: unaligned: Fix regular load/store instruction emulation for EVA
MIPS: unaligned: Surround load/store macros in do {} while statements
...
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/Kconfig | 5 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 1 | ||||
-rw-r--r-- | drivers/irqchip/irq-bcm7038-l1.c | 335 | ||||
-rw-r--r-- | drivers/irqchip/irq-bcm7120-l2.c | 193 | ||||
-rw-r--r-- | drivers/irqchip/irq-brcmstb-l2.c | 9 | ||||
-rw-r--r-- | drivers/irqchip/irq-mips-gic.c | 40 |
6 files changed, 515 insertions, 68 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index c8d260e33a90..6de62a96e79c 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -60,6 +60,11 @@ config ATMEL_AIC5_IRQ select MULTI_IRQ_HANDLER select SPARSE_IRQ +config BCM7038_L1_IRQ + bool + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + config BCM7120_L2_IRQ bool select GENERIC_IRQ_CHIP diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 552a74027601..dda4927e47a6 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o +obj-$(CONFIG_BCM7038_L1_IRQ) += irq-bcm7038-l1.o obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c new file mode 100644 index 000000000000..d3b8c8be15f6 --- /dev/null +++ b/drivers/irqchip/irq-bcm7038-l1.c @@ -0,0 +1,335 @@ +/* + * Broadcom BCM7038 style Level 1 interrupt controller driver + * + * Copyright (C) 2014 Broadcom Corporation + * Author: Kevin Cernekee + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/bitops.h> +#include <linux/kconfig.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/smp.h> +#include <linux/types.h> +#include <linux/irqchip/chained_irq.h> + +#include "irqchip.h" + +#define IRQS_PER_WORD 32 +#define REG_BYTES_PER_IRQ_WORD (sizeof(u32) * 4) +#define MAX_WORDS 8 + +struct bcm7038_l1_cpu; + +struct bcm7038_l1_chip { + raw_spinlock_t lock; + unsigned int n_words; + struct irq_domain *domain; + struct bcm7038_l1_cpu *cpus[NR_CPUS]; + u8 affinity[MAX_WORDS * IRQS_PER_WORD]; +}; + +struct bcm7038_l1_cpu { + void __iomem *map_base; + u32 mask_cache[0]; +}; + +/* + * STATUS/MASK_STATUS/MASK_SET/MASK_CLEAR are packed one right after another: + * + * 7038: + * 0x1000_1400: W0_STATUS + * 0x1000_1404: W1_STATUS + * 0x1000_1408: W0_MASK_STATUS + * 0x1000_140c: W1_MASK_STATUS + * 0x1000_1410: W0_MASK_SET + * 0x1000_1414: W1_MASK_SET + * 0x1000_1418: W0_MASK_CLEAR + * 0x1000_141c: W1_MASK_CLEAR + * + * 7445: + * 0xf03e_1500: W0_STATUS + * 0xf03e_1504: W1_STATUS + * 0xf03e_1508: W2_STATUS + * 0xf03e_150c: W3_STATUS + * 0xf03e_1510: W4_STATUS + * 0xf03e_1514: W0_MASK_STATUS + * 0xf03e_1518: W1_MASK_STATUS + * [...] + */ + +static inline unsigned int reg_status(struct bcm7038_l1_chip *intc, + unsigned int word) +{ + return (0 * intc->n_words + word) * sizeof(u32); +} + +static inline unsigned int reg_mask_status(struct bcm7038_l1_chip *intc, + unsigned int word) +{ + return (1 * intc->n_words + word) * sizeof(u32); +} + +static inline unsigned int reg_mask_set(struct bcm7038_l1_chip *intc, + unsigned int word) +{ + return (2 * intc->n_words + word) * sizeof(u32); +} + +static inline unsigned int reg_mask_clr(struct bcm7038_l1_chip *intc, + unsigned int word) +{ + return (3 * intc->n_words + word) * sizeof(u32); +} + +static inline u32 l1_readl(void __iomem *reg) +{ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + return ioread32be(reg); + else + return readl(reg); +} + +static inline void l1_writel(u32 val, void __iomem *reg) +{ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + iowrite32be(val, reg); + else + writel(val, reg); +} + +static void bcm7038_l1_irq_handle(unsigned int irq, struct irq_desc *desc) +{ + struct bcm7038_l1_chip *intc = irq_desc_get_handler_data(desc); + struct bcm7038_l1_cpu *cpu; + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned int idx; + +#ifdef CONFIG_SMP + cpu = intc->cpus[cpu_logical_map(smp_processor_id())]; +#else + cpu = intc->cpus[0]; +#endif + + chained_irq_enter(chip, desc); + + for (idx = 0; idx < intc->n_words; idx++) { + int base = idx * IRQS_PER_WORD; + unsigned long pending, flags; + int hwirq; + + raw_spin_lock_irqsave(&intc->lock, flags); + pending = l1_readl(cpu->map_base + reg_status(intc, idx)) & + ~cpu->mask_cache[idx]; + raw_spin_unlock_irqrestore(&intc->lock, flags); + + for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) { + generic_handle_irq(irq_find_mapping(intc->domain, + base + hwirq)); + } + } + + chained_irq_exit(chip, desc); +} + +static void __bcm7038_l1_unmask(struct irq_data *d, unsigned int cpu_idx) +{ + struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); + u32 word = d->hwirq / IRQS_PER_WORD; + u32 mask = BIT(d->hwirq % IRQS_PER_WORD); + + intc->cpus[cpu_idx]->mask_cache[word] &= ~mask; + l1_writel(mask, intc->cpus[cpu_idx]->map_base + + reg_mask_clr(intc, word)); +} + +static void __bcm7038_l1_mask(struct irq_data *d, unsigned int cpu_idx) +{ + struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); + u32 word = d->hwirq / IRQS_PER_WORD; + u32 mask = BIT(d->hwirq % IRQS_PER_WORD); + + intc->cpus[cpu_idx]->mask_cache[word] |= mask; + l1_writel(mask, intc->cpus[cpu_idx]->map_base + + reg_mask_set(intc, word)); +} + +static void bcm7038_l1_unmask(struct irq_data *d) +{ + struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); + unsigned long flags; + + raw_spin_lock_irqsave(&intc->lock, flags); + __bcm7038_l1_unmask(d, intc->affinity[d->hwirq]); + raw_spin_unlock_irqrestore(&intc->lock, flags); +} + +static void bcm7038_l1_mask(struct irq_data *d) +{ + struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); + unsigned long flags; + + raw_spin_lock_irqsave(&intc->lock, flags); + __bcm7038_l1_mask(d, intc->affinity[d->hwirq]); + raw_spin_unlock_irqrestore(&intc->lock, flags); +} + +static int bcm7038_l1_set_affinity(struct irq_data *d, + const struct cpumask *dest, + bool force) +{ + struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); + unsigned long flags; + irq_hw_number_t hw = d->hwirq; + u32 word = hw / IRQS_PER_WORD; + u32 mask = BIT(hw % IRQS_PER_WORD); + unsigned int first_cpu = cpumask_any_and(dest, cpu_online_mask); + bool was_disabled; + + raw_spin_lock_irqsave(&intc->lock, flags); + + was_disabled = !!(intc->cpus[intc->affinity[hw]]->mask_cache[word] & + mask); + __bcm7038_l1_mask(d, intc->affinity[hw]); + intc->affinity[hw] = first_cpu; + if (!was_disabled) + __bcm7038_l1_unmask(d, first_cpu); + + raw_spin_unlock_irqrestore(&intc->lock, flags); + return 0; +} + +static int __init bcm7038_l1_init_one(struct device_node *dn, + unsigned int idx, + struct bcm7038_l1_chip *intc) +{ + struct resource res; + resource_size_t sz; + struct bcm7038_l1_cpu *cpu; + unsigned int i, n_words, parent_irq; + + if (of_address_to_resource(dn, idx, &res)) + return -EINVAL; + sz = resource_size(&res); + n_words = sz / REG_BYTES_PER_IRQ_WORD; + + if (n_words > MAX_WORDS) + return -EINVAL; + else if (!intc->n_words) + intc->n_words = n_words; + else if (intc->n_words != n_words) + return -EINVAL; + + cpu = intc->cpus[idx] = kzalloc(sizeof(*cpu) + n_words * sizeof(u32), + GFP_KERNEL); + if (!cpu) + return -ENOMEM; + + cpu->map_base = ioremap(res.start, sz); + if (!cpu->map_base) + return -ENOMEM; + + for (i = 0; i < n_words; i++) { + l1_writel(0xffffffff, cpu->map_base + reg_mask_set(intc, i)); + cpu->mask_cache[i] = 0xffffffff; + } + + parent_irq = irq_of_parse_and_map(dn, idx); + if (!parent_irq) { + pr_err("failed to map parent interrupt %d\n", parent_irq); + return -EINVAL; + } + irq_set_handler_data(parent_irq, intc); + irq_set_chained_handler(parent_irq, bcm7038_l1_irq_handle); + + return 0; +} + +static struct irq_chip bcm7038_l1_irq_chip = { + .name = "bcm7038-l1", + .irq_mask = bcm7038_l1_mask, + .irq_unmask = bcm7038_l1_unmask, + .irq_set_affinity = bcm7038_l1_set_affinity, +}; + +static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw_irq) +{ + irq_set_chip_and_handler(virq, &bcm7038_l1_irq_chip, handle_level_irq); + irq_set_chip_data(virq, d->host_data); + return 0; +} + +static const struct irq_domain_ops bcm7038_l1_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = bcm7038_l1_map, +}; + +int __init bcm7038_l1_of_init(struct device_node *dn, + struct device_node *parent) +{ + struct bcm7038_l1_chip *intc; + int idx, ret; + + intc = kzalloc(sizeof(*intc), GFP_KERNEL); + if (!intc) + return -ENOMEM; + + raw_spin_lock_init(&intc->lock); + for_each_possible_cpu(idx) { + ret = bcm7038_l1_init_one(dn, idx, intc); + if (ret < 0) { + if (idx) + break; + pr_err("failed to remap intc L1 registers\n"); + goto out_free; + } + } + + intc->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * intc->n_words, + &bcm7038_l1_domain_ops, + intc); + if (!intc->domain) { + ret = -ENOMEM; + goto out_unmap; + } + + pr_info("registered BCM7038 L1 intc (mem: 0x%p, IRQs: %d)\n", + intc->cpus[0]->map_base, IRQS_PER_WORD * intc->n_words); + + return 0; + +out_unmap: + for_each_possible_cpu(idx) { + struct bcm7038_l1_cpu *cpu = intc->cpus[idx]; + + if (cpu) { + if (cpu->map_base) + iounmap(cpu->map_base); + kfree(cpu); + } + } +out_free: + kfree(intc); + return ret; +} + +IRQCHIP_DECLARE(bcm7038_l1, "brcm,bcm7038-l1-intc", bcm7038_l1_of_init); 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); diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c index 313c2c64498a..d6bcc6be0777 100644 --- a/drivers/irqchip/irq-brcmstb-l2.c +++ b/drivers/irqchip/irq-brcmstb-l2.c @@ -136,7 +136,11 @@ int __init brcmstb_l2_intc_of_init(struct device_node *np, /* Disable all interrupts by default */ writel(0xffffffff, data->base + CPU_MASK_SET); - writel(0xffffffff, data->base + CPU_CLEAR); + + /* Wakeup interrupts may be retained from S5 (cold boot) */ + data->can_wake = of_property_read_bool(np, "brcm,irq-can-wake"); + if (!data->can_wake) + writel(0xffffffff, data->base + CPU_CLEAR); data->parent_irq = irq_of_parse_and_map(np, 0); if (!data->parent_irq) { @@ -188,8 +192,7 @@ int __init brcmstb_l2_intc_of_init(struct device_node *np, ct->chip.irq_suspend = brcmstb_l2_intc_suspend; ct->chip.irq_resume = brcmstb_l2_intc_resume; - if (of_property_read_bool(np, "brcm,irq-can-wake")) { - data->can_wake = true; + if (data->can_wake) { /* This IRQ chip can wake the system, set all child interrupts * in wake_enabled mask */ diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index f2d269bca789..bc48b7dc89ec 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -239,7 +239,7 @@ int gic_get_c0_compare_int(void) int gic_get_c0_perfcount_int(void) { if (!gic_local_irq_is_routable(GIC_LOCAL_INT_PERFCTR)) { - /* Is the erformance counter shared with the timer? */ + /* Is the performance counter shared with the timer? */ if (cp0_perfcount_irq < 0) return -1; return MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; @@ -248,6 +248,29 @@ int gic_get_c0_perfcount_int(void) GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_PERFCTR)); } +int gic_get_c0_fdc_int(void) +{ + if (!gic_local_irq_is_routable(GIC_LOCAL_INT_FDC)) { + /* Is the FDC IRQ even present? */ + if (cp0_fdc_irq < 0) + return -1; + return MIPS_CPU_IRQ_BASE + cp0_fdc_irq; + } + + /* + * Some cores claim the FDC is routable but it doesn't actually seem to + * be connected. + */ + switch (current_cpu_type()) { + case CPU_INTERAPTIV: + case CPU_PROAPTIV: + return -1; + } + + return irq_create_mapping(gic_irq_domain, + GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_FDC)); +} + static void gic_handle_shared_int(void) { unsigned int i, intr, virq; @@ -613,15 +636,20 @@ static int gic_local_irq_domain_map(struct irq_domain *d, unsigned int virq, * of the MIPS kernel code does not use the percpu IRQ API for * the CP0 timer and performance counter interrupts. */ - if (intr != GIC_LOCAL_INT_TIMER && intr != GIC_LOCAL_INT_PERFCTR) { + switch (intr) { + case GIC_LOCAL_INT_TIMER: + case GIC_LOCAL_INT_PERFCTR: + case GIC_LOCAL_INT_FDC: + irq_set_chip_and_handler(virq, + &gic_all_vpes_local_irq_controller, + handle_percpu_irq); + break; + default: irq_set_chip_and_handler(virq, &gic_local_irq_controller, handle_percpu_devid_irq); irq_set_percpu_devid(virq); - } else { - irq_set_chip_and_handler(virq, - &gic_all_vpes_local_irq_controller, - handle_percpu_irq); + break; } spin_lock_irqsave(&gic_lock, flags); |