]>
Commit | Line | Data |
---|---|---|
393adcac WG |
1 | /* |
2 | * Copyright (C) 2008 Ilya Yanok, Emcraft Systems | |
3 | * | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | */ | |
10 | ||
11 | #include <linux/irq.h> | |
12 | #include <linux/of_platform.h> | |
13 | #include <linux/io.h> | |
14 | ||
15 | /* | |
16 | * The FPGA supports 9 interrupt sources, which can be routed to 3 | |
17 | * interrupt request lines of the MPIC. The line to be used can be | |
18 | * specified through the third cell of FDT property "interrupts". | |
19 | */ | |
20 | ||
21 | #define SOCRATES_FPGA_NUM_IRQS 9 | |
22 | ||
23 | #define FPGA_PIC_IRQCFG (0x0) | |
24 | #define FPGA_PIC_IRQMASK(n) (0x4 + 0x4 * (n)) | |
25 | ||
26 | #define SOCRATES_FPGA_IRQ_MASK ((1 << SOCRATES_FPGA_NUM_IRQS) - 1) | |
27 | ||
28 | struct socrates_fpga_irq_info { | |
29 | unsigned int irq_line; | |
30 | int type; | |
31 | }; | |
32 | ||
33 | /* | |
34 | * Interrupt routing and type table | |
35 | * | |
36 | * IRQ_TYPE_NONE means the interrupt type is configurable, | |
37 | * otherwise it's fixed to the specified value. | |
38 | */ | |
39 | static struct socrates_fpga_irq_info fpga_irqs[SOCRATES_FPGA_NUM_IRQS] = { | |
40 | [0] = {0, IRQ_TYPE_NONE}, | |
41 | [1] = {0, IRQ_TYPE_LEVEL_HIGH}, | |
42 | [2] = {0, IRQ_TYPE_LEVEL_LOW}, | |
43 | [3] = {0, IRQ_TYPE_NONE}, | |
44 | [4] = {0, IRQ_TYPE_NONE}, | |
45 | [5] = {0, IRQ_TYPE_NONE}, | |
46 | [6] = {0, IRQ_TYPE_NONE}, | |
47 | [7] = {0, IRQ_TYPE_NONE}, | |
48 | [8] = {0, IRQ_TYPE_LEVEL_HIGH}, | |
49 | }; | |
50 | ||
51 | #define socrates_fpga_irq_to_hw(virq) ((unsigned int)irq_map[virq].hwirq) | |
52 | ||
7e026f72 | 53 | static DEFINE_RAW_SPINLOCK(socrates_fpga_pic_lock); |
393adcac WG |
54 | |
55 | static void __iomem *socrates_fpga_pic_iobase; | |
56 | static struct irq_host *socrates_fpga_pic_irq_host; | |
57 | static unsigned int socrates_fpga_irqs[3]; | |
58 | ||
59 | static inline uint32_t socrates_fpga_pic_read(int reg) | |
60 | { | |
61 | return in_be32(socrates_fpga_pic_iobase + reg); | |
62 | } | |
63 | ||
64 | static inline void socrates_fpga_pic_write(int reg, uint32_t val) | |
65 | { | |
66 | out_be32(socrates_fpga_pic_iobase + reg, val); | |
67 | } | |
68 | ||
69 | static inline unsigned int socrates_fpga_pic_get_irq(unsigned int irq) | |
70 | { | |
71 | uint32_t cause; | |
72 | unsigned long flags; | |
73 | int i; | |
74 | ||
75 | /* Check irq line routed to the MPIC */ | |
76 | for (i = 0; i < 3; i++) { | |
77 | if (irq == socrates_fpga_irqs[i]) | |
78 | break; | |
79 | } | |
80 | if (i == 3) | |
81 | return NO_IRQ; | |
82 | ||
7e026f72 | 83 | raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); |
393adcac | 84 | cause = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(i)); |
7e026f72 | 85 | raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); |
393adcac WG |
86 | for (i = SOCRATES_FPGA_NUM_IRQS - 1; i >= 0; i--) { |
87 | if (cause >> (i + 16)) | |
88 | break; | |
89 | } | |
90 | return irq_linear_revmap(socrates_fpga_pic_irq_host, | |
91 | (irq_hw_number_t)i); | |
92 | } | |
93 | ||
94 | void socrates_fpga_pic_cascade(unsigned int irq, struct irq_desc *desc) | |
95 | { | |
96 | unsigned int cascade_irq; | |
97 | ||
98 | /* | |
99 | * See if we actually have an interrupt, call generic handling code if | |
100 | * we do. | |
101 | */ | |
102 | cascade_irq = socrates_fpga_pic_get_irq(irq); | |
103 | ||
104 | if (cascade_irq != NO_IRQ) | |
105 | generic_handle_irq(cascade_irq); | |
106 | desc->chip->eoi(irq); | |
107 | ||
108 | } | |
109 | ||
110 | static void socrates_fpga_pic_ack(unsigned int virq) | |
111 | { | |
112 | unsigned long flags; | |
113 | unsigned int hwirq, irq_line; | |
114 | uint32_t mask; | |
115 | ||
116 | hwirq = socrates_fpga_irq_to_hw(virq); | |
117 | ||
118 | irq_line = fpga_irqs[hwirq].irq_line; | |
7e026f72 | 119 | raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); |
393adcac WG |
120 | mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line)) |
121 | & SOCRATES_FPGA_IRQ_MASK; | |
122 | mask |= (1 << (hwirq + 16)); | |
123 | socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask); | |
7e026f72 | 124 | raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); |
393adcac WG |
125 | } |
126 | ||
127 | static void socrates_fpga_pic_mask(unsigned int virq) | |
128 | { | |
129 | unsigned long flags; | |
130 | unsigned int hwirq; | |
131 | int irq_line; | |
132 | u32 mask; | |
133 | ||
134 | hwirq = socrates_fpga_irq_to_hw(virq); | |
135 | ||
136 | irq_line = fpga_irqs[hwirq].irq_line; | |
7e026f72 | 137 | raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); |
393adcac WG |
138 | mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line)) |
139 | & SOCRATES_FPGA_IRQ_MASK; | |
140 | mask &= ~(1 << hwirq); | |
141 | socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask); | |
7e026f72 | 142 | raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); |
393adcac WG |
143 | } |
144 | ||
145 | static void socrates_fpga_pic_mask_ack(unsigned int virq) | |
146 | { | |
147 | unsigned long flags; | |
148 | unsigned int hwirq; | |
149 | int irq_line; | |
150 | u32 mask; | |
151 | ||
152 | hwirq = socrates_fpga_irq_to_hw(virq); | |
153 | ||
154 | irq_line = fpga_irqs[hwirq].irq_line; | |
7e026f72 | 155 | raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); |
393adcac WG |
156 | mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line)) |
157 | & SOCRATES_FPGA_IRQ_MASK; | |
158 | mask &= ~(1 << hwirq); | |
159 | mask |= (1 << (hwirq + 16)); | |
160 | socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask); | |
7e026f72 | 161 | raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); |
393adcac WG |
162 | } |
163 | ||
164 | static void socrates_fpga_pic_unmask(unsigned int virq) | |
165 | { | |
166 | unsigned long flags; | |
167 | unsigned int hwirq; | |
168 | int irq_line; | |
169 | u32 mask; | |
170 | ||
171 | hwirq = socrates_fpga_irq_to_hw(virq); | |
172 | ||
173 | irq_line = fpga_irqs[hwirq].irq_line; | |
7e026f72 | 174 | raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); |
393adcac WG |
175 | mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line)) |
176 | & SOCRATES_FPGA_IRQ_MASK; | |
177 | mask |= (1 << hwirq); | |
178 | socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask); | |
7e026f72 | 179 | raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); |
393adcac WG |
180 | } |
181 | ||
182 | static void socrates_fpga_pic_eoi(unsigned int virq) | |
183 | { | |
184 | unsigned long flags; | |
185 | unsigned int hwirq; | |
186 | int irq_line; | |
187 | u32 mask; | |
188 | ||
189 | hwirq = socrates_fpga_irq_to_hw(virq); | |
190 | ||
191 | irq_line = fpga_irqs[hwirq].irq_line; | |
7e026f72 | 192 | raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); |
393adcac WG |
193 | mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line)) |
194 | & SOCRATES_FPGA_IRQ_MASK; | |
195 | mask |= (1 << (hwirq + 16)); | |
196 | socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask); | |
7e026f72 | 197 | raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); |
393adcac WG |
198 | } |
199 | ||
200 | static int socrates_fpga_pic_set_type(unsigned int virq, | |
201 | unsigned int flow_type) | |
202 | { | |
203 | unsigned long flags; | |
204 | unsigned int hwirq; | |
205 | int polarity; | |
206 | u32 mask; | |
207 | ||
208 | hwirq = socrates_fpga_irq_to_hw(virq); | |
209 | ||
210 | if (fpga_irqs[hwirq].type != IRQ_TYPE_NONE) | |
211 | return -EINVAL; | |
212 | ||
213 | switch (flow_type & IRQ_TYPE_SENSE_MASK) { | |
214 | case IRQ_TYPE_LEVEL_HIGH: | |
215 | polarity = 1; | |
216 | break; | |
217 | case IRQ_TYPE_LEVEL_LOW: | |
218 | polarity = 0; | |
219 | break; | |
220 | default: | |
221 | return -EINVAL; | |
222 | } | |
7e026f72 | 223 | raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); |
393adcac WG |
224 | mask = socrates_fpga_pic_read(FPGA_PIC_IRQCFG); |
225 | if (polarity) | |
226 | mask |= (1 << hwirq); | |
227 | else | |
228 | mask &= ~(1 << hwirq); | |
229 | socrates_fpga_pic_write(FPGA_PIC_IRQCFG, mask); | |
7e026f72 | 230 | raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); |
393adcac WG |
231 | return 0; |
232 | } | |
233 | ||
234 | static struct irq_chip socrates_fpga_pic_chip = { | |
fc380c0c | 235 | .name = "FPGA-PIC", |
393adcac WG |
236 | .ack = socrates_fpga_pic_ack, |
237 | .mask = socrates_fpga_pic_mask, | |
238 | .mask_ack = socrates_fpga_pic_mask_ack, | |
239 | .unmask = socrates_fpga_pic_unmask, | |
240 | .eoi = socrates_fpga_pic_eoi, | |
241 | .set_type = socrates_fpga_pic_set_type, | |
242 | }; | |
243 | ||
244 | static int socrates_fpga_pic_host_map(struct irq_host *h, unsigned int virq, | |
245 | irq_hw_number_t hwirq) | |
246 | { | |
247 | /* All interrupts are LEVEL sensitive */ | |
6cff46f4 | 248 | irq_to_desc(virq)->status |= IRQ_LEVEL; |
393adcac WG |
249 | set_irq_chip_and_handler(virq, &socrates_fpga_pic_chip, |
250 | handle_fasteoi_irq); | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | static int socrates_fpga_pic_host_xlate(struct irq_host *h, | |
40d50cf7 | 256 | struct device_node *ct, const u32 *intspec, unsigned int intsize, |
393adcac WG |
257 | irq_hw_number_t *out_hwirq, unsigned int *out_flags) |
258 | { | |
259 | struct socrates_fpga_irq_info *fpga_irq = &fpga_irqs[intspec[0]]; | |
260 | ||
261 | *out_hwirq = intspec[0]; | |
262 | if (fpga_irq->type == IRQ_TYPE_NONE) { | |
263 | /* type is configurable */ | |
264 | if (intspec[1] != IRQ_TYPE_LEVEL_LOW && | |
265 | intspec[1] != IRQ_TYPE_LEVEL_HIGH) { | |
266 | pr_warning("FPGA PIC: invalid irq type, " | |
267 | "setting default active low\n"); | |
268 | *out_flags = IRQ_TYPE_LEVEL_LOW; | |
269 | } else { | |
270 | *out_flags = intspec[1]; | |
271 | } | |
272 | } else { | |
273 | /* type is fixed */ | |
274 | *out_flags = fpga_irq->type; | |
275 | } | |
276 | ||
277 | /* Use specified interrupt routing */ | |
278 | if (intspec[2] <= 2) | |
279 | fpga_irq->irq_line = intspec[2]; | |
280 | else | |
281 | pr_warning("FPGA PIC: invalid irq routing\n"); | |
282 | ||
283 | return 0; | |
284 | } | |
285 | ||
286 | static struct irq_host_ops socrates_fpga_pic_host_ops = { | |
287 | .map = socrates_fpga_pic_host_map, | |
288 | .xlate = socrates_fpga_pic_host_xlate, | |
289 | }; | |
290 | ||
291 | void socrates_fpga_pic_init(struct device_node *pic) | |
292 | { | |
293 | unsigned long flags; | |
294 | int i; | |
295 | ||
296 | /* Setup an irq_host structure */ | |
297 | socrates_fpga_pic_irq_host = irq_alloc_host(pic, IRQ_HOST_MAP_LINEAR, | |
298 | SOCRATES_FPGA_NUM_IRQS, &socrates_fpga_pic_host_ops, | |
299 | SOCRATES_FPGA_NUM_IRQS); | |
300 | if (socrates_fpga_pic_irq_host == NULL) { | |
301 | pr_err("FPGA PIC: Unable to allocate host\n"); | |
302 | return; | |
303 | } | |
304 | ||
305 | for (i = 0; i < 3; i++) { | |
306 | socrates_fpga_irqs[i] = irq_of_parse_and_map(pic, i); | |
307 | if (socrates_fpga_irqs[i] == NO_IRQ) { | |
308 | pr_warning("FPGA PIC: can't get irq%d.\n", i); | |
309 | continue; | |
310 | } | |
311 | set_irq_chained_handler(socrates_fpga_irqs[i], | |
312 | socrates_fpga_pic_cascade); | |
313 | } | |
314 | ||
315 | socrates_fpga_pic_iobase = of_iomap(pic, 0); | |
316 | ||
7e026f72 | 317 | raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); |
393adcac WG |
318 | socrates_fpga_pic_write(FPGA_PIC_IRQMASK(0), |
319 | SOCRATES_FPGA_IRQ_MASK << 16); | |
320 | socrates_fpga_pic_write(FPGA_PIC_IRQMASK(1), | |
321 | SOCRATES_FPGA_IRQ_MASK << 16); | |
322 | socrates_fpga_pic_write(FPGA_PIC_IRQMASK(2), | |
323 | SOCRATES_FPGA_IRQ_MASK << 16); | |
7e026f72 | 324 | raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); |
393adcac WG |
325 | |
326 | pr_info("FPGA PIC: Setting up Socrates FPGA PIC\n"); | |
327 | } |