blob: 23126aab82ab797977da94e428fb22f1d1ca88fe [file] [log] [blame]
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -07001/*
2 * External interrupt handling for AT32AP CPUs
3 *
4 * Copyright (C) 2006 Atmel Corporation
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/errno.h>
12#include <linux/init.h>
13#include <linux/interrupt.h>
14#include <linux/irq.h>
15#include <linux/platform_device.h>
16#include <linux/random.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090017#include <linux/slab.h>
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070018
19#include <asm/io.h>
20
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020021/* EIC register offsets */
22#define EIC_IER 0x0000
23#define EIC_IDR 0x0004
24#define EIC_IMR 0x0008
25#define EIC_ISR 0x000c
26#define EIC_ICR 0x0010
27#define EIC_MODE 0x0014
28#define EIC_EDGE 0x0018
29#define EIC_LEVEL 0x001c
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020030#define EIC_NMIC 0x0024
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070031
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020032/* Bitfields in NMIC */
Haavard Skinnemoene7ba1762007-10-10 14:58:29 +020033#define EIC_NMIC_ENABLE (1 << 0)
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020034
35/* Bit manipulation macros */
36#define EIC_BIT(name) \
37 (1 << EIC_##name##_OFFSET)
38#define EIC_BF(name,value) \
39 (((value) & ((1 << EIC_##name##_SIZE) - 1)) \
40 << EIC_##name##_OFFSET)
41#define EIC_BFEXT(name,value) \
42 (((value) >> EIC_##name##_OFFSET) \
43 & ((1 << EIC_##name##_SIZE) - 1))
44#define EIC_BFINS(name,value,old) \
45 (((old) & ~(((1 << EIC_##name##_SIZE) - 1) \
46 << EIC_##name##_OFFSET)) \
47 | EIC_BF(name,value))
48
49/* Register access macros */
50#define eic_readl(port,reg) \
51 __raw_readl((port)->regs + EIC_##reg)
52#define eic_writel(port,reg,value) \
53 __raw_writel((value), (port)->regs + EIC_##reg)
54
55struct eic {
56 void __iomem *regs;
57 struct irq_chip *chip;
58 unsigned int first_irq;
59};
60
Haavard Skinnemoene7ba1762007-10-10 14:58:29 +020061static struct eic *nmi_eic;
62static bool nmi_enabled;
63
Thomas Gleixner7776e232011-02-06 17:29:01 +010064static void eic_ack_irq(struct irq_chip *d)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070065{
Thomas Gleixner7776e232011-02-06 17:29:01 +010066 struct eic *eic = irq_data_get_irq_chip_data(data);
67 eic_writel(eic, ICR, 1 << (d->irq - eic->first_irq));
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070068}
69
Thomas Gleixner7776e232011-02-06 17:29:01 +010070static void eic_mask_irq(struct irq_chip *d)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070071{
Thomas Gleixner7776e232011-02-06 17:29:01 +010072 struct eic *eic = irq_data_get_irq_chip_data(data);
73 eic_writel(eic, IDR, 1 << (d->irq - eic->first_irq));
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070074}
75
Thomas Gleixner7776e232011-02-06 17:29:01 +010076static void eic_mask_ack_irq(struct irq_chip *d)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070077{
Thomas Gleixner7776e232011-02-06 17:29:01 +010078 struct eic *eic = irq_data_get_irq_chip_data(data);
79 eic_writel(eic, ICR, 1 << (d->irq - eic->first_irq));
80 eic_writel(eic, IDR, 1 << (d->irq - eic->first_irq));
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070081}
82
Thomas Gleixner7776e232011-02-06 17:29:01 +010083static void eic_unmask_irq(struct irq_chip *d)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070084{
Thomas Gleixner7776e232011-02-06 17:29:01 +010085 struct eic *eic = irq_data_get_irq_chip_data(data);
86 eic_writel(eic, IER, 1 << (d->irq - eic->first_irq));
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070087}
88
Thomas Gleixner7776e232011-02-06 17:29:01 +010089static int eic_set_irq_type(struct irq_chip *d, unsigned int flow_type)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070090{
Thomas Gleixner7776e232011-02-06 17:29:01 +010091 struct eic *eic = irq_data_get_irq_chip_data(data);
Haavard Skinnemoen01cb0872006-12-04 12:00:03 +010092 struct irq_desc *desc;
Thomas Gleixner7776e232011-02-06 17:29:01 +010093 unsigned int irq = d->irq;
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020094 unsigned int i = irq - eic->first_irq;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070095 u32 mode, edge, level;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070096 int ret = 0;
97
David Brownell58febc02007-01-23 20:21:36 -080098 flow_type &= IRQ_TYPE_SENSE_MASK;
Haavard Skinnemoen01cb0872006-12-04 12:00:03 +010099 if (flow_type == IRQ_TYPE_NONE)
100 flow_type = IRQ_TYPE_LEVEL_LOW;
101
Thomas Gleixner7776e232011-02-06 17:29:01 +0100102 desc = irq_to_desc(irq);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700103
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200104 mode = eic_readl(eic, MODE);
105 edge = eic_readl(eic, EDGE);
106 level = eic_readl(eic, LEVEL);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700107
108 switch (flow_type) {
109 case IRQ_TYPE_LEVEL_LOW:
110 mode |= 1 << i;
111 level &= ~(1 << i);
112 break;
113 case IRQ_TYPE_LEVEL_HIGH:
114 mode |= 1 << i;
115 level |= 1 << i;
116 break;
117 case IRQ_TYPE_EDGE_RISING:
118 mode &= ~(1 << i);
119 edge |= 1 << i;
120 break;
121 case IRQ_TYPE_EDGE_FALLING:
122 mode &= ~(1 << i);
123 edge &= ~(1 << i);
124 break;
125 default:
126 ret = -EINVAL;
127 break;
128 }
129
David Brownell58febc02007-01-23 20:21:36 -0800130 if (ret == 0) {
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200131 eic_writel(eic, MODE, mode);
132 eic_writel(eic, EDGE, edge);
133 eic_writel(eic, LEVEL, level);
David Brownell58febc02007-01-23 20:21:36 -0800134
David Brownelle4f586f2007-12-18 20:50:28 -0800135 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) {
David Brownell58febc02007-01-23 20:21:36 -0800136 flow_type |= IRQ_LEVEL;
Thomas Gleixnerd75f1bf2011-03-24 16:39:32 +0100137 __irq_set_handler_locked(irq, handle_level_irq);
David Brownelle4f586f2007-12-18 20:50:28 -0800138 } else
Thomas Gleixnerd75f1bf2011-03-24 16:39:32 +0100139 __irq_set_handler_locked(irq, handle_edge_irq);
David Brownell58febc02007-01-23 20:21:36 -0800140 desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
141 desc->status |= flow_type;
142 }
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700143
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700144 return ret;
145}
146
Haavard Skinnemoen86298962007-10-22 15:51:04 +0200147static struct irq_chip eic_chip = {
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200148 .name = "eic",
Thomas Gleixner7776e232011-02-06 17:29:01 +0100149 .irq_ack = eic_ack_irq,
150 .irq_mask = eic_mask_irq,
151 .irq_mask_ack = eic_mask_ack_irq,
152 .irq_unmask = eic_unmask_irq,
153 .irq_set_type = eic_set_irq_type,
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700154};
155
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200156static void demux_eic_irq(unsigned int irq, struct irq_desc *desc)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700157{
Thomas Gleixnerd75f1bf2011-03-24 16:39:32 +0100158 struct eic *eic = irq_desc_get_handler_data(desc);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700159 unsigned long status, pending;
David Brownelle4f586f2007-12-18 20:50:28 -0800160 unsigned int i;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700161
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200162 status = eic_readl(eic, ISR);
163 pending = status & eic_readl(eic, IMR);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700164
165 while (pending) {
166 i = fls(pending) - 1;
167 pending &= ~(1 << i);
168
David Brownelle4f586f2007-12-18 20:50:28 -0800169 generic_handle_irq(i + eic->first_irq);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700170 }
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700171}
172
Haavard Skinnemoene7ba1762007-10-10 14:58:29 +0200173int nmi_enable(void)
174{
175 nmi_enabled = true;
176
177 if (nmi_eic)
178 eic_writel(nmi_eic, NMIC, EIC_NMIC_ENABLE);
179
180 return 0;
181}
182
183void nmi_disable(void)
184{
185 if (nmi_eic)
186 eic_writel(nmi_eic, NMIC, 0);
187
188 nmi_enabled = false;
189}
190
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200191static int __init eic_probe(struct platform_device *pdev)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700192{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200193 struct eic *eic;
194 struct resource *regs;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700195 unsigned int i;
Thomas Gleixner4b1135a2008-10-16 15:33:18 +0200196 unsigned int nr_of_irqs;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700197 unsigned int int_irq;
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200198 int ret;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700199 u32 pattern;
200
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200201 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
202 int_irq = platform_get_irq(pdev, 0);
203 if (!regs || !int_irq) {
204 dev_dbg(&pdev->dev, "missing regs and/or irq resource\n");
205 return -ENXIO;
206 }
207
208 ret = -ENOMEM;
209 eic = kzalloc(sizeof(struct eic), GFP_KERNEL);
210 if (!eic) {
211 dev_dbg(&pdev->dev, "no memory for eic structure\n");
212 goto err_kzalloc;
213 }
214
215 eic->first_irq = EIM_IRQ_BASE + 32 * pdev->id;
216 eic->regs = ioremap(regs->start, regs->end - regs->start + 1);
217 if (!eic->regs) {
218 dev_dbg(&pdev->dev, "failed to map regs\n");
219 goto err_ioremap;
220 }
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700221
222 /*
223 * Find out how many interrupt lines that are actually
224 * implemented in hardware.
225 */
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200226 eic_writel(eic, IDR, ~0UL);
227 eic_writel(eic, MODE, ~0UL);
228 pattern = eic_readl(eic, MODE);
Thomas Gleixner4b1135a2008-10-16 15:33:18 +0200229 nr_of_irqs = fls(pattern);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700230
Haavard Skinnemoend6c49a72008-01-24 16:56:53 +0100231 /* Trigger on low level unless overridden by driver */
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200232 eic_writel(eic, EDGE, 0UL);
Haavard Skinnemoend6c49a72008-01-24 16:56:53 +0100233 eic_writel(eic, LEVEL, 0UL);
Haavard Skinnemoen01cb0872006-12-04 12:00:03 +0100234
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200235 eic->chip = &eic_chip;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700236
Thomas Gleixner4b1135a2008-10-16 15:33:18 +0200237 for (i = 0; i < nr_of_irqs; i++) {
Thomas Gleixnerd75f1bf2011-03-24 16:39:32 +0100238 irq_set_chip_and_handler(eic->first_irq + i, &eic_chip,
Haavard Skinnemoend6c49a72008-01-24 16:56:53 +0100239 handle_level_irq);
Thomas Gleixnerd75f1bf2011-03-24 16:39:32 +0100240 irq_set_chip_data(eic->first_irq + i, eic);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700241 }
242
Thomas Gleixnerd75f1bf2011-03-24 16:39:32 +0100243 irq_set_chained_handler(int_irq, demux_eic_irq);
244 irq_set_handler_data(int_irq, eic);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700245
Haavard Skinnemoene7ba1762007-10-10 14:58:29 +0200246 if (pdev->id == 0) {
247 nmi_eic = eic;
248 if (nmi_enabled)
249 /*
250 * Someone tried to enable NMI before we were
251 * ready. Do it now.
252 */
253 nmi_enable();
254 }
255
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200256 dev_info(&pdev->dev,
257 "External Interrupt Controller at 0x%p, IRQ %u\n",
258 eic->regs, int_irq);
259 dev_info(&pdev->dev,
260 "Handling %u external IRQs, starting with IRQ %u\n",
Thomas Gleixner4b1135a2008-10-16 15:33:18 +0200261 nr_of_irqs, eic->first_irq);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700262
263 return 0;
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200264
265err_ioremap:
266 kfree(eic);
267err_kzalloc:
268 return ret;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700269}
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200270
271static struct platform_driver eic_driver = {
272 .driver = {
273 .name = "at32_eic",
274 },
275};
276
277static int __init eic_init(void)
278{
279 return platform_driver_probe(&eic_driver, eic_probe);
280}
281arch_initcall(eic_init);