aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/irqchip
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-07-02 19:14:35 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-07-02 19:14:35 -0400
commita4883ef6af5e513a1e8c2ab9aab721604aa3a4f5 (patch)
treee893f951d150c1d760f46040483193a3ac713a4e /drivers/irqchip
parentab3d681e9d41816f90836ea8fe235168d973207f (diff)
parentd2e08473f2488d53a71c2f53455f934ec6c44c53 (diff)
Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull core irq changes from Ingo Molnar: "The main changes: - generic-irqchip driver additions, cleanups and fixes - 3 new irqchip drivers: ARMv7-M NVIC, TB10x and Marvell Orion SoCs - irq_get_trigger_type() simplification and cross-arch cleanup - various cleanups, simplifications - documentation updates" * 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (26 commits) softirq: Use _RET_IP_ genirq: Add the generic chip to the genirq docbook genirq: generic-chip: Export some irq_gc_ functions genirq: Fix can_request_irq() for IRQs without an action irqchip: exynos-combiner: Staticize combiner_init irqchip: Add support for ARMv7-M NVIC irqchip: Add TB10x interrupt controller driver irqdomain: Use irq_get_trigger_type() to get IRQ flags MIPS: octeon: Use irq_get_trigger_type() to get IRQ flags arm: orion: Use irq_get_trigger_type() to get IRQ flags mfd: stmpe: use irq_get_trigger_type() to get IRQ flags mfd: twl4030-irq: Use irq_get_trigger_type() to get IRQ flags gpio: mvebu: Use irq_get_trigger_type() to get IRQ flags genirq: Add irq_get_trigger_type() to get IRQ flags genirq: Irqchip: document gcflags arg of irq_alloc_domain_generic_chips genirq: Set irq thread to RT priority on creation irqchip: Add support for Marvell Orion SoCs genirq: Add kerneldoc for irq_disable. genirq: irqchip: Add mask to block out invalid irqs genirq: Generic chip: Add linear irq domain support ...
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/Kconfig15
-rw-r--r--drivers/irqchip/Makefile3
-rw-r--r--drivers/irqchip/exynos-combiner.c8
-rw-r--r--drivers/irqchip/irq-nvic.c117
-rw-r--r--drivers/irqchip/irq-orion.c192
-rw-r--r--drivers/irqchip/irq-tb10x.c195
6 files changed, 526 insertions, 4 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 4a33351c25dc..1fea003ed33f 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -10,6 +10,11 @@ config ARM_GIC
10config GIC_NON_BANKED 10config GIC_NON_BANKED
11 bool 11 bool
12 12
13config ARM_NVIC
14 bool
15 select IRQ_DOMAIN
16 select GENERIC_IRQ_CHIP
17
13config ARM_VIC 18config ARM_VIC
14 bool 19 bool
15 select IRQ_DOMAIN 20 select IRQ_DOMAIN
@@ -25,6 +30,11 @@ config ARM_VIC_NR
25 The maximum number of VICs available in the system, for 30 The maximum number of VICs available in the system, for
26 power management. 31 power management.
27 32
33config ORION_IRQCHIP
34 bool
35 select IRQ_DOMAIN
36 select MULTI_IRQ_HANDLER
37
28config RENESAS_INTC_IRQPIN 38config RENESAS_INTC_IRQPIN
29 bool 39 bool
30 select IRQ_DOMAIN 40 select IRQ_DOMAIN
@@ -33,6 +43,11 @@ config RENESAS_IRQC
33 bool 43 bool
34 select IRQ_DOMAIN 44 select IRQ_DOMAIN
35 45
46config TB10X_IRQC
47 bool
48 select IRQ_DOMAIN
49 select GENERIC_IRQ_CHIP
50
36config VERSATILE_FPGA_IRQ 51config VERSATILE_FPGA_IRQ
37 bool 52 bool
38 select IRQ_DOMAIN 53 select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index cda4cb5f7327..2065ef6a949c 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -7,12 +7,15 @@ obj-$(CONFIG_ARCH_MXS) += irq-mxs.o
7obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o 7obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
8obj-$(CONFIG_METAG) += irq-metag-ext.o 8obj-$(CONFIG_METAG) += irq-metag-ext.o
9obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o 9obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
10obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
10obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o 11obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
11obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o 12obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
12obj-$(CONFIG_ARM_GIC) += irq-gic.o 13obj-$(CONFIG_ARM_GIC) += irq-gic.o
14obj-$(CONFIG_ARM_NVIC) += irq-nvic.o
13obj-$(CONFIG_ARM_VIC) += irq-vic.o 15obj-$(CONFIG_ARM_VIC) += irq-vic.o
14obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o 16obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o
15obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o 17obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
16obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o 18obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
17obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o 19obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
18obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o 20obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
21obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c
index a9d2b2fa4afd..4c6826513901 100644
--- a/drivers/irqchip/exynos-combiner.c
+++ b/drivers/irqchip/exynos-combiner.c
@@ -204,10 +204,10 @@ static unsigned int combiner_lookup_irq(int group)
204 return 0; 204 return 0;
205} 205}
206 206
207void __init combiner_init(void __iomem *combiner_base, 207static void __init combiner_init(void __iomem *combiner_base,
208 struct device_node *np, 208 struct device_node *np,
209 unsigned int max_nr, 209 unsigned int max_nr,
210 int irq_base) 210 int irq_base)
211{ 211{
212 int i, irq; 212 int i, irq;
213 unsigned int nr_irq; 213 unsigned int nr_irq;
diff --git a/drivers/irqchip/irq-nvic.c b/drivers/irqchip/irq-nvic.c
new file mode 100644
index 000000000000..8d0c8b3181c5
--- /dev/null
+++ b/drivers/irqchip/irq-nvic.c
@@ -0,0 +1,117 @@
1/*
2 * drivers/irq/irq-nvic.c
3 *
4 * Copyright (C) 2008 ARM Limited, All Rights Reserved.
5 * Copyright (C) 2013 Pengutronix
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * Support for the Nested Vectored Interrupt Controller found on the
12 * ARMv7-M CPUs (Cortex-M3/M4)
13 */
14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
16#include <linux/init.h>
17#include <linux/kernel.h>
18#include <linux/slab.h>
19#include <linux/err.h>
20#include <linux/io.h>
21#include <linux/of.h>
22#include <linux/of_address.h>
23#include <linux/irq.h>
24#include <linux/irqdomain.h>
25
26#include <asm/v7m.h>
27#include <asm/exception.h>
28
29#include "irqchip.h"
30
31#define NVIC_ISER 0x000
32#define NVIC_ICER 0x080
33#define NVIC_IPR 0x300
34
35#define NVIC_MAX_BANKS 16
36/*
37 * Each bank handles 32 irqs. Only the 16th (= last) bank handles only
38 * 16 irqs.
39 */
40#define NVIC_MAX_IRQ ((NVIC_MAX_BANKS - 1) * 32 + 16)
41
42static struct irq_domain *nvic_irq_domain;
43
44asmlinkage void __exception_irq_entry
45nvic_handle_irq(irq_hw_number_t hwirq, struct pt_regs *regs)
46{
47 unsigned int irq = irq_linear_revmap(nvic_irq_domain, hwirq);
48
49 handle_IRQ(irq, regs);
50}
51
52static void nvic_eoi(struct irq_data *d)
53{
54 /*
55 * This is a no-op as end of interrupt is signaled by the exception
56 * return sequence.
57 */
58}
59
60static int __init nvic_of_init(struct device_node *node,
61 struct device_node *parent)
62{
63 unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
64 unsigned int irqs, i, ret, numbanks;
65 void __iomem *nvic_base;
66
67 numbanks = (readl_relaxed(V7M_SCS_ICTR) &
68 V7M_SCS_ICTR_INTLINESNUM_MASK) + 1;
69
70 nvic_base = of_iomap(node, 0);
71 if (!nvic_base) {
72 pr_warn("unable to map nvic registers\n");
73 return -ENOMEM;
74 }
75
76 irqs = numbanks * 32;
77 if (irqs > NVIC_MAX_IRQ)
78 irqs = NVIC_MAX_IRQ;
79
80 nvic_irq_domain =
81 irq_domain_add_linear(node, irqs, &irq_generic_chip_ops, NULL);
82 if (!nvic_irq_domain) {
83 pr_warn("Failed to allocate irq domain\n");
84 return -ENOMEM;
85 }
86
87 ret = irq_alloc_domain_generic_chips(nvic_irq_domain, 32, numbanks,
88 "nvic_irq", handle_fasteoi_irq,
89 clr, 0, IRQ_GC_INIT_MASK_CACHE);
90 if (ret) {
91 pr_warn("Failed to allocate irq chips\n");
92 irq_domain_remove(nvic_irq_domain);
93 return ret;
94 }
95
96 for (i = 0; i < numbanks; ++i) {
97 struct irq_chip_generic *gc;
98
99 gc = irq_get_domain_generic_chip(nvic_irq_domain, 32 * i);
100 gc->reg_base = nvic_base + 4 * i;
101 gc->chip_types[0].regs.enable = NVIC_ISER;
102 gc->chip_types[0].regs.disable = NVIC_ICER;
103 gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
104 gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
105 gc->chip_types[0].chip.irq_eoi = nvic_eoi;
106
107 /* disable interrupts */
108 writel_relaxed(~0, gc->reg_base + NVIC_ICER);
109 }
110
111 /* Set priority on all interrupts */
112 for (i = 0; i < irqs; i += 4)
113 writel_relaxed(0, nvic_base + NVIC_IPR + i);
114
115 return 0;
116}
117IRQCHIP_DECLARE(armv7m_nvic, "arm,armv7m-nvic", nvic_of_init);
diff --git a/drivers/irqchip/irq-orion.c b/drivers/irqchip/irq-orion.c
new file mode 100644
index 000000000000..e51d40031884
--- /dev/null
+++ b/drivers/irqchip/irq-orion.c
@@ -0,0 +1,192 @@
1/*
2 * Marvell Orion SoCs IRQ chip driver.
3 *
4 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
5 *
6 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
9 */
10
11#include <linux/io.h>
12#include <linux/irq.h>
13#include <linux/of.h>
14#include <linux/of_address.h>
15#include <linux/of_irq.h>
16#include <asm/exception.h>
17#include <asm/mach/irq.h>
18
19#include "irqchip.h"
20
21/*
22 * Orion SoC main interrupt controller
23 */
24#define ORION_IRQS_PER_CHIP 32
25
26#define ORION_IRQ_CAUSE 0x00
27#define ORION_IRQ_MASK 0x04
28#define ORION_IRQ_FIQ_MASK 0x08
29#define ORION_IRQ_ENDP_MASK 0x0c
30
31static struct irq_domain *orion_irq_domain;
32
33static asmlinkage void
34__exception_irq_entry orion_handle_irq(struct pt_regs *regs)
35{
36 struct irq_domain_chip_generic *dgc = orion_irq_domain->gc;
37 int n, base = 0;
38
39 for (n = 0; n < dgc->num_chips; n++, base += ORION_IRQS_PER_CHIP) {
40 struct irq_chip_generic *gc =
41 irq_get_domain_generic_chip(orion_irq_domain, base);
42 u32 stat = readl_relaxed(gc->reg_base + ORION_IRQ_CAUSE) &
43 gc->mask_cache;
44 while (stat) {
45 u32 hwirq = ffs(stat) - 1;
46 u32 irq = irq_find_mapping(orion_irq_domain,
47 gc->irq_base + hwirq);
48 handle_IRQ(irq, regs);
49 stat &= ~(1 << hwirq);
50 }
51 }
52}
53
54static int __init orion_irq_init(struct device_node *np,
55 struct device_node *parent)
56{
57 unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
58 int n, ret, base, num_chips = 0;
59 struct resource r;
60
61 /* count number of irq chips by valid reg addresses */
62 while (of_address_to_resource(np, num_chips, &r) == 0)
63 num_chips++;
64
65 orion_irq_domain = irq_domain_add_linear(np,
66 num_chips * ORION_IRQS_PER_CHIP,
67 &irq_generic_chip_ops, NULL);
68 if (!orion_irq_domain)
69 panic("%s: unable to add irq domain\n", np->name);
70
71 ret = irq_alloc_domain_generic_chips(orion_irq_domain,
72 ORION_IRQS_PER_CHIP, 1, np->name,
73 handle_level_irq, clr, 0,
74 IRQ_GC_INIT_MASK_CACHE);
75 if (ret)
76 panic("%s: unable to alloc irq domain gc\n", np->name);
77
78 for (n = 0, base = 0; n < num_chips; n++, base += ORION_IRQS_PER_CHIP) {
79 struct irq_chip_generic *gc =
80 irq_get_domain_generic_chip(orion_irq_domain, base);
81
82 of_address_to_resource(np, n, &r);
83
84 if (!request_mem_region(r.start, resource_size(&r), np->name))
85 panic("%s: unable to request mem region %d",
86 np->name, n);
87
88 gc->reg_base = ioremap(r.start, resource_size(&r));
89 if (!gc->reg_base)
90 panic("%s: unable to map resource %d", np->name, n);
91
92 gc->chip_types[0].regs.mask = ORION_IRQ_MASK;
93 gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
94 gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
95
96 /* mask all interrupts */
97 writel(0, gc->reg_base + ORION_IRQ_MASK);
98 }
99
100 set_handle_irq(orion_handle_irq);
101 return 0;
102}
103IRQCHIP_DECLARE(orion_intc, "marvell,orion-intc", orion_irq_init);
104
105/*
106 * Orion SoC bridge interrupt controller
107 */
108#define ORION_BRIDGE_IRQ_CAUSE 0x00
109#define ORION_BRIDGE_IRQ_MASK 0x04
110
111static void orion_bridge_irq_handler(unsigned int irq, struct irq_desc *desc)
112{
113 struct irq_domain *d = irq_get_handler_data(irq);
114 struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, irq);
115 u32 stat = readl_relaxed(gc->reg_base + ORION_BRIDGE_IRQ_CAUSE) &
116 gc->mask_cache;
117
118 while (stat) {
119 u32 hwirq = ffs(stat) - 1;
120
121 generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq));
122 stat &= ~(1 << hwirq);
123 }
124}
125
126static int __init orion_bridge_irq_init(struct device_node *np,
127 struct device_node *parent)
128{
129 unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
130 struct resource r;
131 struct irq_domain *domain;
132 struct irq_chip_generic *gc;
133 int ret, irq, nrirqs = 32;
134
135 /* get optional number of interrupts provided */
136 of_property_read_u32(np, "marvell,#interrupts", &nrirqs);
137
138 domain = irq_domain_add_linear(np, nrirqs,
139 &irq_generic_chip_ops, NULL);
140 if (!domain) {
141 pr_err("%s: unable to add irq domain\n", np->name);
142 return -ENOMEM;
143 }
144
145 ret = irq_alloc_domain_generic_chips(domain, nrirqs, 1, np->name,
146 handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE);
147 if (ret) {
148 pr_err("%s: unable to alloc irq domain gc\n", np->name);
149 return ret;
150 }
151
152 ret = of_address_to_resource(np, 0, &r);
153 if (ret) {
154 pr_err("%s: unable to get resource\n", np->name);
155 return ret;
156 }
157
158 if (!request_mem_region(r.start, resource_size(&r), np->name)) {
159 pr_err("%s: unable to request mem region\n", np->name);
160 return -ENOMEM;
161 }
162
163 /* Map the parent interrupt for the chained handler */
164 irq = irq_of_parse_and_map(np, 0);
165 if (irq <= 0) {
166 pr_err("%s: unable to parse irq\n", np->name);
167 return -EINVAL;
168 }
169
170 gc = irq_get_domain_generic_chip(domain, 0);
171 gc->reg_base = ioremap(r.start, resource_size(&r));
172 if (!gc->reg_base) {
173 pr_err("%s: unable to map resource\n", np->name);
174 return -ENOMEM;
175 }
176
177 gc->chip_types[0].regs.ack = ORION_BRIDGE_IRQ_CAUSE;
178 gc->chip_types[0].regs.mask = ORION_BRIDGE_IRQ_MASK;
179 gc->chip_types[0].chip.irq_ack = irq_gc_ack_clr_bit;
180 gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
181 gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
182
183 /* mask all interrupts */
184 writel(0, gc->reg_base + ORION_BRIDGE_IRQ_MASK);
185
186 irq_set_handler_data(irq, domain);
187 irq_set_chained_handler(irq, orion_bridge_irq_handler);
188
189 return 0;
190}
191IRQCHIP_DECLARE(orion_bridge_intc,
192 "marvell,orion-bridge-intc", orion_bridge_irq_init);
diff --git a/drivers/irqchip/irq-tb10x.c b/drivers/irqchip/irq-tb10x.c
new file mode 100644
index 000000000000..7c44c99bf1f2
--- /dev/null
+++ b/drivers/irqchip/irq-tb10x.c
@@ -0,0 +1,195 @@
1/*
2 * Abilis Systems interrupt controller driver
3 *
4 * Copyright (C) Abilis Systems 2012
5 *
6 * Author: Christian Ruppert <christian.ruppert@abilis.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <linux/interrupt.h>
23#include <linux/irqdomain.h>
24#include <linux/irq.h>
25#include <linux/of_irq.h>
26#include <linux/of_address.h>
27#include <linux/of_platform.h>
28#include <linux/io.h>
29#include <linux/slab.h>
30#include <linux/bitops.h>
31#include "irqchip.h"
32
33#define AB_IRQCTL_INT_ENABLE 0x00
34#define AB_IRQCTL_INT_STATUS 0x04
35#define AB_IRQCTL_SRC_MODE 0x08
36#define AB_IRQCTL_SRC_POLARITY 0x0C
37#define AB_IRQCTL_INT_MODE 0x10
38#define AB_IRQCTL_INT_POLARITY 0x14
39#define AB_IRQCTL_INT_FORCE 0x18
40
41#define AB_IRQCTL_MAXIRQ 32
42
43static inline void ab_irqctl_writereg(struct irq_chip_generic *gc, u32 reg,
44 u32 val)
45{
46 irq_reg_writel(val, gc->reg_base + reg);
47}
48
49static inline u32 ab_irqctl_readreg(struct irq_chip_generic *gc, u32 reg)
50{
51 return irq_reg_readl(gc->reg_base + reg);
52}
53
54static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type)
55{
56 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
57 uint32_t im, mod, pol;
58
59 im = data->mask;
60
61 irq_gc_lock(gc);
62
63 mod = ab_irqctl_readreg(gc, AB_IRQCTL_SRC_MODE) | im;
64 pol = ab_irqctl_readreg(gc, AB_IRQCTL_SRC_POLARITY) | im;
65
66 switch (flow_type & IRQF_TRIGGER_MASK) {
67 case IRQ_TYPE_EDGE_FALLING:
68 pol ^= im;
69 break;
70 case IRQ_TYPE_LEVEL_HIGH:
71 mod ^= im;
72 break;
73 case IRQ_TYPE_NONE:
74 flow_type = IRQ_TYPE_LEVEL_LOW;
75 case IRQ_TYPE_LEVEL_LOW:
76 mod ^= im;
77 pol ^= im;
78 break;
79 case IRQ_TYPE_EDGE_RISING:
80 break;
81 default:
82 irq_gc_unlock(gc);
83 pr_err("%s: Cannot assign multiple trigger modes to IRQ %d.\n",
84 __func__, data->irq);
85 return -EBADR;
86 }
87
88 irqd_set_trigger_type(data, flow_type);
89 irq_setup_alt_chip(data, flow_type);
90
91 ab_irqctl_writereg(gc, AB_IRQCTL_SRC_MODE, mod);
92 ab_irqctl_writereg(gc, AB_IRQCTL_SRC_POLARITY, pol);
93 ab_irqctl_writereg(gc, AB_IRQCTL_INT_STATUS, im);
94
95 irq_gc_unlock(gc);
96
97 return IRQ_SET_MASK_OK;
98}
99
100static void tb10x_irq_cascade(unsigned int irq, struct irq_desc *desc)
101{
102 struct irq_domain *domain = irq_desc_get_handler_data(desc);
103
104 generic_handle_irq(irq_find_mapping(domain, irq));
105}
106
107static int __init of_tb10x_init_irq(struct device_node *ictl,
108 struct device_node *parent)
109{
110 int i, ret, nrirqs = of_irq_count(ictl);
111 struct resource mem;
112 struct irq_chip_generic *gc;
113 struct irq_domain *domain;
114 void __iomem *reg_base;
115
116 if (of_address_to_resource(ictl, 0, &mem)) {
117 pr_err("%s: No registers declared in DeviceTree.\n",
118 ictl->name);
119 return -EINVAL;
120 }
121
122 if (!request_mem_region(mem.start, resource_size(&mem),
123 ictl->name)) {
124 pr_err("%s: Request mem region failed.\n", ictl->name);
125 return -EBUSY;
126 }
127
128 reg_base = ioremap(mem.start, resource_size(&mem));
129 if (!reg_base) {
130 ret = -EBUSY;
131 pr_err("%s: ioremap failed.\n", ictl->name);
132 goto ioremap_fail;
133 }
134
135 domain = irq_domain_add_linear(ictl, AB_IRQCTL_MAXIRQ,
136 &irq_generic_chip_ops, NULL);
137 if (!domain) {
138 ret = -ENOMEM;
139 pr_err("%s: Could not register interrupt domain.\n",
140 ictl->name);
141 goto irq_domain_add_fail;
142 }
143
144 ret = irq_alloc_domain_generic_chips(domain, AB_IRQCTL_MAXIRQ,
145 2, ictl->name, handle_level_irq,
146 IRQ_NOREQUEST, IRQ_NOPROBE,
147 IRQ_GC_INIT_MASK_CACHE);
148 if (ret) {
149 pr_err("%s: Could not allocate generic interrupt chip.\n",
150 ictl->name);
151 goto gc_alloc_fail;
152 }
153
154 gc = domain->gc->gc[0];
155 gc->reg_base = reg_base;
156
157 gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK;
158 gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
159 gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
160 gc->chip_types[0].chip.irq_set_type = tb10x_irq_set_type;
161 gc->chip_types[0].regs.mask = AB_IRQCTL_INT_ENABLE;
162
163 gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
164 gc->chip_types[1].chip.name = gc->chip_types[0].chip.name;
165 gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit;
166 gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit;
167 gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit;
168 gc->chip_types[1].chip.irq_set_type = tb10x_irq_set_type;
169 gc->chip_types[1].regs.ack = AB_IRQCTL_INT_STATUS;
170 gc->chip_types[1].regs.mask = AB_IRQCTL_INT_ENABLE;
171 gc->chip_types[1].handler = handle_edge_irq;
172
173 for (i = 0; i < nrirqs; i++) {
174 unsigned int irq = irq_of_parse_and_map(ictl, i);
175
176 irq_set_handler_data(irq, domain);
177 irq_set_chained_handler(irq, tb10x_irq_cascade);
178 }
179
180 ab_irqctl_writereg(gc, AB_IRQCTL_INT_ENABLE, 0);
181 ab_irqctl_writereg(gc, AB_IRQCTL_INT_MODE, 0);
182 ab_irqctl_writereg(gc, AB_IRQCTL_INT_POLARITY, 0);
183 ab_irqctl_writereg(gc, AB_IRQCTL_INT_STATUS, ~0UL);
184
185 return 0;
186
187gc_alloc_fail:
188 irq_domain_remove(domain);
189irq_domain_add_fail:
190 iounmap(reg_base);
191ioremap_fail:
192 release_mem_region(mem.start, resource_size(&mem));
193 return ret;
194}
195IRQCHIP_DECLARE(tb10x_intc, "abilis,tb10x-ictl", of_tb10x_init_irq);