blob: e108e7bba8c00bc61656088132378964214242ce [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
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020029#define EIC_NMIC 0x0024
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070030
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020031/* Bitfields in NMIC */
Haavard Skinnemoene7ba1762007-10-10 14:58:29 +020032#define EIC_NMIC_ENABLE (1 << 0)
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020033
34/* Bit manipulation macros */
35#define EIC_BIT(name) \
36 (1 << EIC_##name##_OFFSET)
37#define EIC_BF(name,value) \
38 (((value) & ((1 << EIC_##name##_SIZE) - 1)) \
39 << EIC_##name##_OFFSET)
40#define EIC_BFEXT(name,value) \
41 (((value) >> EIC_##name##_OFFSET) \
42 & ((1 << EIC_##name##_SIZE) - 1))
43#define EIC_BFINS(name,value,old) \
44 (((old) & ~(((1 << EIC_##name##_SIZE) - 1) \
45 << EIC_##name##_OFFSET)) \
46 | EIC_BF(name,value))
47
48/* Register access macros */
49#define eic_readl(port,reg) \
50 __raw_readl((port)->regs + EIC_##reg)
51#define eic_writel(port,reg,value) \
52 __raw_writel((value), (port)->regs + EIC_##reg)
53
54struct eic {
55 void __iomem *regs;
56 struct irq_chip *chip;
57 unsigned int first_irq;
58};
59
Haavard Skinnemoene7ba1762007-10-10 14:58:29 +020060static struct eic *nmi_eic;
61static bool nmi_enabled;
62
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020063static void eic_ack_irq(unsigned int irq)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070064{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020065 struct eic *eic = get_irq_chip_data(irq);
66 eic_writel(eic, ICR, 1 << (irq - eic->first_irq));
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070067}
68
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020069static void eic_mask_irq(unsigned int irq)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070070{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020071 struct eic *eic = get_irq_chip_data(irq);
72 eic_writel(eic, IDR, 1 << (irq - eic->first_irq));
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070073}
74
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020075static void eic_mask_ack_irq(unsigned int irq)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070076{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020077 struct eic *eic = get_irq_chip_data(irq);
78 eic_writel(eic, ICR, 1 << (irq - eic->first_irq));
79 eic_writel(eic, IDR, 1 << (irq - eic->first_irq));
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070080}
81
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020082static void eic_unmask_irq(unsigned int irq)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070083{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020084 struct eic *eic = get_irq_chip_data(irq);
85 eic_writel(eic, IER, 1 << (irq - eic->first_irq));
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070086}
87
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020088static int eic_set_irq_type(unsigned int irq, unsigned int flow_type)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070089{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020090 struct eic *eic = get_irq_chip_data(irq);
Haavard Skinnemoen01cb0872006-12-04 12:00:03 +010091 struct irq_desc *desc;
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +020092 unsigned int i = irq - eic->first_irq;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070093 u32 mode, edge, level;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070094 int ret = 0;
95
David Brownell58febc02007-01-23 20:21:36 -080096 flow_type &= IRQ_TYPE_SENSE_MASK;
Haavard Skinnemoen01cb0872006-12-04 12:00:03 +010097 if (flow_type == IRQ_TYPE_NONE)
98 flow_type = IRQ_TYPE_LEVEL_LOW;
99
100 desc = &irq_desc[irq];
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700101
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200102 mode = eic_readl(eic, MODE);
103 edge = eic_readl(eic, EDGE);
104 level = eic_readl(eic, LEVEL);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700105
106 switch (flow_type) {
107 case IRQ_TYPE_LEVEL_LOW:
108 mode |= 1 << i;
109 level &= ~(1 << i);
110 break;
111 case IRQ_TYPE_LEVEL_HIGH:
112 mode |= 1 << i;
113 level |= 1 << i;
114 break;
115 case IRQ_TYPE_EDGE_RISING:
116 mode &= ~(1 << i);
117 edge |= 1 << i;
118 break;
119 case IRQ_TYPE_EDGE_FALLING:
120 mode &= ~(1 << i);
121 edge &= ~(1 << i);
122 break;
123 default:
124 ret = -EINVAL;
125 break;
126 }
127
David Brownell58febc02007-01-23 20:21:36 -0800128 if (ret == 0) {
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200129 eic_writel(eic, MODE, mode);
130 eic_writel(eic, EDGE, edge);
131 eic_writel(eic, LEVEL, level);
David Brownell58febc02007-01-23 20:21:36 -0800132
133 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
134 flow_type |= IRQ_LEVEL;
135 desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
136 desc->status |= flow_type;
137 }
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700138
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700139 return ret;
140}
141
Haavard Skinnemoen86298962007-10-22 15:51:04 +0200142static struct irq_chip eic_chip = {
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200143 .name = "eic",
144 .ack = eic_ack_irq,
145 .mask = eic_mask_irq,
146 .mask_ack = eic_mask_ack_irq,
147 .unmask = eic_unmask_irq,
148 .set_type = eic_set_irq_type,
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700149};
150
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200151static void demux_eic_irq(unsigned int irq, struct irq_desc *desc)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700152{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200153 struct eic *eic = desc->handler_data;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700154 struct irq_desc *ext_desc;
155 unsigned long status, pending;
156 unsigned int i, ext_irq;
157
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200158 status = eic_readl(eic, ISR);
159 pending = status & eic_readl(eic, IMR);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700160
161 while (pending) {
162 i = fls(pending) - 1;
163 pending &= ~(1 << i);
164
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200165 ext_irq = i + eic->first_irq;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700166 ext_desc = irq_desc + ext_irq;
David Brownell58febc02007-01-23 20:21:36 -0800167 if (ext_desc->status & IRQ_LEVEL)
168 handle_level_irq(ext_irq, ext_desc);
169 else
170 handle_edge_irq(ext_irq, ext_desc);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700171 }
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700172}
173
Haavard Skinnemoene7ba1762007-10-10 14:58:29 +0200174int nmi_enable(void)
175{
176 nmi_enabled = true;
177
178 if (nmi_eic)
179 eic_writel(nmi_eic, NMIC, EIC_NMIC_ENABLE);
180
181 return 0;
182}
183
184void nmi_disable(void)
185{
186 if (nmi_eic)
187 eic_writel(nmi_eic, NMIC, 0);
188
189 nmi_enabled = false;
190}
191
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200192static int __init eic_probe(struct platform_device *pdev)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700193{
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200194 struct eic *eic;
195 struct resource *regs;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700196 unsigned int i;
197 unsigned int nr_irqs;
198 unsigned int int_irq;
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200199 int ret;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700200 u32 pattern;
201
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200202 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
203 int_irq = platform_get_irq(pdev, 0);
204 if (!regs || !int_irq) {
205 dev_dbg(&pdev->dev, "missing regs and/or irq resource\n");
206 return -ENXIO;
207 }
208
209 ret = -ENOMEM;
210 eic = kzalloc(sizeof(struct eic), GFP_KERNEL);
211 if (!eic) {
212 dev_dbg(&pdev->dev, "no memory for eic structure\n");
213 goto err_kzalloc;
214 }
215
216 eic->first_irq = EIM_IRQ_BASE + 32 * pdev->id;
217 eic->regs = ioremap(regs->start, regs->end - regs->start + 1);
218 if (!eic->regs) {
219 dev_dbg(&pdev->dev, "failed to map regs\n");
220 goto err_ioremap;
221 }
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700222
223 /*
224 * Find out how many interrupt lines that are actually
225 * implemented in hardware.
226 */
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200227 eic_writel(eic, IDR, ~0UL);
228 eic_writel(eic, MODE, ~0UL);
229 pattern = eic_readl(eic, MODE);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700230 nr_irqs = fls(pattern);
231
Haavard Skinnemoen01cb0872006-12-04 12:00:03 +0100232 /* Trigger on falling edge unless overridden by driver */
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200233 eic_writel(eic, MODE, 0UL);
234 eic_writel(eic, EDGE, 0UL);
Haavard Skinnemoen01cb0872006-12-04 12:00:03 +0100235
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200236 eic->chip = &eic_chip;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700237
238 for (i = 0; i < nr_irqs; i++) {
David Brownell58febc02007-01-23 20:21:36 -0800239 /* NOTE the handler we set here is ignored by the demux */
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200240 set_irq_chip_and_handler(eic->first_irq + i, &eic_chip,
David Brownell58febc02007-01-23 20:21:36 -0800241 handle_level_irq);
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200242 set_irq_chip_data(eic->first_irq + i, eic);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700243 }
244
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200245 set_irq_chained_handler(int_irq, demux_eic_irq);
246 set_irq_data(int_irq, eic);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700247
Haavard Skinnemoene7ba1762007-10-10 14:58:29 +0200248 if (pdev->id == 0) {
249 nmi_eic = eic;
250 if (nmi_enabled)
251 /*
252 * Someone tried to enable NMI before we were
253 * ready. Do it now.
254 */
255 nmi_enable();
256 }
257
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200258 dev_info(&pdev->dev,
259 "External Interrupt Controller at 0x%p, IRQ %u\n",
260 eic->regs, int_irq);
261 dev_info(&pdev->dev,
262 "Handling %u external IRQs, starting with IRQ %u\n",
263 nr_irqs, eic->first_irq);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700264
265 return 0;
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200266
267err_ioremap:
268 kfree(eic);
269err_kzalloc:
270 return ret;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700271}
Haavard Skinnemoen7a5b8052007-06-04 12:58:30 +0200272
273static struct platform_driver eic_driver = {
274 .driver = {
275 .name = "at32_eic",
276 },
277};
278
279static int __init eic_init(void)
280{
281 return platform_driver_probe(&eic_driver, eic_probe);
282}
283arch_initcall(eic_init);