aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Cooper <jason@lakedaemon.net>2014-07-18 16:58:34 -0400
committerJason Cooper <jason@lakedaemon.net>2014-07-18 16:58:34 -0400
commitf0cf9d2facbe3aa93b302058c013729cbc1bca22 (patch)
tree272bdf38e50d723fc88ef1fa068582087b6b8b27
parent20c0c607605244dd237707bb07e7f2ffd82e8aa5 (diff)
parent6704d12d688192366f3a70e6f8a85cb5a869cd5a (diff)
Merge branch 'irqchip/atmel-aic' into irqchip/core
Topic branch set up to facilitate merging the rest of the series which removes the driver from arch code.
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt (renamed from Documentation/devicetree/bindings/arm/atmel-aic.txt)0
-rw-r--r--drivers/irqchip/Kconfig14
-rw-r--r--drivers/irqchip/Makefile2
-rw-r--r--drivers/irqchip/irq-atmel-aic-common.c254
-rw-r--r--drivers/irqchip/irq-atmel-aic-common.h39
-rw-r--r--drivers/irqchip/irq-atmel-aic.c262
-rw-r--r--drivers/irqchip/irq-atmel-aic5.c353
-rw-r--r--include/linux/irq.h2
-rw-r--r--kernel/irq/generic-chip.c5
9 files changed, 929 insertions, 2 deletions
diff --git a/Documentation/devicetree/bindings/arm/atmel-aic.txt b/Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt
index 2742e9cfd6b1..2742e9cfd6b1 100644
--- a/Documentation/devicetree/bindings/arm/atmel-aic.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 5b256ed37d96..4e230e7c76ee 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -35,6 +35,20 @@ config ARM_VIC_NR
35 The maximum number of VICs available in the system, for 35 The maximum number of VICs available in the system, for
36 power management. 36 power management.
37 37
38config ATMEL_AIC_IRQ
39 bool
40 select GENERIC_IRQ_CHIP
41 select IRQ_DOMAIN
42 select MULTI_IRQ_HANDLER
43 select SPARSE_IRQ
44
45config ATMEL_AIC5_IRQ
46 bool
47 select GENERIC_IRQ_CHIP
48 select IRQ_DOMAIN
49 select MULTI_IRQ_HANDLER
50 select SPARSE_IRQ
51
38config BRCMSTB_L2_IRQ 52config BRCMSTB_L2_IRQ
39 bool 53 bool
40 depends on ARM 54 depends on ARM
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 7638221ee552..73052ba9ca62 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -20,6 +20,8 @@ obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o
20obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o 20obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o
21obj-$(CONFIG_ARM_NVIC) += irq-nvic.o 21obj-$(CONFIG_ARM_NVIC) += irq-nvic.o
22obj-$(CONFIG_ARM_VIC) += irq-vic.o 22obj-$(CONFIG_ARM_VIC) += irq-vic.o
23obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o
24obj-$(CONFIG_ATMEL_AIC5_IRQ) += irq-atmel-aic-common.o irq-atmel-aic5.o
23obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o 25obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o
24obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o 26obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o
25obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o 27obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c
new file mode 100644
index 000000000000..6ae3cdee0681
--- /dev/null
+++ b/drivers/irqchip/irq-atmel-aic-common.c
@@ -0,0 +1,254 @@
1/*
2 * Atmel AT91 common AIC (Advanced Interrupt Controller) code shared by
3 * irq-atmel-aic and irq-atmel-aic5 drivers
4 *
5 * Copyright (C) 2004 SAN People
6 * Copyright (C) 2004 ATMEL
7 * Copyright (C) Rick Bronson
8 * Copyright (C) 2014 Free Electrons
9 *
10 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
11 *
12 * This file is licensed under the terms of the GNU General Public
13 * License version 2. This program is licensed "as is" without any
14 * warranty of any kind, whether express or implied.
15 */
16
17#include <linux/errno.h>
18#include <linux/io.h>
19#include <linux/irq.h>
20#include <linux/irqdomain.h>
21#include <linux/of.h>
22#include <linux/of_address.h>
23#include <linux/slab.h>
24
25#include "irq-atmel-aic-common.h"
26
27#define AT91_AIC_PRIOR GENMASK(2, 0)
28#define AT91_AIC_IRQ_MIN_PRIORITY 0
29#define AT91_AIC_IRQ_MAX_PRIORITY 7
30
31#define AT91_AIC_SRCTYPE GENMASK(7, 6)
32#define AT91_AIC_SRCTYPE_LOW (0 << 5)
33#define AT91_AIC_SRCTYPE_FALLING (1 << 5)
34#define AT91_AIC_SRCTYPE_HIGH (2 << 5)
35#define AT91_AIC_SRCTYPE_RISING (3 << 5)
36
37struct aic_chip_data {
38 u32 ext_irqs;
39};
40
41static void aic_common_shutdown(struct irq_data *d)
42{
43 struct irq_chip_type *ct = irq_data_get_chip_type(d);
44
45 ct->chip.irq_mask(d);
46}
47
48int aic_common_set_type(struct irq_data *d, unsigned type, unsigned *val)
49{
50 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
51 struct aic_chip_data *aic = gc->private;
52 unsigned aic_type;
53
54 switch (type) {
55 case IRQ_TYPE_LEVEL_HIGH:
56 aic_type = AT91_AIC_SRCTYPE_HIGH;
57 break;
58 case IRQ_TYPE_EDGE_RISING:
59 aic_type = AT91_AIC_SRCTYPE_RISING;
60 break;
61 case IRQ_TYPE_LEVEL_LOW:
62 if (!(d->mask & aic->ext_irqs))
63 return -EINVAL;
64
65 aic_type = AT91_AIC_SRCTYPE_LOW;
66 break;
67 case IRQ_TYPE_EDGE_FALLING:
68 if (!(d->mask & aic->ext_irqs))
69 return -EINVAL;
70
71 aic_type = AT91_AIC_SRCTYPE_FALLING;
72 break;
73 default:
74 return -EINVAL;
75 }
76
77 *val &= AT91_AIC_SRCTYPE;
78 *val |= aic_type;
79
80 return 0;
81}
82
83int aic_common_set_priority(int priority, unsigned *val)
84{
85 if (priority < AT91_AIC_IRQ_MIN_PRIORITY ||
86 priority > AT91_AIC_IRQ_MAX_PRIORITY)
87 return -EINVAL;
88
89 *val &= AT91_AIC_PRIOR;
90 *val |= priority;
91
92 return 0;
93}
94
95int aic_common_irq_domain_xlate(struct irq_domain *d,
96 struct device_node *ctrlr,
97 const u32 *intspec,
98 unsigned int intsize,
99 irq_hw_number_t *out_hwirq,
100 unsigned int *out_type)
101{
102 if (WARN_ON(intsize < 3))
103 return -EINVAL;
104
105 if (WARN_ON((intspec[2] < AT91_AIC_IRQ_MIN_PRIORITY) ||
106 (intspec[2] > AT91_AIC_IRQ_MAX_PRIORITY)))
107 return -EINVAL;
108
109 *out_hwirq = intspec[0];
110 *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
111
112 return 0;
113}
114
115static void __init aic_common_ext_irq_of_init(struct irq_domain *domain)
116{
117 struct device_node *node = domain->of_node;
118 struct irq_chip_generic *gc;
119 struct aic_chip_data *aic;
120 struct property *prop;
121 const __be32 *p;
122 u32 hwirq;
123
124 gc = irq_get_domain_generic_chip(domain, 0);
125
126 aic = gc->private;
127 aic->ext_irqs |= 1;
128
129 of_property_for_each_u32(node, "atmel,external-irqs", prop, p, hwirq) {
130 gc = irq_get_domain_generic_chip(domain, hwirq);
131 if (!gc) {
132 pr_warn("AIC: external irq %d >= %d skip it\n",
133 hwirq, domain->revmap_size);
134 continue;
135 }
136
137 aic = gc->private;
138 aic->ext_irqs |= (1 << (hwirq % 32));
139 }
140}
141
142#define AT91_RTC_IDR 0x24
143#define AT91_RTC_IMR 0x28
144#define AT91_RTC_IRQ_MASK 0x1f
145
146void __init aic_common_rtc_irq_fixup(struct device_node *root)
147{
148 struct device_node *np;
149 void __iomem *regs;
150
151 np = of_find_compatible_node(root, NULL, "atmel,at91rm9200-rtc");
152 if (!np)
153 np = of_find_compatible_node(root, NULL,
154 "atmel,at91sam9x5-rtc");
155
156 if (!np)
157 return;
158
159 regs = of_iomap(np, 0);
160 of_node_put(np);
161
162 if (!regs)
163 return;
164
165 writel(AT91_RTC_IRQ_MASK, regs + AT91_RTC_IDR);
166
167 iounmap(regs);
168}
169
170void __init aic_common_irq_fixup(const struct of_device_id *matches)
171{
172 struct device_node *root = of_find_node_by_path("/");
173 const struct of_device_id *match;
174
175 if (!root)
176 return;
177
178 match = of_match_node(matches, root);
179 of_node_put(root);
180
181 if (match) {
182 void (*fixup)(struct device_node *) = match->data;
183 fixup(root);
184 }
185
186 of_node_put(root);
187}
188
189struct irq_domain *__init aic_common_of_init(struct device_node *node,
190 const struct irq_domain_ops *ops,
191 const char *name, int nirqs)
192{
193 struct irq_chip_generic *gc;
194 struct irq_domain *domain;
195 struct aic_chip_data *aic;
196 void __iomem *reg_base;
197 int nchips;
198 int ret;
199 int i;
200
201 nchips = DIV_ROUND_UP(nirqs, 32);
202
203 reg_base = of_iomap(node, 0);
204 if (!reg_base)
205 return ERR_PTR(-ENOMEM);
206
207 aic = kcalloc(nchips, sizeof(*aic), GFP_KERNEL);
208 if (!aic) {
209 ret = -ENOMEM;
210 goto err_iounmap;
211 }
212
213 domain = irq_domain_add_linear(node, nchips * 32, ops, aic);
214 if (!domain) {
215 ret = -ENOMEM;
216 goto err_free_aic;
217 }
218
219 ret = irq_alloc_domain_generic_chips(domain, 32, 1, name,
220 handle_level_irq, 0, 0,
221 IRQCHIP_SKIP_SET_WAKE);
222 if (ret)
223 goto err_domain_remove;
224
225 for (i = 0; i < nchips; i++) {
226 gc = irq_get_domain_generic_chip(domain, i * 32);
227
228 gc->reg_base = reg_base;
229
230 gc->unused = 0;
231 gc->wake_enabled = ~0;
232 gc->chip_types[0].type = IRQ_TYPE_SENSE_MASK;
233 gc->chip_types[0].handler = handle_fasteoi_irq;
234 gc->chip_types[0].chip.irq_eoi = irq_gc_eoi;
235 gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
236 gc->chip_types[0].chip.irq_shutdown = aic_common_shutdown;
237 gc->private = &aic[i];
238 }
239
240 aic_common_ext_irq_of_init(domain);
241
242 return domain;
243
244err_domain_remove:
245 irq_domain_remove(domain);
246
247err_free_aic:
248 kfree(aic);
249
250err_iounmap:
251 iounmap(reg_base);
252
253 return ERR_PTR(ret);
254}
diff --git a/drivers/irqchip/irq-atmel-aic-common.h b/drivers/irqchip/irq-atmel-aic-common.h
new file mode 100644
index 000000000000..90aa00e918d6
--- /dev/null
+++ b/drivers/irqchip/irq-atmel-aic-common.h
@@ -0,0 +1,39 @@
1/*
2 * Atmel AT91 common AIC (Advanced Interrupt Controller) header file
3 *
4 * Copyright (C) 2004 SAN People
5 * Copyright (C) 2004 ATMEL
6 * Copyright (C) Rick Bronson
7 * Copyright (C) 2014 Free Electrons
8 *
9 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
10 *
11 * This file is licensed under the terms of the GNU General Public
12 * License version 2. This program is licensed "as is" without any
13 * warranty of any kind, whether express or implied.
14 */
15
16#ifndef __IRQ_ATMEL_AIC_COMMON_H
17#define __IRQ_ATMEL_AIC_COMMON_H
18
19
20int aic_common_set_type(struct irq_data *d, unsigned type, unsigned *val);
21
22int aic_common_set_priority(int priority, unsigned *val);
23
24int aic_common_irq_domain_xlate(struct irq_domain *d,
25 struct device_node *ctrlr,
26 const u32 *intspec,
27 unsigned int intsize,
28 irq_hw_number_t *out_hwirq,
29 unsigned int *out_type);
30
31struct irq_domain *__init aic_common_of_init(struct device_node *node,
32 const struct irq_domain_ops *ops,
33 const char *name, int nirqs);
34
35void __init aic_common_rtc_irq_fixup(struct device_node *root);
36
37void __init aic_common_irq_fixup(const struct of_device_id *matches);
38
39#endif /* __IRQ_ATMEL_AIC_COMMON_H */
diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c
new file mode 100644
index 000000000000..a82869e9fb26
--- /dev/null
+++ b/drivers/irqchip/irq-atmel-aic.c
@@ -0,0 +1,262 @@
1/*
2 * Atmel AT91 AIC (Advanced Interrupt Controller) driver
3 *
4 * Copyright (C) 2004 SAN People
5 * Copyright (C) 2004 ATMEL
6 * Copyright (C) Rick Bronson
7 * Copyright (C) 2014 Free Electrons
8 *
9 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
10 *
11 * This file is licensed under the terms of the GNU General Public
12 * License version 2. This program is licensed "as is" without any
13 * warranty of any kind, whether express or implied.
14 */
15
16#include <linux/init.h>
17#include <linux/module.h>
18#include <linux/mm.h>
19#include <linux/bitmap.h>
20#include <linux/types.h>
21#include <linux/irq.h>
22#include <linux/of.h>
23#include <linux/of_address.h>
24#include <linux/of_irq.h>
25#include <linux/irqdomain.h>
26#include <linux/err.h>
27#include <linux/slab.h>
28#include <linux/io.h>
29
30#include <asm/exception.h>
31#include <asm/mach/irq.h>
32
33#include "irq-atmel-aic-common.h"
34#include "irqchip.h"
35
36/* Number of irq lines managed by AIC */
37#define NR_AIC_IRQS 32
38
39#define AT91_AIC_SMR(n) ((n) * 4)
40
41#define AT91_AIC_SVR(n) (0x80 + ((n) * 4))
42#define AT91_AIC_IVR 0x100
43#define AT91_AIC_FVR 0x104
44#define AT91_AIC_ISR 0x108
45
46#define AT91_AIC_IPR 0x10c
47#define AT91_AIC_IMR 0x110
48#define AT91_AIC_CISR 0x114
49
50#define AT91_AIC_IECR 0x120
51#define AT91_AIC_IDCR 0x124
52#define AT91_AIC_ICCR 0x128
53#define AT91_AIC_ISCR 0x12c
54#define AT91_AIC_EOICR 0x130
55#define AT91_AIC_SPU 0x134
56#define AT91_AIC_DCR 0x138
57
58static struct irq_domain *aic_domain;
59
60static asmlinkage void __exception_irq_entry
61aic_handle(struct pt_regs *regs)
62{
63 struct irq_domain_chip_generic *dgc = aic_domain->gc;
64 struct irq_chip_generic *gc = dgc->gc[0];
65 u32 irqnr;
66 u32 irqstat;
67
68 irqnr = irq_reg_readl(gc->reg_base + AT91_AIC_IVR);
69 irqstat = irq_reg_readl(gc->reg_base + AT91_AIC_ISR);
70
71 irqnr = irq_find_mapping(aic_domain, irqnr);
72
73 if (!irqstat)
74 irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR);
75 else
76 handle_IRQ(irqnr, regs);
77}
78
79static int aic_retrigger(struct irq_data *d)
80{
81 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
82
83 /* Enable interrupt on AIC5 */
84 irq_gc_lock(gc);
85 irq_reg_writel(d->mask, gc->reg_base + AT91_AIC_ISCR);
86 irq_gc_unlock(gc);
87
88 return 0;
89}
90
91static int aic_set_type(struct irq_data *d, unsigned type)
92{
93 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
94 unsigned int smr;
95 int ret;
96
97 smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(d->hwirq));
98 ret = aic_common_set_type(d, type, &smr);
99 if (ret)
100 return ret;
101
102 irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(d->hwirq));
103
104 return 0;
105}
106
107#ifdef CONFIG_PM
108static void aic_suspend(struct irq_data *d)
109{
110 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
111
112 irq_gc_lock(gc);
113 irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IDCR);
114 irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IECR);
115 irq_gc_unlock(gc);
116}
117
118static void aic_resume(struct irq_data *d)
119{
120 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
121
122 irq_gc_lock(gc);
123 irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IDCR);
124 irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IECR);
125 irq_gc_unlock(gc);
126}
127
128static void aic_pm_shutdown(struct irq_data *d)
129{
130 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
131
132 irq_gc_lock(gc);
133 irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR);
134 irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR);
135 irq_gc_unlock(gc);
136}
137#else
138#define aic_suspend NULL
139#define aic_resume NULL
140#define aic_pm_shutdown NULL
141#endif /* CONFIG_PM */
142
143static void __init aic_hw_init(struct irq_domain *domain)
144{
145 struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
146 int i;
147
148 /*
149 * Perform 8 End Of Interrupt Command to make sure AIC
150 * will not Lock out nIRQ
151 */
152 for (i = 0; i < 8; i++)
153 irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR);
154
155 /*
156 * Spurious Interrupt ID in Spurious Vector Register.
157 * When there is no current interrupt, the IRQ Vector Register
158 * reads the value stored in AIC_SPU
159 */
160 irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_SPU);
161
162 /* No debugging in AIC: Debug (Protect) Control Register */
163 irq_reg_writel(0, gc->reg_base + AT91_AIC_DCR);
164
165 /* Disable and clear all interrupts initially */
166 irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR);
167 irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR);
168
169 for (i = 0; i < 32; i++)
170 irq_reg_writel(i, gc->reg_base + AT91_AIC_SVR(i));
171}
172
173static int aic_irq_domain_xlate(struct irq_domain *d,
174 struct device_node *ctrlr,
175 const u32 *intspec, unsigned int intsize,
176 irq_hw_number_t *out_hwirq,
177 unsigned int *out_type)
178{
179 struct irq_domain_chip_generic *dgc = d->gc;
180 struct irq_chip_generic *gc;
181 unsigned smr;
182 int idx;
183 int ret;
184
185 if (!dgc)
186 return -EINVAL;
187
188 ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize,
189 out_hwirq, out_type);
190 if (ret)
191 return ret;
192
193 idx = intspec[0] / dgc->irqs_per_chip;
194 if (idx >= dgc->num_chips)
195 return -EINVAL;
196
197 gc = dgc->gc[idx];
198
199 irq_gc_lock(gc);
200 smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(*out_hwirq));
201 ret = aic_common_set_priority(intspec[2], &smr);
202 if (!ret)
203 irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(*out_hwirq));
204 irq_gc_unlock(gc);
205
206 return ret;
207}
208
209static const struct irq_domain_ops aic_irq_ops = {
210 .map = irq_map_generic_chip,
211 .xlate = aic_irq_domain_xlate,
212};
213
214static void __init at91sam9_aic_irq_fixup(struct device_node *root)
215{
216 aic_common_rtc_irq_fixup(root);
217}
218
219static const struct of_device_id __initdata aic_irq_fixups[] = {
220 { .compatible = "atmel,at91sam9g45", .data = at91sam9_aic_irq_fixup },
221 { .compatible = "atmel,at91sam9n12", .data = at91sam9_aic_irq_fixup },
222 { .compatible = "atmel,at91sam9rl", .data = at91sam9_aic_irq_fixup },
223 { .compatible = "atmel,at91sam9x5", .data = at91sam9_aic_irq_fixup },
224 { /* sentinel */ },
225};
226
227static int __init aic_of_init(struct device_node *node,
228 struct device_node *parent)
229{
230 struct irq_chip_generic *gc;
231 struct irq_domain *domain;
232
233 if (aic_domain)
234 return -EEXIST;
235
236 domain = aic_common_of_init(node, &aic_irq_ops, "atmel-aic",
237 NR_AIC_IRQS);
238 if (IS_ERR(domain))
239 return PTR_ERR(domain);
240
241 aic_common_irq_fixup(aic_irq_fixups);
242
243 aic_domain = domain;
244 gc = irq_get_domain_generic_chip(domain, 0);
245
246 gc->chip_types[0].regs.eoi = AT91_AIC_EOICR;
247 gc->chip_types[0].regs.enable = AT91_AIC_IECR;
248 gc->chip_types[0].regs.disable = AT91_AIC_IDCR;
249 gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
250 gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
251 gc->chip_types[0].chip.irq_retrigger = aic_retrigger;
252 gc->chip_types[0].chip.irq_set_type = aic_set_type;
253 gc->chip_types[0].chip.irq_suspend = aic_suspend;
254 gc->chip_types[0].chip.irq_resume = aic_resume;
255 gc->chip_types[0].chip.irq_pm_shutdown = aic_pm_shutdown;
256
257 aic_hw_init(domain);
258 set_handle_irq(aic_handle);
259
260 return 0;
261}
262IRQCHIP_DECLARE(at91rm9200_aic, "atmel,at91rm9200-aic", aic_of_init);
diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c
new file mode 100644
index 000000000000..edb227081524
--- /dev/null
+++ b/drivers/irqchip/irq-atmel-aic5.c
@@ -0,0 +1,353 @@
1/*
2 * Atmel AT91 AIC5 (Advanced Interrupt Controller) driver
3 *
4 * Copyright (C) 2004 SAN People
5 * Copyright (C) 2004 ATMEL
6 * Copyright (C) Rick Bronson
7 * Copyright (C) 2014 Free Electrons
8 *
9 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
10 *
11 * This file is licensed under the terms of the GNU General Public
12 * License version 2. This program is licensed "as is" without any
13 * warranty of any kind, whether express or implied.
14 */
15
16#include <linux/init.h>
17#include <linux/module.h>
18#include <linux/mm.h>
19#include <linux/bitmap.h>
20#include <linux/types.h>
21#include <linux/irq.h>
22#include <linux/of.h>
23#include <linux/of_address.h>
24#include <linux/of_irq.h>
25#include <linux/irqdomain.h>
26#include <linux/err.h>
27#include <linux/slab.h>
28#include <linux/io.h>
29
30#include <asm/exception.h>
31#include <asm/mach/irq.h>
32
33#include "irq-atmel-aic-common.h"
34#include "irqchip.h"
35
36/* Number of irq lines managed by AIC */
37#define NR_AIC5_IRQS 128
38
39#define AT91_AIC5_SSR 0x0
40#define AT91_AIC5_INTSEL_MSK (0x7f << 0)
41
42#define AT91_AIC5_SMR 0x4
43
44#define AT91_AIC5_SVR 0x8
45#define AT91_AIC5_IVR 0x10
46#define AT91_AIC5_FVR 0x14
47#define AT91_AIC5_ISR 0x18
48
49#define AT91_AIC5_IPR0 0x20
50#define AT91_AIC5_IPR1 0x24
51#define AT91_AIC5_IPR2 0x28
52#define AT91_AIC5_IPR3 0x2c
53#define AT91_AIC5_IMR 0x30
54#define AT91_AIC5_CISR 0x34
55
56#define AT91_AIC5_IECR 0x40
57#define AT91_AIC5_IDCR 0x44
58#define AT91_AIC5_ICCR 0x48
59#define AT91_AIC5_ISCR 0x4c
60#define AT91_AIC5_EOICR 0x38
61#define AT91_AIC5_SPU 0x3c
62#define AT91_AIC5_DCR 0x6c
63
64#define AT91_AIC5_FFER 0x50
65#define AT91_AIC5_FFDR 0x54
66#define AT91_AIC5_FFSR 0x58
67
68static struct irq_domain *aic5_domain;
69
70static asmlinkage void __exception_irq_entry
71aic5_handle(struct pt_regs *regs)
72{
73 struct irq_domain_chip_generic *dgc = aic5_domain->gc;
74 struct irq_chip_generic *gc = dgc->gc[0];
75 u32 irqnr;
76 u32 irqstat;
77
78 irqnr = irq_reg_readl(gc->reg_base + AT91_AIC5_IVR);
79 irqstat = irq_reg_readl(gc->reg_base + AT91_AIC5_ISR);
80
81 irqnr = irq_find_mapping(aic5_domain, irqnr);
82
83 if (!irqstat)
84 irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR);
85 else
86 handle_IRQ(irqnr, regs);
87}
88
89static void aic5_mask(struct irq_data *d)
90{
91 struct irq_domain *domain = d->domain;
92 struct irq_domain_chip_generic *dgc = domain->gc;
93 struct irq_chip_generic *gc = dgc->gc[0];
94
95 /* Disable interrupt on AIC5 */
96 irq_gc_lock(gc);
97 irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
98 irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR);
99 gc->mask_cache &= ~d->mask;
100 irq_gc_unlock(gc);
101}
102
103static void aic5_unmask(struct irq_data *d)
104{
105 struct irq_domain *domain = d->domain;
106 struct irq_domain_chip_generic *dgc = domain->gc;
107 struct irq_chip_generic *gc = dgc->gc[0];
108
109 /* Enable interrupt on AIC5 */
110 irq_gc_lock(gc);
111 irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
112 irq_reg_writel(1, gc->reg_base + AT91_AIC5_IECR);
113 gc->mask_cache |= d->mask;
114 irq_gc_unlock(gc);
115}
116
117static int aic5_retrigger(struct irq_data *d)
118{
119 struct irq_domain *domain = d->domain;
120 struct irq_domain_chip_generic *dgc = domain->gc;
121 struct irq_chip_generic *gc = dgc->gc[0];
122
123 /* Enable interrupt on AIC5 */
124 irq_gc_lock(gc);
125 irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
126 irq_reg_writel(1, gc->reg_base + AT91_AIC5_ISCR);
127 irq_gc_unlock(gc);
128
129 return 0;
130}
131
132static int aic5_set_type(struct irq_data *d, unsigned type)
133{
134 struct irq_domain *domain = d->domain;
135 struct irq_domain_chip_generic *dgc = domain->gc;
136 struct irq_chip_generic *gc = dgc->gc[0];
137 unsigned int smr;
138 int ret;
139
140 irq_gc_lock(gc);
141 irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
142 smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR);
143 ret = aic_common_set_type(d, type, &smr);
144 if (!ret)
145 irq_reg_writel(smr, gc->reg_base + AT91_AIC5_SMR);
146 irq_gc_unlock(gc);
147
148 return ret;
149}
150
151#ifdef CONFIG_PM
152static void aic5_suspend(struct irq_data *d)
153{
154 struct irq_domain *domain = d->domain;
155 struct irq_domain_chip_generic *dgc = domain->gc;
156 struct irq_chip_generic *bgc = dgc->gc[0];
157 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
158 int i;
159 u32 mask;
160
161 irq_gc_lock(bgc);
162 for (i = 0; i < dgc->irqs_per_chip; i++) {
163 mask = 1 << i;
164 if ((mask & gc->mask_cache) == (mask & gc->wake_active))
165 continue;
166
167 irq_reg_writel(i + gc->irq_base,
168 bgc->reg_base + AT91_AIC5_SSR);
169 if (mask & gc->wake_active)
170 irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR);
171 else
172 irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR);
173 }
174 irq_gc_unlock(bgc);
175}
176
177static void aic5_resume(struct irq_data *d)
178{
179 struct irq_domain *domain = d->domain;
180 struct irq_domain_chip_generic *dgc = domain->gc;
181 struct irq_chip_generic *bgc = dgc->gc[0];
182 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
183 int i;
184 u32 mask;
185
186 irq_gc_lock(bgc);
187 for (i = 0; i < dgc->irqs_per_chip; i++) {
188 mask = 1 << i;
189 if ((mask & gc->mask_cache) == (mask & gc->wake_active))
190 continue;
191
192 irq_reg_writel(i + gc->irq_base,
193 bgc->reg_base + AT91_AIC5_SSR);
194 if (mask & gc->mask_cache)
195 irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR);
196 else
197 irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR);
198 }
199 irq_gc_unlock(bgc);
200}
201
202static void aic5_pm_shutdown(struct irq_data *d)
203{
204 struct irq_domain *domain = d->domain;
205 struct irq_domain_chip_generic *dgc = domain->gc;
206 struct irq_chip_generic *bgc = dgc->gc[0];
207 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
208 int i;
209
210 irq_gc_lock(bgc);
211 for (i = 0; i < dgc->irqs_per_chip; i++) {
212 irq_reg_writel(i + gc->irq_base,
213 bgc->reg_base + AT91_AIC5_SSR);
214 irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR);
215 irq_reg_writel(1, bgc->reg_base + AT91_AIC5_ICCR);
216 }
217 irq_gc_unlock(bgc);
218}
219#else
220#define aic5_suspend NULL
221#define aic5_resume NULL
222#define aic5_pm_shutdown NULL
223#endif /* CONFIG_PM */
224
225static void __init aic5_hw_init(struct irq_domain *domain)
226{
227 struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
228 int i;
229
230 /*
231 * Perform 8 End Of Interrupt Command to make sure AIC
232 * will not Lock out nIRQ
233 */
234 for (i = 0; i < 8; i++)
235 irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR);
236
237 /*
238 * Spurious Interrupt ID in Spurious Vector Register.
239 * When there is no current interrupt, the IRQ Vector Register
240 * reads the value stored in AIC_SPU
241 */
242 irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC5_SPU);
243
244 /* No debugging in AIC: Debug (Protect) Control Register */
245 irq_reg_writel(0, gc->reg_base + AT91_AIC5_DCR);
246
247 /* Disable and clear all interrupts initially */
248 for (i = 0; i < domain->revmap_size; i++) {
249 irq_reg_writel(i, gc->reg_base + AT91_AIC5_SSR);
250 irq_reg_writel(i, gc->reg_base + AT91_AIC5_SVR);
251 irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR);
252 irq_reg_writel(1, gc->reg_base + AT91_AIC5_ICCR);
253 }
254}
255
256static int aic5_irq_domain_xlate(struct irq_domain *d,
257 struct device_node *ctrlr,
258 const u32 *intspec, unsigned int intsize,
259 irq_hw_number_t *out_hwirq,
260 unsigned int *out_type)
261{
262 struct irq_domain_chip_generic *dgc = d->gc;
263 struct irq_chip_generic *gc;
264 unsigned smr;
265 int ret;
266
267 if (!dgc)
268 return -EINVAL;
269
270 ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize,
271 out_hwirq, out_type);
272 if (ret)
273 return ret;
274
275 gc = dgc->gc[0];
276
277 irq_gc_lock(gc);
278 irq_reg_writel(*out_hwirq, gc->reg_base + AT91_AIC5_SSR);
279 smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR);
280 ret = aic_common_set_priority(intspec[2], &smr);
281 if (!ret)
282 irq_reg_writel(intspec[2] | smr, gc->reg_base + AT91_AIC5_SMR);
283 irq_gc_unlock(gc);
284
285 return ret;
286}
287
288static const struct irq_domain_ops aic5_irq_ops = {
289 .map = irq_map_generic_chip,
290 .xlate = aic5_irq_domain_xlate,
291};
292
293static void __init sama5d3_aic_irq_fixup(struct device_node *root)
294{
295 aic_common_rtc_irq_fixup(root);
296}
297
298static const struct of_device_id __initdata aic5_irq_fixups[] = {
299 { .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup },
300 { /* sentinel */ },
301};
302
303static int __init aic5_of_init(struct device_node *node,
304 struct device_node *parent,
305 int nirqs)
306{
307 struct irq_chip_generic *gc;
308 struct irq_domain *domain;
309 int nchips;
310 int i;
311
312 if (nirqs > NR_AIC5_IRQS)
313 return -EINVAL;
314
315 if (aic5_domain)
316 return -EEXIST;
317
318 domain = aic_common_of_init(node, &aic5_irq_ops, "atmel-aic5",
319 nirqs);
320 if (IS_ERR(domain))
321 return PTR_ERR(domain);
322
323 aic_common_irq_fixup(aic5_irq_fixups);
324
325 aic5_domain = domain;
326 nchips = aic5_domain->revmap_size / 32;
327 for (i = 0; i < nchips; i++) {
328 gc = irq_get_domain_generic_chip(domain, i * 32);
329
330 gc->chip_types[0].regs.eoi = AT91_AIC5_EOICR;
331 gc->chip_types[0].chip.irq_mask = aic5_mask;
332 gc->chip_types[0].chip.irq_unmask = aic5_unmask;
333 gc->chip_types[0].chip.irq_retrigger = aic5_retrigger;
334 gc->chip_types[0].chip.irq_set_type = aic5_set_type;
335 gc->chip_types[0].chip.irq_suspend = aic5_suspend;
336 gc->chip_types[0].chip.irq_resume = aic5_resume;
337 gc->chip_types[0].chip.irq_pm_shutdown = aic5_pm_shutdown;
338 }
339
340 aic5_hw_init(domain);
341 set_handle_irq(aic5_handle);
342
343 return 0;
344}
345
346#define NR_SAMA5D3_IRQS 50
347
348static int __init sama5d3_aic5_of_init(struct device_node *node,
349 struct device_node *parent)
350{
351 return aic5_of_init(node, parent, NR_SAMA5D3_IRQS);
352}
353IRQCHIP_DECLARE(sama5d3_aic5, "atmel,sama5d3-aic", sama5d3_aic5_of_init);
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 0d998d8b01d8..62af59242ddc 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -771,6 +771,8 @@ void irq_gc_eoi(struct irq_data *d);
771int irq_gc_set_wake(struct irq_data *d, unsigned int on); 771int irq_gc_set_wake(struct irq_data *d, unsigned int on);
772 772
773/* Setup functions for irq_chip_generic */ 773/* Setup functions for irq_chip_generic */
774int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
775 irq_hw_number_t hw_irq);
774struct irq_chip_generic * 776struct irq_chip_generic *
775irq_alloc_generic_chip(const char *name, int nr_ct, unsigned int irq_base, 777irq_alloc_generic_chip(const char *name, int nr_ct, unsigned int irq_base,
776 void __iomem *reg_base, irq_flow_handler_t handler); 778 void __iomem *reg_base, irq_flow_handler_t handler);
diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c
index 452d6f2ba21d..cf80e7b0ddab 100644
--- a/kernel/irq/generic-chip.c
+++ b/kernel/irq/generic-chip.c
@@ -341,8 +341,8 @@ static struct lock_class_key irq_nested_lock_class;
341/* 341/*
342 * irq_map_generic_chip - Map a generic chip for an irq domain 342 * irq_map_generic_chip - Map a generic chip for an irq domain
343 */ 343 */
344static int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, 344int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
345 irq_hw_number_t hw_irq) 345 irq_hw_number_t hw_irq)
346{ 346{
347 struct irq_data *data = irq_get_irq_data(virq); 347 struct irq_data *data = irq_get_irq_data(virq);
348 struct irq_domain_chip_generic *dgc = d->gc; 348 struct irq_domain_chip_generic *dgc = d->gc;
@@ -394,6 +394,7 @@ static int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
394 irq_modify_status(virq, dgc->irq_flags_to_clear, dgc->irq_flags_to_set); 394 irq_modify_status(virq, dgc->irq_flags_to_clear, dgc->irq_flags_to_set);
395 return 0; 395 return 0;
396} 396}
397EXPORT_SYMBOL_GPL(irq_map_generic_chip);
397 398
398struct irq_domain_ops irq_generic_chip_ops = { 399struct irq_domain_ops irq_generic_chip_ops = {
399 .map = irq_map_generic_chip, 400 .map = irq_map_generic_chip,