diff options
Diffstat (limited to 'drivers/irqchip/irq-sirfsoc.c')
| -rw-r--r-- | drivers/irqchip/irq-sirfsoc.c | 126 | 
1 files changed, 126 insertions, 0 deletions
| diff --git a/drivers/irqchip/irq-sirfsoc.c b/drivers/irqchip/irq-sirfsoc.c new file mode 100644 index 000000000000..69ea44ebcf61 --- /dev/null +++ b/drivers/irqchip/irq-sirfsoc.c @@ -0,0 +1,126 @@ +/* + * interrupt controller support for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/irqdomain.h> +#include <linux/syscore_ops.h> +#include <asm/mach/irq.h> +#include <asm/exception.h> +#include "irqchip.h" + +#define SIRFSOC_INT_RISC_MASK0          0x0018 +#define SIRFSOC_INT_RISC_MASK1          0x001C +#define SIRFSOC_INT_RISC_LEVEL0         0x0020 +#define SIRFSOC_INT_RISC_LEVEL1         0x0024 +#define SIRFSOC_INIT_IRQ_ID		0x0038 + +#define SIRFSOC_NUM_IRQS		128 + +static struct irq_domain *sirfsoc_irqdomain; + +static __init void +sirfsoc_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) +{ +	struct irq_chip_generic *gc; +	struct irq_chip_type *ct; + +	gc = irq_alloc_generic_chip("SIRFINTC", 1, irq_start, base, handle_level_irq); +	ct = gc->chip_types; + +	ct->chip.irq_mask = irq_gc_mask_clr_bit; +	ct->chip.irq_unmask = irq_gc_mask_set_bit; +	ct->regs.mask = SIRFSOC_INT_RISC_MASK0; + +	irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, 0); +} + +static asmlinkage void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs) +{ +	void __iomem *base = sirfsoc_irqdomain->host_data; +	u32 irqstat, irqnr; + +	irqstat = readl_relaxed(base + SIRFSOC_INIT_IRQ_ID); +	irqnr = irq_find_mapping(sirfsoc_irqdomain, irqstat & 0xff); + +	handle_IRQ(irqnr, regs); +} + +static int __init sirfsoc_irq_init(struct device_node *np, struct device_node *parent) +{ +	void __iomem *base = of_iomap(np, 0); +	if (!base) +		panic("unable to map intc cpu registers\n"); + +	/* using legacy because irqchip_generic does not work with linear */ +	sirfsoc_irqdomain = irq_domain_add_legacy(np, SIRFSOC_NUM_IRQS, 0, 0, +				 &irq_domain_simple_ops, base); + +	sirfsoc_alloc_gc(base, 0, 32); +	sirfsoc_alloc_gc(base + 4, 32, SIRFSOC_NUM_IRQS - 32); + +	writel_relaxed(0, base + SIRFSOC_INT_RISC_LEVEL0); +	writel_relaxed(0, base + SIRFSOC_INT_RISC_LEVEL1); + +	writel_relaxed(0, base + SIRFSOC_INT_RISC_MASK0); +	writel_relaxed(0, base + SIRFSOC_INT_RISC_MASK1); + +	set_handle_irq(sirfsoc_handle_irq); + +	return 0; +} +IRQCHIP_DECLARE(sirfsoc_intc, "sirf,prima2-intc", sirfsoc_irq_init); + +struct sirfsoc_irq_status { +	u32 mask0; +	u32 mask1; +	u32 level0; +	u32 level1; +}; + +static struct sirfsoc_irq_status sirfsoc_irq_st; + +static int sirfsoc_irq_suspend(void) +{ +	void __iomem *base = sirfsoc_irqdomain->host_data; + +	sirfsoc_irq_st.mask0 = readl_relaxed(base + SIRFSOC_INT_RISC_MASK0); +	sirfsoc_irq_st.mask1 = readl_relaxed(base + SIRFSOC_INT_RISC_MASK1); +	sirfsoc_irq_st.level0 = readl_relaxed(base + SIRFSOC_INT_RISC_LEVEL0); +	sirfsoc_irq_st.level1 = readl_relaxed(base + SIRFSOC_INT_RISC_LEVEL1); + +	return 0; +} + +static void sirfsoc_irq_resume(void) +{ +	void __iomem *base = sirfsoc_irqdomain->host_data; + +	writel_relaxed(sirfsoc_irq_st.mask0, base + SIRFSOC_INT_RISC_MASK0); +	writel_relaxed(sirfsoc_irq_st.mask1, base + SIRFSOC_INT_RISC_MASK1); +	writel_relaxed(sirfsoc_irq_st.level0, base + SIRFSOC_INT_RISC_LEVEL0); +	writel_relaxed(sirfsoc_irq_st.level1, base + SIRFSOC_INT_RISC_LEVEL1); +} + +static struct syscore_ops sirfsoc_irq_syscore_ops = { +	.suspend	= sirfsoc_irq_suspend, +	.resume		= sirfsoc_irq_resume, +}; + +static int __init sirfsoc_irq_pm_init(void) +{ +	if (!sirfsoc_irqdomain) +		return 0; + +	register_syscore_ops(&sirfsoc_irq_syscore_ops); +	return 0; +} +device_initcall(sirfsoc_irq_pm_init); |