diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-04-06 03:02:57 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-04-06 03:02:57 -0400 |
commit | f541ae326fa120fa5c57433e4d9a133df212ce41 (patch) | |
tree | bdbd94ec72cfc601118051cb35e8617d55510177 /arch/powerpc/platforms/85xx/socrates_fpga_pic.c | |
parent | e255357764f92afcafafbd4879b222b8c752065a (diff) | |
parent | 0221c81b1b8eb0cbb6b30a0ced52ead32d2b4e4c (diff) |
Merge branch 'linus' into perfcounters/core-v2
Merge reason: we have gathered quite a few conflicts, need to merge upstream
Conflicts:
arch/powerpc/kernel/Makefile
arch/x86/ia32/ia32entry.S
arch/x86/include/asm/hardirq.h
arch/x86/include/asm/unistd_32.h
arch/x86/include/asm/unistd_64.h
arch/x86/kernel/cpu/common.c
arch/x86/kernel/irq.c
arch/x86/kernel/syscall_table_32.S
arch/x86/mm/iomap_32.c
include/linux/sched.h
kernel/Makefile
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/powerpc/platforms/85xx/socrates_fpga_pic.c')
-rw-r--r-- | arch/powerpc/platforms/85xx/socrates_fpga_pic.c | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/85xx/socrates_fpga_pic.c b/arch/powerpc/platforms/85xx/socrates_fpga_pic.c new file mode 100644 index 000000000000..60edf63d0157 --- /dev/null +++ b/arch/powerpc/platforms/85xx/socrates_fpga_pic.c | |||
@@ -0,0 +1,327 @@ | |||
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 | |||
53 | static DEFINE_SPINLOCK(socrates_fpga_pic_lock); | ||
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 | |||
83 | spin_lock_irqsave(&socrates_fpga_pic_lock, flags); | ||
84 | cause = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(i)); | ||
85 | spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); | ||
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; | ||
119 | spin_lock_irqsave(&socrates_fpga_pic_lock, flags); | ||
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); | ||
124 | spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); | ||
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; | ||
137 | spin_lock_irqsave(&socrates_fpga_pic_lock, flags); | ||
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); | ||
142 | spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); | ||
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; | ||
155 | spin_lock_irqsave(&socrates_fpga_pic_lock, flags); | ||
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); | ||
161 | spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); | ||
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; | ||
174 | spin_lock_irqsave(&socrates_fpga_pic_lock, flags); | ||
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); | ||
179 | spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); | ||
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; | ||
192 | spin_lock_irqsave(&socrates_fpga_pic_lock, flags); | ||
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); | ||
197 | spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); | ||
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 | } | ||
223 | spin_lock_irqsave(&socrates_fpga_pic_lock, flags); | ||
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); | ||
230 | spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); | ||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | static struct irq_chip socrates_fpga_pic_chip = { | ||
235 | .typename = " FPGA-PIC ", | ||
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 */ | ||
248 | get_irq_desc(virq)->status |= IRQ_LEVEL; | ||
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, | ||
256 | struct device_node *ct, u32 *intspec, unsigned int intsize, | ||
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 | |||
317 | spin_lock_irqsave(&socrates_fpga_pic_lock, flags); | ||
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); | ||
324 | spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); | ||
325 | |||
326 | pr_info("FPGA PIC: Setting up Socrates FPGA PIC\n"); | ||
327 | } | ||