blob: 390d4df21ef66a42e18d696f06c6959bbac2a8ad [file] [log] [blame]
SAN People73a59c12006-01-09 17:05:41 +00001/*
Andrew Victor9d041262007-02-05 11:42:07 +01002 * linux/arch/arm/mach-at91/irq.c
SAN People73a59c12006-01-09 17:05:41 +00003 *
4 * Copyright (C) 2004 SAN People
5 * Copyright (C) 2004 ATMEL
6 * Copyright (C) Rick Bronson
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 as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
SAN People73a59c12006-01-09 17:05:41 +000023#include <linux/init.h>
24#include <linux/module.h>
25#include <linux/mm.h>
26#include <linux/types.h>
Nicolas Ferree2615012011-11-22 22:26:09 +010027#include <linux/irq.h>
28#include <linux/of.h>
29#include <linux/of_address.h>
30#include <linux/of_irq.h>
31#include <linux/irqdomain.h>
32#include <linux/err.h>
Ludovic Desrochesf8a073e2012-06-20 16:13:30 +020033#include <linux/slab.h>
SAN People73a59c12006-01-09 17:05:41 +000034
Russell Kinga09e64f2008-08-05 16:14:15 +010035#include <mach/hardware.h>
SAN People73a59c12006-01-09 17:05:41 +000036#include <asm/irq.h>
SAN People73a59c12006-01-09 17:05:41 +000037#include <asm/setup.h>
38
Ludovic Desroches3e135462012-06-11 15:38:03 +020039#include <asm/exception.h>
SAN People73a59c12006-01-09 17:05:41 +000040#include <asm/mach/arch.h>
41#include <asm/mach/irq.h>
42#include <asm/mach/map.h>
43
Jean-Christophe PLAGNIOL-VILLARDbe6d4322011-11-03 01:12:50 +080044void __iomem *at91_aic_base;
Nicolas Ferree2615012011-11-22 22:26:09 +010045static struct irq_domain *at91_aic_domain;
46static struct device_node *at91_aic_np;
Ludovic Desrochesf8a073e2012-06-20 16:13:30 +020047static unsigned int *at91_aic_irq_priorities;
SAN People73a59c12006-01-09 17:05:41 +000048
Ludovic Desroches3e135462012-06-11 15:38:03 +020049asmlinkage void __exception_irq_entry at91_aic_handle_irq(struct pt_regs *regs)
50{
51 u32 irqnr;
52 u32 irqstat;
53
54 irqnr = at91_aic_read(AT91_AIC_IVR);
55 irqstat = at91_aic_read(AT91_AIC_ISR);
56
57 /*
58 * ISR value is 0 when there is no current interrupt or when there is
59 * a spurious interrupt
60 */
61 if (!irqstat)
62 at91_aic_write(AT91_AIC_EOICR, 0);
63 else
64 handle_IRQ(irqnr, regs);
65}
66
Lennert Buytenhekda0f9402010-11-29 10:26:19 +010067static void at91_aic_mask_irq(struct irq_data *d)
SAN People73a59c12006-01-09 17:05:41 +000068{
69 /* Disable interrupt on AIC */
Nicolas Ferree2615012011-11-22 22:26:09 +010070 at91_aic_write(AT91_AIC_IDCR, 1 << d->hwirq);
SAN People73a59c12006-01-09 17:05:41 +000071}
72
Lennert Buytenhekda0f9402010-11-29 10:26:19 +010073static void at91_aic_unmask_irq(struct irq_data *d)
SAN People73a59c12006-01-09 17:05:41 +000074{
75 /* Enable interrupt on AIC */
Nicolas Ferree2615012011-11-22 22:26:09 +010076 at91_aic_write(AT91_AIC_IECR, 1 << d->hwirq);
SAN People73a59c12006-01-09 17:05:41 +000077}
78
Ludovic Desroches42a859d2012-05-25 14:11:51 +020079static void at91_aic_eoi(struct irq_data *d)
80{
81 /*
82 * Mark end-of-interrupt on AIC, the controller doesn't care about
83 * the value written. Moreover it's a write-only register.
84 */
85 at91_aic_write(AT91_AIC_EOICR, 0);
86}
87
Andrew Victor1f4fd0a2006-11-30 10:01:47 +010088unsigned int at91_extern_irq;
89
Nicolas Ferree2615012011-11-22 22:26:09 +010090#define is_extern_irq(hwirq) ((1 << (hwirq)) & at91_extern_irq)
Andrew Victor1f4fd0a2006-11-30 10:01:47 +010091
Lennert Buytenhekda0f9402010-11-29 10:26:19 +010092static int at91_aic_set_type(struct irq_data *d, unsigned type)
SAN People73a59c12006-01-09 17:05:41 +000093{
94 unsigned int smr, srctype;
95
SAN People73a59c12006-01-09 17:05:41 +000096 switch (type) {
Dmitry Baryshkov6cab4862008-07-27 04:23:31 +010097 case IRQ_TYPE_LEVEL_HIGH:
SAN People73a59c12006-01-09 17:05:41 +000098 srctype = AT91_AIC_SRCTYPE_HIGH;
99 break;
Dmitry Baryshkov6cab4862008-07-27 04:23:31 +0100100 case IRQ_TYPE_EDGE_RISING:
SAN People73a59c12006-01-09 17:05:41 +0000101 srctype = AT91_AIC_SRCTYPE_RISING;
102 break;
Dmitry Baryshkov6cab4862008-07-27 04:23:31 +0100103 case IRQ_TYPE_LEVEL_LOW:
Nicolas Ferree2615012011-11-22 22:26:09 +0100104 if ((d->hwirq == AT91_ID_FIQ) || is_extern_irq(d->hwirq)) /* only supported on external interrupts */
Andrew Victor1f4fd0a2006-11-30 10:01:47 +0100105 srctype = AT91_AIC_SRCTYPE_LOW;
106 else
Andrew Victor37f2e4b2006-06-19 15:26:52 +0100107 return -EINVAL;
SAN People73a59c12006-01-09 17:05:41 +0000108 break;
Dmitry Baryshkov6cab4862008-07-27 04:23:31 +0100109 case IRQ_TYPE_EDGE_FALLING:
Nicolas Ferree2615012011-11-22 22:26:09 +0100110 if ((d->hwirq == AT91_ID_FIQ) || is_extern_irq(d->hwirq)) /* only supported on external interrupts */
Andrew Victor1f4fd0a2006-11-30 10:01:47 +0100111 srctype = AT91_AIC_SRCTYPE_FALLING;
112 else
Andrew Victor37f2e4b2006-06-19 15:26:52 +0100113 return -EINVAL;
SAN People73a59c12006-01-09 17:05:41 +0000114 break;
115 default:
116 return -EINVAL;
117 }
118
Nicolas Ferree2615012011-11-22 22:26:09 +0100119 smr = at91_aic_read(AT91_AIC_SMR(d->hwirq)) & ~AT91_AIC_SRCTYPE;
120 at91_aic_write(AT91_AIC_SMR(d->hwirq), smr | srctype);
SAN People73a59c12006-01-09 17:05:41 +0000121 return 0;
122}
123
Andrew Victor683c66b2006-06-19 15:26:53 +0100124#ifdef CONFIG_PM
125
126static u32 wakeups;
127static u32 backups;
128
Lennert Buytenhekda0f9402010-11-29 10:26:19 +0100129static int at91_aic_set_wake(struct irq_data *d, unsigned value)
Andrew Victor683c66b2006-06-19 15:26:53 +0100130{
Nicolas Ferree2615012011-11-22 22:26:09 +0100131 if (unlikely(d->hwirq >= NR_AIC_IRQS))
Andrew Victor683c66b2006-06-19 15:26:53 +0100132 return -EINVAL;
133
134 if (value)
Nicolas Ferree2615012011-11-22 22:26:09 +0100135 wakeups |= (1 << d->hwirq);
Andrew Victor683c66b2006-06-19 15:26:53 +0100136 else
Nicolas Ferree2615012011-11-22 22:26:09 +0100137 wakeups &= ~(1 << d->hwirq);
Andrew Victor683c66b2006-06-19 15:26:53 +0100138
139 return 0;
140}
141
142void at91_irq_suspend(void)
143{
Jean-Christophe PLAGNIOL-VILLARDbe6d4322011-11-03 01:12:50 +0800144 backups = at91_aic_read(AT91_AIC_IMR);
145 at91_aic_write(AT91_AIC_IDCR, backups);
146 at91_aic_write(AT91_AIC_IECR, wakeups);
Andrew Victor683c66b2006-06-19 15:26:53 +0100147}
148
149void at91_irq_resume(void)
150{
Jean-Christophe PLAGNIOL-VILLARDbe6d4322011-11-03 01:12:50 +0800151 at91_aic_write(AT91_AIC_IDCR, wakeups);
152 at91_aic_write(AT91_AIC_IECR, backups);
Andrew Victor683c66b2006-06-19 15:26:53 +0100153}
154
155#else
Andrew Victorba854e12006-07-05 17:22:52 +0100156#define at91_aic_set_wake NULL
Andrew Victor683c66b2006-06-19 15:26:53 +0100157#endif
158
David Brownell38c677c2006-08-01 22:26:25 +0100159static struct irq_chip at91_aic_chip = {
160 .name = "AIC",
Lennert Buytenhekda0f9402010-11-29 10:26:19 +0100161 .irq_mask = at91_aic_mask_irq,
162 .irq_unmask = at91_aic_unmask_irq,
163 .irq_set_type = at91_aic_set_type,
164 .irq_set_wake = at91_aic_set_wake,
Ludovic Desroches42a859d2012-05-25 14:11:51 +0200165 .irq_eoi = at91_aic_eoi,
SAN People73a59c12006-01-09 17:05:41 +0000166};
167
Nicolas Ferre8014d6f42012-02-14 18:08:14 +0100168static void __init at91_aic_hw_init(unsigned int spu_vector)
169{
170 int i;
171
172 /*
173 * Perform 8 End Of Interrupt Command to make sure AIC
174 * will not Lock out nIRQ
175 */
176 for (i = 0; i < 8; i++)
177 at91_aic_write(AT91_AIC_EOICR, 0);
178
179 /*
180 * Spurious Interrupt ID in Spurious Vector Register.
181 * When there is no current interrupt, the IRQ Vector Register
182 * reads the value stored in AIC_SPU
183 */
184 at91_aic_write(AT91_AIC_SPU, spu_vector);
185
186 /* No debugging in AIC: Debug (Protect) Control Register */
187 at91_aic_write(AT91_AIC_DCR, 0);
188
189 /* Disable and clear all interrupts initially */
190 at91_aic_write(AT91_AIC_IDCR, 0xFFFFFFFF);
191 at91_aic_write(AT91_AIC_ICCR, 0xFFFFFFFF);
192}
193
Nicolas Ferree2615012011-11-22 22:26:09 +0100194#if defined(CONFIG_OF)
Nicolas Ferre8014d6f42012-02-14 18:08:14 +0100195static int at91_aic_irq_map(struct irq_domain *h, unsigned int virq,
196 irq_hw_number_t hw)
197{
198 /* Put virq number in Source Vector Register */
199 at91_aic_write(AT91_AIC_SVR(hw), virq);
200
Ludovic Desrochesf8a073e2012-06-20 16:13:30 +0200201 /* Active Low interrupt, with priority */
202 at91_aic_write(AT91_AIC_SMR(hw),
203 AT91_AIC_SRCTYPE_LOW | at91_aic_irq_priorities[hw]);
Nicolas Ferre8014d6f42012-02-14 18:08:14 +0100204
Ludovic Desroches42a859d2012-05-25 14:11:51 +0200205 irq_set_chip_and_handler(virq, &at91_aic_chip, handle_fasteoi_irq);
Nicolas Ferre8014d6f42012-02-14 18:08:14 +0100206 set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
207
208 return 0;
209}
210
Ludovic Desrochesf8a073e2012-06-20 16:13:30 +0200211static int at91_aic_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
212 const u32 *intspec, unsigned int intsize,
213 irq_hw_number_t *out_hwirq, unsigned int *out_type)
214{
215 if (WARN_ON(intsize < 3))
216 return -EINVAL;
217 if (WARN_ON(intspec[0] >= NR_AIC_IRQS))
218 return -EINVAL;
219 if (WARN_ON((intspec[2] < AT91_AIC_IRQ_MIN_PRIORITY)
220 || (intspec[2] > AT91_AIC_IRQ_MAX_PRIORITY)))
221 return -EINVAL;
222
223 *out_hwirq = intspec[0];
224 *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
225 at91_aic_irq_priorities[*out_hwirq] = intspec[2];
226
227 return 0;
228}
229
Nicolas Ferre8014d6f42012-02-14 18:08:14 +0100230static struct irq_domain_ops at91_aic_irq_ops = {
231 .map = at91_aic_irq_map,
Ludovic Desrochesf8a073e2012-06-20 16:13:30 +0200232 .xlate = at91_aic_irq_domain_xlate,
Nicolas Ferre8014d6f42012-02-14 18:08:14 +0100233};
234
235int __init at91_aic_of_init(struct device_node *node,
Nicolas Ferree2615012011-11-22 22:26:09 +0100236 struct device_node *parent)
237{
Jean-Christophe PLAGNIOL-VILLARDc6573942012-04-09 19:36:36 +0800238 struct property *prop;
239 const __be32 *p;
240 u32 val;
241
Ludovic Desrochesf8a073e2012-06-20 16:13:30 +0200242 at91_aic_irq_priorities = kzalloc(NR_AIC_IRQS
243 * sizeof(*at91_aic_irq_priorities),
244 GFP_KERNEL);
245 if (!at91_aic_irq_priorities)
246 return -ENOMEM;
247
Nicolas Ferree2615012011-11-22 22:26:09 +0100248 at91_aic_base = of_iomap(node, 0);
249 at91_aic_np = node;
250
Nicolas Ferre8014d6f42012-02-14 18:08:14 +0100251 at91_aic_domain = irq_domain_add_linear(at91_aic_np, NR_AIC_IRQS,
252 &at91_aic_irq_ops, NULL);
253 if (!at91_aic_domain)
254 panic("Unable to add AIC irq domain (DT)\n");
255
Jean-Christophe PLAGNIOL-VILLARDc6573942012-04-09 19:36:36 +0800256 at91_extern_irq = 0;
257 of_property_for_each_u32(node, "atmel,external-irqs", prop, p, val) {
258 if (val > 31)
259 pr_warn("AIC: external irq %d > 31 skip it\n", val);
260 else
261 at91_extern_irq |= (1 << val);
262 }
263
Nicolas Ferre8014d6f42012-02-14 18:08:14 +0100264 irq_set_default_host(at91_aic_domain);
265
266 at91_aic_hw_init(NR_AIC_IRQS);
267
Nicolas Ferree2615012011-11-22 22:26:09 +0100268 return 0;
269}
Nicolas Ferree2615012011-11-22 22:26:09 +0100270#endif
271
SAN People73a59c12006-01-09 17:05:41 +0000272/*
273 * Initialize the AIC interrupt controller.
274 */
Andrew Victorba854e12006-07-05 17:22:52 +0100275void __init at91_aic_init(unsigned int priority[NR_AIC_IRQS])
SAN People73a59c12006-01-09 17:05:41 +0000276{
277 unsigned int i;
Nicolas Ferree2615012011-11-22 22:26:09 +0100278 int irq_base;
SAN People73a59c12006-01-09 17:05:41 +0000279
Nicolas Ferre8014d6f42012-02-14 18:08:14 +0100280 at91_aic_base = ioremap(AT91_AIC, 512);
Jean-Christophe PLAGNIOL-VILLARDbe6d4322011-11-03 01:12:50 +0800281 if (!at91_aic_base)
Nicolas Ferree2615012011-11-22 22:26:09 +0100282 panic("Unable to ioremap AIC registers\n");
283
284 /* Add irq domain for AIC */
285 irq_base = irq_alloc_descs(-1, 0, NR_AIC_IRQS, 0);
286 if (irq_base < 0) {
287 WARN(1, "Cannot allocate irq_descs, assuming pre-allocated\n");
288 irq_base = 0;
289 }
290 at91_aic_domain = irq_domain_add_legacy(at91_aic_np, NR_AIC_IRQS,
291 irq_base, 0,
292 &irq_domain_simple_ops, NULL);
293
294 if (!at91_aic_domain)
295 panic("Unable to add AIC irq domain\n");
Jean-Christophe PLAGNIOL-VILLARDbe6d4322011-11-03 01:12:50 +0800296
Nicolas Ferre8014d6f42012-02-14 18:08:14 +0100297 irq_set_default_host(at91_aic_domain);
298
SAN People73a59c12006-01-09 17:05:41 +0000299 /*
300 * The IVR is used by macro get_irqnr_and_base to read and verify.
301 * The irq number is NR_AIC_IRQS when a spurious interrupt has occurred.
302 */
303 for (i = 0; i < NR_AIC_IRQS; i++) {
Nicolas Ferree2615012011-11-22 22:26:09 +0100304 /* Put hardware irq number in Source Vector Register: */
Jean-Christophe PLAGNIOL-VILLARDbe6d4322011-11-03 01:12:50 +0800305 at91_aic_write(AT91_AIC_SVR(i), i);
Andrew Victorba854e12006-07-05 17:22:52 +0100306 /* Active Low interrupt, with the specified priority */
Jean-Christophe PLAGNIOL-VILLARDbe6d4322011-11-03 01:12:50 +0800307 at91_aic_write(AT91_AIC_SMR(i), AT91_AIC_SRCTYPE_LOW | priority[i]);
SAN People73a59c12006-01-09 17:05:41 +0000308
Ludovic Desroches42a859d2012-05-25 14:11:51 +0200309 irq_set_chip_and_handler(i, &at91_aic_chip, handle_fasteoi_irq);
SAN People73a59c12006-01-09 17:05:41 +0000310 set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
SAN People73a59c12006-01-09 17:05:41 +0000311 }
312
Nicolas Ferre8014d6f42012-02-14 18:08:14 +0100313 at91_aic_hw_init(NR_AIC_IRQS);
SAN People73a59c12006-01-09 17:05:41 +0000314}