diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-11 17:56:37 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-11 17:56:37 -0800 |
commit | c0222ac086669a631814bbf857f8c8023452a4d7 (patch) | |
tree | bb1d9908031fcf69016eeefa7b35a4f68f414333 /arch/mips/bcm3384/irq.c | |
parent | 140cd7fb04a4a2bc09a30980bc8104cc89e09330 (diff) | |
parent | e2965cd0003f222bd49f67907c2bc6ed691c6d20 (diff) | |
download | linux-c0222ac086669a631814bbf857f8c8023452a4d7.tar.bz2 |
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
Pull MIPS updates from Ralf Baechle:
"This is an unusually large pull request for MIPS - in parts because
lots of patches missed the 3.18 deadline but primarily because some
folks opened the flood gates.
- Retire the MIPS-specific phys_t with the generic phys_addr_t.
- Improvments for the backtrace code used by oprofile.
- Better backtraces on SMP systems.
- Cleanups for the Octeon platform code.
- Cleanups and fixes for the Loongson platform code.
- Cleanups and fixes to the firmware library.
- Switch ATH79 platform to use the firmware library.
- Grand overhault to the SEAD3 and Malta interrupt code.
- Move the GIC interrupt code to drivers/irqchip
- Lots of GIC cleanups and updates to the GIC code to use modern IRQ
infrastructures and features of the kernel.
- OF documentation updates for the GIC bindings
- Move GIC clocksource driver to drivers/clocksource
- Merge GIC clocksource driver with clockevent driver.
- Further updates to bring the GIC clocksource driver up to date.
- R3000 TLB code cleanups
- Improvments to the Loongson 3 platform code.
- Convert pr_warning to pr_warn.
- Merge a bunch of small lantiq and ralink fixes that have been
staged/lingering inside the openwrt tree for a while.
- Update archhelp for IP22/IP32
- Fix a number of issues for Loongson 1B.
- New clocksource and clockevent driver for Loongson 1B.
- Further work on clk handling for Loongson 1B.
- Platform work for Broadcom BMIPS.
- Error handling cleanups for TurboChannel.
- Fixes and optimization to the microMIPS support.
- Option to disable the FTLB.
- Dump more relevant information on machine check exception
- Change binfmt to allow arch to examine PT_*PROC headers
- Support for new style FPU register model in O32
- VDSO randomization.
- BCM47xx cleanups
- BCM47xx reimplement the way the kernel accesses NVRAM information.
- Random cleanups
- Add support for ATH25 platforms
- Remove pointless locking code in some PCI platforms.
- Some improvments to EVA support
- Minor Alchemy cleanup"
* 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus: (185 commits)
MIPS: Add MFHC0 and MTHC0 instructions to uasm.
MIPS: Cosmetic cleanups of page table headers.
MIPS: Add CP0 macros for extended EntryLo registers
MIPS: Remove now unused definition of phys_t.
MIPS: Replace use of phys_t with phys_addr_t.
MIPS: Replace MIPS-specific 64BIT_PHYS_ADDR with generic PHYS_ADDR_T_64BIT
PCMCIA: Alchemy Don't select 64BIT_PHYS_ADDR in Kconfig.
MIPS: lib: memset: Clean up some MIPS{EL,EB} ifdefery
MIPS: iomap: Use __mem_{read,write}{b,w,l} for MMIO
MIPS: <asm/types.h> fix indentation.
MAINTAINERS: Add entry for BMIPS multiplatform kernel
MIPS: Enable VDSO randomization
MIPS: Remove a temporary hack for debugging cache flushes in SMTC configuration
MIPS: Remove declaration of obsolete arch_init_clk_ops()
MIPS: atomic.h: Reformat to fit in 79 columns
MIPS: Apply `.insn' to fixup labels throughout
MIPS: Fix microMIPS LL/SC immediate offsets
MIPS: Kconfig: Only allow 32-bit microMIPS builds
MIPS: signal.c: Fix an invalid cast in ISA mode bit handling
MIPS: mm: Only build one microassembler that is suitable
...
Diffstat (limited to 'arch/mips/bcm3384/irq.c')
-rw-r--r-- | arch/mips/bcm3384/irq.c | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/arch/mips/bcm3384/irq.c b/arch/mips/bcm3384/irq.c new file mode 100644 index 000000000000..0fb5134fb832 --- /dev/null +++ b/arch/mips/bcm3384/irq.c @@ -0,0 +1,193 @@ +/* + * 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. + * + * Partially based on arch/mips/ralink/irq.c + * + * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2013 John Crispin <blogic@openwrt.org> + * Copyright (C) 2014 Kevin Cernekee <cernekee@gmail.com> + */ + +#include <linux/io.h> +#include <linux/bitops.h> +#include <linux/of_platform.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/irqdomain.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <asm/bmips.h> +#include <asm/irq_cpu.h> +#include <asm/mipsregs.h> + +/* INTC register offsets */ +#define INTC_REG_ENABLE 0x00 +#define INTC_REG_STATUS 0x04 + +#define MAX_WORDS 2 +#define IRQS_PER_WORD 32 + +struct bcm3384_intc { + int n_words; + void __iomem *reg[MAX_WORDS]; + u32 enable[MAX_WORDS]; + spinlock_t lock; +}; + +static void bcm3384_intc_irq_unmask(struct irq_data *d) +{ + struct bcm3384_intc *priv = d->domain->host_data; + unsigned long flags; + int idx = d->hwirq / IRQS_PER_WORD; + int bit = d->hwirq % IRQS_PER_WORD; + + spin_lock_irqsave(&priv->lock, flags); + priv->enable[idx] |= BIT(bit); + __raw_writel(priv->enable[idx], priv->reg[idx] + INTC_REG_ENABLE); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void bcm3384_intc_irq_mask(struct irq_data *d) +{ + struct bcm3384_intc *priv = d->domain->host_data; + unsigned long flags; + int idx = d->hwirq / IRQS_PER_WORD; + int bit = d->hwirq % IRQS_PER_WORD; + + spin_lock_irqsave(&priv->lock, flags); + priv->enable[idx] &= ~BIT(bit); + __raw_writel(priv->enable[idx], priv->reg[idx] + INTC_REG_ENABLE); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static struct irq_chip bcm3384_intc_irq_chip = { + .name = "INTC", + .irq_unmask = bcm3384_intc_irq_unmask, + .irq_mask = bcm3384_intc_irq_mask, + .irq_mask_ack = bcm3384_intc_irq_mask, +}; + +unsigned int get_c0_compare_int(void) +{ + return CP0_LEGACY_COMPARE_IRQ; +} + +static void bcm3384_intc_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct irq_domain *domain = irq_get_handler_data(irq); + struct bcm3384_intc *priv = domain->host_data; + unsigned long flags; + unsigned int idx; + + for (idx = 0; idx < priv->n_words; idx++) { + unsigned long pending; + int hwirq; + + spin_lock_irqsave(&priv->lock, flags); + pending = __raw_readl(priv->reg[idx] + INTC_REG_STATUS) & + priv->enable[idx]; + spin_unlock_irqrestore(&priv->lock, flags); + + for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) { + generic_handle_irq(irq_find_mapping(domain, + hwirq + idx * IRQS_PER_WORD)); + } + } +} + +asmlinkage void plat_irq_dispatch(void) +{ + unsigned long pending = + (read_c0_status() & read_c0_cause() & ST0_IM) >> STATUSB_IP0; + int bit; + + for_each_set_bit(bit, &pending, 8) + do_IRQ(MIPS_CPU_IRQ_BASE + bit); +} + +static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) +{ + irq_set_chip_and_handler(irq, &bcm3384_intc_irq_chip, handle_level_irq); + return 0; +} + +static const struct irq_domain_ops irq_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = intc_map, +}; + +static int __init ioremap_one_pair(struct bcm3384_intc *priv, + struct device_node *node, + int idx) +{ + struct resource res; + + if (of_address_to_resource(node, idx, &res)) + return 0; + + if (request_mem_region(res.start, resource_size(&res), + res.name) < 0) + pr_err("Failed to request INTC register region\n"); + + priv->reg[idx] = ioremap_nocache(res.start, resource_size(&res)); + if (!priv->reg[idx]) + panic("Failed to ioremap INTC register range"); + + /* start up with everything masked before we hook the parent IRQ */ + __raw_writel(0, priv->reg[idx] + INTC_REG_ENABLE); + priv->enable[idx] = 0; + + return IRQS_PER_WORD; +} + +static int __init intc_of_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_domain *domain; + unsigned int parent_irq, n_irqs = 0; + struct bcm3384_intc *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + panic("Failed to allocate bcm3384_intc struct"); + + spin_lock_init(&priv->lock); + + parent_irq = irq_of_parse_and_map(node, 0); + if (!parent_irq) + panic("Failed to get INTC IRQ"); + + n_irqs += ioremap_one_pair(priv, node, 0); + n_irqs += ioremap_one_pair(priv, node, 1); + + if (!n_irqs) + panic("Failed to map INTC registers"); + + priv->n_words = n_irqs / IRQS_PER_WORD; + domain = irq_domain_add_linear(node, n_irqs, &irq_domain_ops, priv); + if (!domain) + panic("Failed to add irqdomain"); + + irq_set_chained_handler(parent_irq, bcm3384_intc_irq_handler); + irq_set_handler_data(parent_irq, domain); + + return 0; +} + +static struct of_device_id of_irq_ids[] __initdata = { + { .compatible = "mti,cpu-interrupt-controller", + .data = mips_cpu_intc_init }, + { .compatible = "brcm,bcm3384-intc", + .data = intc_of_init }, + {}, +}; + +void __init arch_init_irq(void) +{ + bmips_tp1_irqs = 0; + of_irq_init(of_irq_ids); +} |