blob: f5bfd4c81fe70b883fdd620fdd5796fdb41328e3 [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>
17
18#include <asm/io.h>
19
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020020/* EIC register offsets */
21#define EIC_IER 0x0000
22#define EIC_IDR 0x0004
23#define EIC_IMR 0x0008
24#define EIC_ISR 0x000c
25#define EIC_ICR 0x0010
26#define EIC_MODE 0x0014
27#define EIC_EDGE 0x0018
28#define EIC_LEVEL 0x001c
29#define EIC_TEST 0x0020
30#define EIC_NMIC 0x0024
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070031
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020032/* Bitfields in TEST */
33#define EIC_TESTEN_OFFSET 31
34#define EIC_TESTEN_SIZE 1
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070035
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020036/* Bitfields in NMIC */
37#define EIC_EN_OFFSET 0
38#define EIC_EN_SIZE 1
39
40/* Bit manipulation macros */
41#define EIC_BIT(name) \
42 (1 << EIC_##name##_OFFSET)
43#define EIC_BF(name,value) \
44 (((value) & ((1 << EIC_##name##_SIZE) - 1)) \
45 << EIC_##name##_OFFSET)
46#define EIC_BFEXT(name,value) \
47 (((value) >> EIC_##name##_OFFSET) \
48 & ((1 << EIC_##name##_SIZE) - 1))
49#define EIC_BFINS(name,value,old) \
50 (((old) & ~(((1 << EIC_##name##_SIZE) - 1) \
51 << EIC_##name##_OFFSET)) \
52 | EIC_BF(name,value))
53
54/* Register access macros */
55#define eic_readl(port,reg) \
56 __raw_readl((port)->regs + EIC_##reg)
57#define eic_writel(port,reg,value) \
58 __raw_writel((value), (port)->regs + EIC_##reg)
59
60struct eic {
61 void __iomem *regs;
62 struct irq_chip *chip;
63 unsigned int first_irq;
64};
65
66static void eic_ack_irq(unsigned int irq)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070067{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020068 struct eic *eic = get_irq_chip_data(irq);
69 eic_writel(eic, ICR, 1 << (irq - eic->first_irq));
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070070}
71
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020072static void eic_mask_irq(unsigned int irq)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070073{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020074 struct eic *eic = get_irq_chip_data(irq);
75 eic_writel(eic, IDR, 1 << (irq - eic->first_irq));
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070076}
77
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020078static void eic_mask_ack_irq(unsigned int irq)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070079{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020080 struct eic *eic = get_irq_chip_data(irq);
81 eic_writel(eic, ICR, 1 << (irq - eic->first_irq));
82 eic_writel(eic, IDR, 1 << (irq - eic->first_irq));
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070083}
84
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020085static void eic_unmask_irq(unsigned int irq)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070086{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020087 struct eic *eic = get_irq_chip_data(irq);
88 eic_writel(eic, IER, 1 << (irq - eic->first_irq));
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070089}
90
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020091static int eic_set_irq_type(unsigned int irq, unsigned int flow_type)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070092{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020093 struct eic *eic = get_irq_chip_data(irq);
Haavard Skinnemoen01cb0872006-12-04 12:00:03 +010094 struct irq_desc *desc;
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020095 unsigned int i = irq - eic->first_irq;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070096 u32 mode, edge, level;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070097 int ret = 0;
98
David Brownell58febc02007-01-23 20:21:36 -080099 flow_type &= IRQ_TYPE_SENSE_MASK;
Haavard Skinnemoen01cb0872006-12-04 12:00:03 +0100100 if (flow_type == IRQ_TYPE_NONE)
101 flow_type = IRQ_TYPE_LEVEL_LOW;
102
103 desc = &irq_desc[irq];
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700104
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200105 mode = eic_readl(eic, MODE);
106 edge = eic_readl(eic, EDGE);
107 level = eic_readl(eic, LEVEL);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700108
109 switch (flow_type) {
110 case IRQ_TYPE_LEVEL_LOW:
111 mode |= 1 << i;
112 level &= ~(1 << i);
113 break;
114 case IRQ_TYPE_LEVEL_HIGH:
115 mode |= 1 << i;
116 level |= 1 << i;
117 break;
118 case IRQ_TYPE_EDGE_RISING:
119 mode &= ~(1 << i);
120 edge |= 1 << i;
121 break;
122 case IRQ_TYPE_EDGE_FALLING:
123 mode &= ~(1 << i);
124 edge &= ~(1 << i);
125 break;
126 default:
127 ret = -EINVAL;
128 break;
129 }
130
David Brownell58febc02007-01-23 20:21:36 -0800131 if (ret == 0) {
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200132 eic_writel(eic, MODE, mode);
133 eic_writel(eic, EDGE, edge);
134 eic_writel(eic, LEVEL, level);
David Brownell58febc02007-01-23 20:21:36 -0800135
136 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
137 flow_type |= IRQ_LEVEL;
138 desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
139 desc->status |= flow_type;
140 }
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700141
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700142 return ret;
143}
144
Haavard Skinnemoen86298962007-10-22 15:51:04 +0200145static struct irq_chip eic_chip = {
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200146 .name = "eic",
147 .ack = eic_ack_irq,
148 .mask = eic_mask_irq,
149 .mask_ack = eic_mask_ack_irq,
150 .unmask = eic_unmask_irq,
151 .set_type = eic_set_irq_type,
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700152};
153
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200154static void demux_eic_irq(unsigned int irq, struct irq_desc *desc)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700155{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200156 struct eic *eic = desc->handler_data;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700157 struct irq_desc *ext_desc;
158 unsigned long status, pending;
159 unsigned int i, ext_irq;
160
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200161 status = eic_readl(eic, ISR);
162 pending = status & eic_readl(eic, IMR);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700163
164 while (pending) {
165 i = fls(pending) - 1;
166 pending &= ~(1 << i);
167
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200168 ext_irq = i + eic->first_irq;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700169 ext_desc = irq_desc + ext_irq;
David Brownell58febc02007-01-23 20:21:36 -0800170 if (ext_desc->status & IRQ_LEVEL)
171 handle_level_irq(ext_irq, ext_desc);
172 else
173 handle_edge_irq(ext_irq, ext_desc);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700174 }
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700175}
176
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200177static int __init eic_probe(struct platform_device *pdev)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700178{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200179 struct eic *eic;
180 struct resource *regs;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700181 unsigned int i;
182 unsigned int nr_irqs;
183 unsigned int int_irq;
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200184 int ret;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700185 u32 pattern;
186
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200187 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
188 int_irq = platform_get_irq(pdev, 0);
189 if (!regs || !int_irq) {
190 dev_dbg(&pdev->dev, "missing regs and/or irq resource\n");
191 return -ENXIO;
192 }
193
194 ret = -ENOMEM;
195 eic = kzalloc(sizeof(struct eic), GFP_KERNEL);
196 if (!eic) {
197 dev_dbg(&pdev->dev, "no memory for eic structure\n");
198 goto err_kzalloc;
199 }
200
201 eic->first_irq = EIM_IRQ_BASE + 32 * pdev->id;
202 eic->regs = ioremap(regs->start, regs->end - regs->start + 1);
203 if (!eic->regs) {
204 dev_dbg(&pdev->dev, "failed to map regs\n");
205 goto err_ioremap;
206 }
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700207
208 /*
209 * Find out how many interrupt lines that are actually
210 * implemented in hardware.
211 */
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200212 eic_writel(eic, IDR, ~0UL);
213 eic_writel(eic, MODE, ~0UL);
214 pattern = eic_readl(eic, MODE);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700215 nr_irqs = fls(pattern);
216
Haavard Skinnemoen01cb0872006-12-04 12:00:03 +0100217 /* Trigger on falling edge unless overridden by driver */
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200218 eic_writel(eic, MODE, 0UL);
219 eic_writel(eic, EDGE, 0UL);
Haavard Skinnemoen01cb0872006-12-04 12:00:03 +0100220
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200221 eic->chip = &eic_chip;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700222
223 for (i = 0; i < nr_irqs; i++) {
David Brownell58febc02007-01-23 20:21:36 -0800224 /* NOTE the handler we set here is ignored by the demux */
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200225 set_irq_chip_and_handler(eic->first_irq + i, &eic_chip,
David Brownell58febc02007-01-23 20:21:36 -0800226 handle_level_irq);
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200227 set_irq_chip_data(eic->first_irq + i, eic);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700228 }
229
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200230 set_irq_chained_handler(int_irq, demux_eic_irq);
231 set_irq_data(int_irq, eic);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700232
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200233 dev_info(&pdev->dev,
234 "External Interrupt Controller at 0x%p, IRQ %u\n",
235 eic->regs, int_irq);
236 dev_info(&pdev->dev,
237 "Handling %u external IRQs, starting with IRQ %u\n",
238 nr_irqs, eic->first_irq);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700239
240 return 0;
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200241
242err_ioremap:
243 kfree(eic);
244err_kzalloc:
245 return ret;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700246}
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200247
248static struct platform_driver eic_driver = {
249 .driver = {
250 .name = "at32_eic",
251 },
252};
253
254static int __init eic_init(void)
255{
256 return platform_driver_probe(&eic_driver, eic_probe);
257}
258arch_initcall(eic_init);