Commit | Line | Data |
---|---|---|
5f97f7f9 HS |
1 | /* |
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 | ||
7a5b8059 HS |
20 | /* 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 | |
31 | ||
32 | /* Bitfields in TEST */ | |
33 | #define EIC_TESTEN_OFFSET 31 | |
34 | #define EIC_TESTEN_SIZE 1 | |
35 | ||
36 | /* 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 | ||
60 | struct eic { | |
61 | void __iomem *regs; | |
62 | struct irq_chip *chip; | |
63 | unsigned int first_irq; | |
64 | }; | |
5f97f7f9 | 65 | |
7a5b8059 | 66 | static void eic_ack_irq(unsigned int irq) |
5f97f7f9 | 67 | { |
7a5b8059 HS |
68 | struct eic *eic = get_irq_chip_data(irq); |
69 | eic_writel(eic, ICR, 1 << (irq - eic->first_irq)); | |
5f97f7f9 HS |
70 | } |
71 | ||
7a5b8059 | 72 | static void eic_mask_irq(unsigned int irq) |
5f97f7f9 | 73 | { |
7a5b8059 HS |
74 | struct eic *eic = get_irq_chip_data(irq); |
75 | eic_writel(eic, IDR, 1 << (irq - eic->first_irq)); | |
5f97f7f9 HS |
76 | } |
77 | ||
7a5b8059 | 78 | static void eic_mask_ack_irq(unsigned int irq) |
5f97f7f9 | 79 | { |
7a5b8059 HS |
80 | 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)); | |
5f97f7f9 HS |
83 | } |
84 | ||
7a5b8059 | 85 | static void eic_unmask_irq(unsigned int irq) |
5f97f7f9 | 86 | { |
7a5b8059 HS |
87 | struct eic *eic = get_irq_chip_data(irq); |
88 | eic_writel(eic, IER, 1 << (irq - eic->first_irq)); | |
5f97f7f9 HS |
89 | } |
90 | ||
7a5b8059 | 91 | static int eic_set_irq_type(unsigned int irq, unsigned int flow_type) |
5f97f7f9 | 92 | { |
7a5b8059 | 93 | struct eic *eic = get_irq_chip_data(irq); |
01cb087e | 94 | struct irq_desc *desc; |
7a5b8059 | 95 | unsigned int i = irq - eic->first_irq; |
5f97f7f9 | 96 | u32 mode, edge, level; |
5f97f7f9 HS |
97 | int ret = 0; |
98 | ||
58febc0b | 99 | flow_type &= IRQ_TYPE_SENSE_MASK; |
01cb087e HS |
100 | if (flow_type == IRQ_TYPE_NONE) |
101 | flow_type = IRQ_TYPE_LEVEL_LOW; | |
102 | ||
103 | desc = &irq_desc[irq]; | |
5f97f7f9 | 104 | |
7a5b8059 HS |
105 | mode = eic_readl(eic, MODE); |
106 | edge = eic_readl(eic, EDGE); | |
107 | level = eic_readl(eic, LEVEL); | |
5f97f7f9 HS |
108 | |
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 | ||
58febc0b | 131 | if (ret == 0) { |
7a5b8059 HS |
132 | eic_writel(eic, MODE, mode); |
133 | eic_writel(eic, EDGE, edge); | |
134 | eic_writel(eic, LEVEL, level); | |
58febc0b DB |
135 | |
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 | } | |
5f97f7f9 | 141 | |
5f97f7f9 HS |
142 | return ret; |
143 | } | |
144 | ||
7a5b8059 HS |
145 | struct irq_chip eic_chip = { |
146 | .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, | |
5f97f7f9 HS |
152 | }; |
153 | ||
7a5b8059 | 154 | static void demux_eic_irq(unsigned int irq, struct irq_desc *desc) |
5f97f7f9 | 155 | { |
7a5b8059 | 156 | struct eic *eic = desc->handler_data; |
5f97f7f9 HS |
157 | struct irq_desc *ext_desc; |
158 | unsigned long status, pending; | |
159 | unsigned int i, ext_irq; | |
160 | ||
7a5b8059 HS |
161 | status = eic_readl(eic, ISR); |
162 | pending = status & eic_readl(eic, IMR); | |
5f97f7f9 HS |
163 | |
164 | while (pending) { | |
165 | i = fls(pending) - 1; | |
166 | pending &= ~(1 << i); | |
167 | ||
7a5b8059 | 168 | ext_irq = i + eic->first_irq; |
5f97f7f9 | 169 | ext_desc = irq_desc + ext_irq; |
58febc0b DB |
170 | if (ext_desc->status & IRQ_LEVEL) |
171 | handle_level_irq(ext_irq, ext_desc); | |
172 | else | |
173 | handle_edge_irq(ext_irq, ext_desc); | |
5f97f7f9 | 174 | } |
5f97f7f9 HS |
175 | } |
176 | ||
7a5b8059 | 177 | static int __init eic_probe(struct platform_device *pdev) |
5f97f7f9 | 178 | { |
7a5b8059 HS |
179 | struct eic *eic; |
180 | struct resource *regs; | |
5f97f7f9 HS |
181 | unsigned int i; |
182 | unsigned int nr_irqs; | |
183 | unsigned int int_irq; | |
7a5b8059 | 184 | int ret; |
5f97f7f9 HS |
185 | u32 pattern; |
186 | ||
7a5b8059 HS |
187 | 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 | } | |
5f97f7f9 HS |
207 | |
208 | /* | |
209 | * Find out how many interrupt lines that are actually | |
210 | * implemented in hardware. | |
211 | */ | |
7a5b8059 HS |
212 | eic_writel(eic, IDR, ~0UL); |
213 | eic_writel(eic, MODE, ~0UL); | |
214 | pattern = eic_readl(eic, MODE); | |
5f97f7f9 HS |
215 | nr_irqs = fls(pattern); |
216 | ||
01cb087e | 217 | /* Trigger on falling edge unless overridden by driver */ |
7a5b8059 HS |
218 | eic_writel(eic, MODE, 0UL); |
219 | eic_writel(eic, EDGE, 0UL); | |
01cb087e | 220 | |
7a5b8059 | 221 | eic->chip = &eic_chip; |
5f97f7f9 HS |
222 | |
223 | for (i = 0; i < nr_irqs; i++) { | |
58febc0b | 224 | /* NOTE the handler we set here is ignored by the demux */ |
7a5b8059 | 225 | set_irq_chip_and_handler(eic->first_irq + i, &eic_chip, |
58febc0b | 226 | handle_level_irq); |
7a5b8059 | 227 | set_irq_chip_data(eic->first_irq + i, eic); |
5f97f7f9 HS |
228 | } |
229 | ||
7a5b8059 HS |
230 | set_irq_chained_handler(int_irq, demux_eic_irq); |
231 | set_irq_data(int_irq, eic); | |
5f97f7f9 | 232 | |
7a5b8059 HS |
233 | 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); | |
5f97f7f9 HS |
239 | |
240 | return 0; | |
7a5b8059 HS |
241 | |
242 | err_ioremap: | |
243 | kfree(eic); | |
244 | err_kzalloc: | |
245 | return ret; | |
246 | } | |
247 | ||
248 | static struct platform_driver eic_driver = { | |
249 | .driver = { | |
250 | .name = "at32_eic", | |
251 | }, | |
252 | }; | |
253 | ||
254 | static int __init eic_init(void) | |
255 | { | |
256 | return platform_driver_probe(&eic_driver, eic_probe); | |
5f97f7f9 | 257 | } |
7a5b8059 | 258 | arch_initcall(eic_init); |