18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * linux/arch/arm/mach-omap2/irq.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Interrupt handler for OMAP2 boards.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2005 Nokia Corporation
78c2ecf20Sopenharmony_ci * Author: Paul Mundt <paul.mundt@nokia.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
108c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive
118c2ecf20Sopenharmony_ci * for more details.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/io.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <asm/exception.h>
208c2ecf20Sopenharmony_ci#include <linux/irqchip.h>
218c2ecf20Sopenharmony_ci#include <linux/irqdomain.h>
228c2ecf20Sopenharmony_ci#include <linux/of.h>
238c2ecf20Sopenharmony_ci#include <linux/of_address.h>
248c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <linux/irqchip/irq-omap-intc.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* selected INTC register offsets */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define INTC_REVISION		0x0000
318c2ecf20Sopenharmony_ci#define INTC_SYSCONFIG		0x0010
328c2ecf20Sopenharmony_ci#define INTC_SYSSTATUS		0x0014
338c2ecf20Sopenharmony_ci#define INTC_SIR		0x0040
348c2ecf20Sopenharmony_ci#define INTC_CONTROL		0x0048
358c2ecf20Sopenharmony_ci#define INTC_PROTECTION		0x004C
368c2ecf20Sopenharmony_ci#define INTC_IDLE		0x0050
378c2ecf20Sopenharmony_ci#define INTC_THRESHOLD		0x0068
388c2ecf20Sopenharmony_ci#define INTC_MIR0		0x0084
398c2ecf20Sopenharmony_ci#define INTC_MIR_CLEAR0		0x0088
408c2ecf20Sopenharmony_ci#define INTC_MIR_SET0		0x008c
418c2ecf20Sopenharmony_ci#define INTC_PENDING_IRQ0	0x0098
428c2ecf20Sopenharmony_ci#define INTC_PENDING_IRQ1	0x00b8
438c2ecf20Sopenharmony_ci#define INTC_PENDING_IRQ2	0x00d8
448c2ecf20Sopenharmony_ci#define INTC_PENDING_IRQ3	0x00f8
458c2ecf20Sopenharmony_ci#define INTC_ILR0		0x0100
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define ACTIVEIRQ_MASK		0x7f	/* omap2/3 active interrupt bits */
488c2ecf20Sopenharmony_ci#define SPURIOUSIRQ_MASK	(0x1ffffff << 7)
498c2ecf20Sopenharmony_ci#define INTCPS_NR_ILR_REGS	128
508c2ecf20Sopenharmony_ci#define INTCPS_NR_MIR_REGS	4
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define INTC_IDLE_FUNCIDLE	(1 << 0)
538c2ecf20Sopenharmony_ci#define INTC_IDLE_TURBO		(1 << 1)
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define INTC_PROTECTION_ENABLE	(1 << 0)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistruct omap_intc_regs {
588c2ecf20Sopenharmony_ci	u32 sysconfig;
598c2ecf20Sopenharmony_ci	u32 protection;
608c2ecf20Sopenharmony_ci	u32 idle;
618c2ecf20Sopenharmony_ci	u32 threshold;
628c2ecf20Sopenharmony_ci	u32 ilr[INTCPS_NR_ILR_REGS];
638c2ecf20Sopenharmony_ci	u32 mir[INTCPS_NR_MIR_REGS];
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_cistatic struct omap_intc_regs intc_context;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic struct irq_domain *domain;
688c2ecf20Sopenharmony_cistatic void __iomem *omap_irq_base;
698c2ecf20Sopenharmony_cistatic int omap_nr_pending;
708c2ecf20Sopenharmony_cistatic int omap_nr_irqs;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic void intc_writel(u32 reg, u32 val)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	writel_relaxed(val, omap_irq_base + reg);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic u32 intc_readl(u32 reg)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	return readl_relaxed(omap_irq_base + reg);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_civoid omap_intc_save_context(void)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	int i;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	intc_context.sysconfig =
878c2ecf20Sopenharmony_ci		intc_readl(INTC_SYSCONFIG);
888c2ecf20Sopenharmony_ci	intc_context.protection =
898c2ecf20Sopenharmony_ci		intc_readl(INTC_PROTECTION);
908c2ecf20Sopenharmony_ci	intc_context.idle =
918c2ecf20Sopenharmony_ci		intc_readl(INTC_IDLE);
928c2ecf20Sopenharmony_ci	intc_context.threshold =
938c2ecf20Sopenharmony_ci		intc_readl(INTC_THRESHOLD);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	for (i = 0; i < omap_nr_irqs; i++)
968c2ecf20Sopenharmony_ci		intc_context.ilr[i] =
978c2ecf20Sopenharmony_ci			intc_readl((INTC_ILR0 + 0x4 * i));
988c2ecf20Sopenharmony_ci	for (i = 0; i < INTCPS_NR_MIR_REGS; i++)
998c2ecf20Sopenharmony_ci		intc_context.mir[i] =
1008c2ecf20Sopenharmony_ci			intc_readl(INTC_MIR0 + (0x20 * i));
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_civoid omap_intc_restore_context(void)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	int i;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	intc_writel(INTC_SYSCONFIG, intc_context.sysconfig);
1088c2ecf20Sopenharmony_ci	intc_writel(INTC_PROTECTION, intc_context.protection);
1098c2ecf20Sopenharmony_ci	intc_writel(INTC_IDLE, intc_context.idle);
1108c2ecf20Sopenharmony_ci	intc_writel(INTC_THRESHOLD, intc_context.threshold);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	for (i = 0; i < omap_nr_irqs; i++)
1138c2ecf20Sopenharmony_ci		intc_writel(INTC_ILR0 + 0x4 * i,
1148c2ecf20Sopenharmony_ci				intc_context.ilr[i]);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	for (i = 0; i < INTCPS_NR_MIR_REGS; i++)
1178c2ecf20Sopenharmony_ci		intc_writel(INTC_MIR0 + 0x20 * i,
1188c2ecf20Sopenharmony_ci			intc_context.mir[i]);
1198c2ecf20Sopenharmony_ci	/* MIRs are saved and restore with other PRCM registers */
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_civoid omap3_intc_prepare_idle(void)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	/*
1258c2ecf20Sopenharmony_ci	 * Disable autoidle as it can stall interrupt controller,
1268c2ecf20Sopenharmony_ci	 * cf. errata ID i540 for 3430 (all revisions up to 3.1.x)
1278c2ecf20Sopenharmony_ci	 */
1288c2ecf20Sopenharmony_ci	intc_writel(INTC_SYSCONFIG, 0);
1298c2ecf20Sopenharmony_ci	intc_writel(INTC_IDLE, INTC_IDLE_TURBO);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_civoid omap3_intc_resume_idle(void)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	/* Re-enable autoidle */
1358c2ecf20Sopenharmony_ci	intc_writel(INTC_SYSCONFIG, 1);
1368c2ecf20Sopenharmony_ci	intc_writel(INTC_IDLE, 0);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/* XXX: FIQ and additional INTC support (only MPU at the moment) */
1408c2ecf20Sopenharmony_cistatic void omap_ack_irq(struct irq_data *d)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	intc_writel(INTC_CONTROL, 0x1);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic void omap_mask_ack_irq(struct irq_data *d)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	irq_gc_mask_disable_reg(d);
1488c2ecf20Sopenharmony_ci	omap_ack_irq(d);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic void __init omap_irq_soft_reset(void)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	unsigned long tmp;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	tmp = intc_readl(INTC_REVISION) & 0xff;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	pr_info("IRQ: Found an INTC at 0x%p (revision %ld.%ld) with %d interrupts\n",
1588c2ecf20Sopenharmony_ci		omap_irq_base, tmp >> 4, tmp & 0xf, omap_nr_irqs);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	tmp = intc_readl(INTC_SYSCONFIG);
1618c2ecf20Sopenharmony_ci	tmp |= 1 << 1;	/* soft reset */
1628c2ecf20Sopenharmony_ci	intc_writel(INTC_SYSCONFIG, tmp);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	while (!(intc_readl(INTC_SYSSTATUS) & 0x1))
1658c2ecf20Sopenharmony_ci		/* Wait for reset to complete */;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* Enable autoidle */
1688c2ecf20Sopenharmony_ci	intc_writel(INTC_SYSCONFIG, 1 << 0);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ciint omap_irq_pending(void)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	int i;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	for (i = 0; i < omap_nr_pending; i++)
1768c2ecf20Sopenharmony_ci		if (intc_readl(INTC_PENDING_IRQ0 + (0x20 * i)))
1778c2ecf20Sopenharmony_ci			return 1;
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_civoid omap3_intc_suspend(void)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	/* A pending interrupt would prevent OMAP from entering suspend */
1848c2ecf20Sopenharmony_ci	omap_ack_irq(NULL);
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic int __init omap_alloc_gc_of(struct irq_domain *d, void __iomem *base)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	int ret;
1908c2ecf20Sopenharmony_ci	int i;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	ret = irq_alloc_domain_generic_chips(d, 32, 1, "INTC",
1938c2ecf20Sopenharmony_ci			handle_level_irq, IRQ_NOREQUEST | IRQ_NOPROBE,
1948c2ecf20Sopenharmony_ci			IRQ_LEVEL, 0);
1958c2ecf20Sopenharmony_ci	if (ret) {
1968c2ecf20Sopenharmony_ci		pr_warn("Failed to allocate irq chips\n");
1978c2ecf20Sopenharmony_ci		return ret;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	for (i = 0; i < omap_nr_pending; i++) {
2018c2ecf20Sopenharmony_ci		struct irq_chip_generic *gc;
2028c2ecf20Sopenharmony_ci		struct irq_chip_type *ct;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		gc = irq_get_domain_generic_chip(d, 32 * i);
2058c2ecf20Sopenharmony_ci		gc->reg_base = base;
2068c2ecf20Sopenharmony_ci		ct = gc->chip_types;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci		ct->type = IRQ_TYPE_LEVEL_MASK;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		ct->chip.irq_ack = omap_mask_ack_irq;
2118c2ecf20Sopenharmony_ci		ct->chip.irq_mask = irq_gc_mask_disable_reg;
2128c2ecf20Sopenharmony_ci		ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		ct->chip.flags |= IRQCHIP_SKIP_SET_WAKE;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		ct->regs.enable = INTC_MIR_CLEAR0 + 32 * i;
2178c2ecf20Sopenharmony_ci		ct->regs.disable = INTC_MIR_SET0 + 32 * i;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	return 0;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic void __init omap_alloc_gc_legacy(void __iomem *base,
2248c2ecf20Sopenharmony_ci		unsigned int irq_start, unsigned int num)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct irq_chip_generic *gc;
2278c2ecf20Sopenharmony_ci	struct irq_chip_type *ct;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	gc = irq_alloc_generic_chip("INTC", 1, irq_start, base,
2308c2ecf20Sopenharmony_ci			handle_level_irq);
2318c2ecf20Sopenharmony_ci	ct = gc->chip_types;
2328c2ecf20Sopenharmony_ci	ct->chip.irq_ack = omap_mask_ack_irq;
2338c2ecf20Sopenharmony_ci	ct->chip.irq_mask = irq_gc_mask_disable_reg;
2348c2ecf20Sopenharmony_ci	ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
2358c2ecf20Sopenharmony_ci	ct->chip.flags |= IRQCHIP_SKIP_SET_WAKE;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	ct->regs.enable = INTC_MIR_CLEAR0;
2388c2ecf20Sopenharmony_ci	ct->regs.disable = INTC_MIR_SET0;
2398c2ecf20Sopenharmony_ci	irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
2408c2ecf20Sopenharmony_ci			IRQ_NOREQUEST | IRQ_NOPROBE, 0);
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int __init omap_init_irq_of(struct device_node *node)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	int ret;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	omap_irq_base = of_iomap(node, 0);
2488c2ecf20Sopenharmony_ci	if (WARN_ON(!omap_irq_base))
2498c2ecf20Sopenharmony_ci		return -ENOMEM;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	domain = irq_domain_add_linear(node, omap_nr_irqs,
2528c2ecf20Sopenharmony_ci			&irq_generic_chip_ops, NULL);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	omap_irq_soft_reset();
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	ret = omap_alloc_gc_of(domain, omap_irq_base);
2578c2ecf20Sopenharmony_ci	if (ret < 0)
2588c2ecf20Sopenharmony_ci		irq_domain_remove(domain);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return ret;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic int __init omap_init_irq_legacy(u32 base, struct device_node *node)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	int j, irq_base;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	omap_irq_base = ioremap(base, SZ_4K);
2688c2ecf20Sopenharmony_ci	if (WARN_ON(!omap_irq_base))
2698c2ecf20Sopenharmony_ci		return -ENOMEM;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	irq_base = irq_alloc_descs(-1, 0, omap_nr_irqs, 0);
2728c2ecf20Sopenharmony_ci	if (irq_base < 0) {
2738c2ecf20Sopenharmony_ci		pr_warn("Couldn't allocate IRQ numbers\n");
2748c2ecf20Sopenharmony_ci		irq_base = 0;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	domain = irq_domain_add_legacy(node, omap_nr_irqs, irq_base, 0,
2788c2ecf20Sopenharmony_ci			&irq_domain_simple_ops, NULL);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	omap_irq_soft_reset();
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	for (j = 0; j < omap_nr_irqs; j += 32)
2838c2ecf20Sopenharmony_ci		omap_alloc_gc_legacy(omap_irq_base + j, j + irq_base, 32);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic void __init omap_irq_enable_protection(void)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	u32 reg;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	reg = intc_readl(INTC_PROTECTION);
2938c2ecf20Sopenharmony_ci	reg |= INTC_PROTECTION_ENABLE;
2948c2ecf20Sopenharmony_ci	intc_writel(INTC_PROTECTION, reg);
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int __init omap_init_irq(u32 base, struct device_node *node)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	int ret;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/*
3028c2ecf20Sopenharmony_ci	 * FIXME legacy OMAP DMA driver sitting under arch/arm/plat-omap/dma.c
3038c2ecf20Sopenharmony_ci	 * depends is still not ready for linear IRQ domains; because of that
3048c2ecf20Sopenharmony_ci	 * we need to temporarily "blacklist" OMAP2 and OMAP3 devices from using
3058c2ecf20Sopenharmony_ci	 * linear IRQ Domain until that driver is finally fixed.
3068c2ecf20Sopenharmony_ci	 */
3078c2ecf20Sopenharmony_ci	if (of_device_is_compatible(node, "ti,omap2-intc") ||
3088c2ecf20Sopenharmony_ci			of_device_is_compatible(node, "ti,omap3-intc")) {
3098c2ecf20Sopenharmony_ci		struct resource res;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		if (of_address_to_resource(node, 0, &res))
3128c2ecf20Sopenharmony_ci			return -ENOMEM;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci		base = res.start;
3158c2ecf20Sopenharmony_ci		ret = omap_init_irq_legacy(base, node);
3168c2ecf20Sopenharmony_ci	} else if (node) {
3178c2ecf20Sopenharmony_ci		ret = omap_init_irq_of(node);
3188c2ecf20Sopenharmony_ci	} else {
3198c2ecf20Sopenharmony_ci		ret = omap_init_irq_legacy(base, NULL);
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (ret == 0)
3238c2ecf20Sopenharmony_ci		omap_irq_enable_protection();
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	return ret;
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic asmlinkage void __exception_irq_entry
3298c2ecf20Sopenharmony_ciomap_intc_handle_irq(struct pt_regs *regs)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	extern unsigned long irq_err_count;
3328c2ecf20Sopenharmony_ci	u32 irqnr;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	irqnr = intc_readl(INTC_SIR);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/*
3378c2ecf20Sopenharmony_ci	 * A spurious IRQ can result if interrupt that triggered the
3388c2ecf20Sopenharmony_ci	 * sorting is no longer active during the sorting (10 INTC
3398c2ecf20Sopenharmony_ci	 * functional clock cycles after interrupt assertion). Or a
3408c2ecf20Sopenharmony_ci	 * change in interrupt mask affected the result during sorting
3418c2ecf20Sopenharmony_ci	 * time. There is no special handling required except ignoring
3428c2ecf20Sopenharmony_ci	 * the SIR register value just read and retrying.
3438c2ecf20Sopenharmony_ci	 * See section 6.2.5 of AM335x TRM Literature Number: SPRUH73K
3448c2ecf20Sopenharmony_ci	 *
3458c2ecf20Sopenharmony_ci	 * Many a times, a spurious interrupt situation has been fixed
3468c2ecf20Sopenharmony_ci	 * by adding a flush for the posted write acking the IRQ in
3478c2ecf20Sopenharmony_ci	 * the device driver. Typically, this is going be the device
3488c2ecf20Sopenharmony_ci	 * driver whose interrupt was handled just before the spurious
3498c2ecf20Sopenharmony_ci	 * IRQ occurred. Pay attention to those device drivers if you
3508c2ecf20Sopenharmony_ci	 * run into hitting the spurious IRQ condition below.
3518c2ecf20Sopenharmony_ci	 */
3528c2ecf20Sopenharmony_ci	if (unlikely((irqnr & SPURIOUSIRQ_MASK) == SPURIOUSIRQ_MASK)) {
3538c2ecf20Sopenharmony_ci		pr_err_once("%s: spurious irq!\n", __func__);
3548c2ecf20Sopenharmony_ci		irq_err_count++;
3558c2ecf20Sopenharmony_ci		omap_ack_irq(NULL);
3568c2ecf20Sopenharmony_ci		return;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	irqnr &= ACTIVEIRQ_MASK;
3608c2ecf20Sopenharmony_ci	handle_domain_irq(domain, irqnr, regs);
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic int __init intc_of_init(struct device_node *node,
3648c2ecf20Sopenharmony_ci			     struct device_node *parent)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	int ret;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	omap_nr_pending = 3;
3698c2ecf20Sopenharmony_ci	omap_nr_irqs = 96;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (WARN_ON(!node))
3728c2ecf20Sopenharmony_ci		return -ENODEV;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	if (of_device_is_compatible(node, "ti,dm814-intc") ||
3758c2ecf20Sopenharmony_ci	    of_device_is_compatible(node, "ti,dm816-intc") ||
3768c2ecf20Sopenharmony_ci	    of_device_is_compatible(node, "ti,am33xx-intc")) {
3778c2ecf20Sopenharmony_ci		omap_nr_irqs = 128;
3788c2ecf20Sopenharmony_ci		omap_nr_pending = 4;
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	ret = omap_init_irq(-1, of_node_get(node));
3828c2ecf20Sopenharmony_ci	if (ret < 0)
3838c2ecf20Sopenharmony_ci		return ret;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	set_handle_irq(omap_intc_handle_irq);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	return 0;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(omap2_intc, "ti,omap2-intc", intc_of_init);
3918c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(omap3_intc, "ti,omap3-intc", intc_of_init);
3928c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(dm814x_intc, "ti,dm814-intc", intc_of_init);
3938c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(dm816x_intc, "ti,dm816-intc", intc_of_init);
3948c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(am33xx_intc, "ti,am33xx-intc", intc_of_init);
395