Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 | 2 | /* |
f30c2269 | 3 | * arch/powerpc/sysdev/ipic.c |
1da177e4 LT |
4 | * |
5 | * IPIC routines implementations. | |
6 | * | |
7 | * Copyright 2005 Freescale Semiconductor, Inc. | |
1da177e4 LT |
8 | */ |
9 | #include <linux/kernel.h> | |
10 | #include <linux/init.h> | |
11 | #include <linux/errno.h> | |
12 | #include <linux/reboot.h> | |
13 | #include <linux/slab.h> | |
14 | #include <linux/stddef.h> | |
15 | #include <linux/sched.h> | |
16 | #include <linux/signal.h> | |
f5a592f7 | 17 | #include <linux/syscore_ops.h> |
b9f0f1bb | 18 | #include <linux/device.h> |
b9f0f1bb | 19 | #include <linux/spinlock.h> |
d49747bd | 20 | #include <linux/fsl_devices.h> |
e6f6390a CL |
21 | #include <linux/irqdomain.h> |
22 | #include <linux/of_address.h> | |
1da177e4 LT |
23 | #include <asm/irq.h> |
24 | #include <asm/io.h> | |
25 | #include <asm/ipic.h> | |
1da177e4 LT |
26 | |
27 | #include "ipic.h" | |
28 | ||
1da177e4 | 29 | static struct ipic * primary_ipic; |
77d4309e | 30 | static struct irq_chip ipic_level_irq_chip, ipic_edge_irq_chip; |
a9e8bf21 | 31 | static DEFINE_RAW_SPINLOCK(ipic_lock); |
1da177e4 LT |
32 | |
33 | static struct ipic_info ipic_info[] = { | |
f03ca957 | 34 | [1] = { |
f03ca957 LY |
35 | .mask = IPIC_SIMSR_H, |
36 | .prio = IPIC_SIPRR_C, | |
37 | .force = IPIC_SIFCR_H, | |
38 | .bit = 16, | |
39 | .prio_mask = 0, | |
40 | }, | |
41 | [2] = { | |
f03ca957 LY |
42 | .mask = IPIC_SIMSR_H, |
43 | .prio = IPIC_SIPRR_C, | |
44 | .force = IPIC_SIFCR_H, | |
45 | .bit = 17, | |
46 | .prio_mask = 1, | |
47 | }, | |
a7267d67 JR |
48 | [3] = { |
49 | .mask = IPIC_SIMSR_H, | |
50 | .prio = IPIC_SIPRR_C, | |
51 | .force = IPIC_SIFCR_H, | |
52 | .bit = 18, | |
53 | .prio_mask = 2, | |
54 | }, | |
f03ca957 | 55 | [4] = { |
f03ca957 LY |
56 | .mask = IPIC_SIMSR_H, |
57 | .prio = IPIC_SIPRR_C, | |
58 | .force = IPIC_SIFCR_H, | |
59 | .bit = 19, | |
60 | .prio_mask = 3, | |
61 | }, | |
a7267d67 JR |
62 | [5] = { |
63 | .mask = IPIC_SIMSR_H, | |
64 | .prio = IPIC_SIPRR_C, | |
65 | .force = IPIC_SIFCR_H, | |
66 | .bit = 20, | |
67 | .prio_mask = 4, | |
68 | }, | |
69 | [6] = { | |
70 | .mask = IPIC_SIMSR_H, | |
71 | .prio = IPIC_SIPRR_C, | |
72 | .force = IPIC_SIFCR_H, | |
73 | .bit = 21, | |
74 | .prio_mask = 5, | |
75 | }, | |
76 | [7] = { | |
77 | .mask = IPIC_SIMSR_H, | |
78 | .prio = IPIC_SIPRR_C, | |
79 | .force = IPIC_SIFCR_H, | |
80 | .bit = 22, | |
81 | .prio_mask = 6, | |
82 | }, | |
83 | [8] = { | |
84 | .mask = IPIC_SIMSR_H, | |
85 | .prio = IPIC_SIPRR_C, | |
86 | .force = IPIC_SIFCR_H, | |
87 | .bit = 23, | |
88 | .prio_mask = 7, | |
89 | }, | |
1da177e4 | 90 | [9] = { |
1da177e4 LT |
91 | .mask = IPIC_SIMSR_H, |
92 | .prio = IPIC_SIPRR_D, | |
93 | .force = IPIC_SIFCR_H, | |
94 | .bit = 24, | |
95 | .prio_mask = 0, | |
96 | }, | |
97 | [10] = { | |
1da177e4 LT |
98 | .mask = IPIC_SIMSR_H, |
99 | .prio = IPIC_SIPRR_D, | |
100 | .force = IPIC_SIFCR_H, | |
101 | .bit = 25, | |
102 | .prio_mask = 1, | |
103 | }, | |
104 | [11] = { | |
1da177e4 LT |
105 | .mask = IPIC_SIMSR_H, |
106 | .prio = IPIC_SIPRR_D, | |
107 | .force = IPIC_SIFCR_H, | |
108 | .bit = 26, | |
109 | .prio_mask = 2, | |
110 | }, | |
f03ca957 | 111 | [12] = { |
f03ca957 LY |
112 | .mask = IPIC_SIMSR_H, |
113 | .prio = IPIC_SIPRR_D, | |
114 | .force = IPIC_SIFCR_H, | |
115 | .bit = 27, | |
116 | .prio_mask = 3, | |
117 | }, | |
118 | [13] = { | |
f03ca957 LY |
119 | .mask = IPIC_SIMSR_H, |
120 | .prio = IPIC_SIPRR_D, | |
121 | .force = IPIC_SIFCR_H, | |
122 | .bit = 28, | |
123 | .prio_mask = 4, | |
124 | }, | |
1da177e4 | 125 | [14] = { |
1da177e4 LT |
126 | .mask = IPIC_SIMSR_H, |
127 | .prio = IPIC_SIPRR_D, | |
128 | .force = IPIC_SIFCR_H, | |
129 | .bit = 29, | |
130 | .prio_mask = 5, | |
131 | }, | |
132 | [15] = { | |
1da177e4 LT |
133 | .mask = IPIC_SIMSR_H, |
134 | .prio = IPIC_SIPRR_D, | |
135 | .force = IPIC_SIFCR_H, | |
136 | .bit = 30, | |
137 | .prio_mask = 6, | |
138 | }, | |
139 | [16] = { | |
1da177e4 LT |
140 | .mask = IPIC_SIMSR_H, |
141 | .prio = IPIC_SIPRR_D, | |
142 | .force = IPIC_SIFCR_H, | |
143 | .bit = 31, | |
144 | .prio_mask = 7, | |
145 | }, | |
146 | [17] = { | |
77d4309e | 147 | .ack = IPIC_SEPNR, |
1da177e4 LT |
148 | .mask = IPIC_SEMSR, |
149 | .prio = IPIC_SMPRR_A, | |
150 | .force = IPIC_SEFCR, | |
151 | .bit = 1, | |
152 | .prio_mask = 5, | |
153 | }, | |
154 | [18] = { | |
77d4309e | 155 | .ack = IPIC_SEPNR, |
1da177e4 LT |
156 | .mask = IPIC_SEMSR, |
157 | .prio = IPIC_SMPRR_A, | |
158 | .force = IPIC_SEFCR, | |
159 | .bit = 2, | |
160 | .prio_mask = 6, | |
161 | }, | |
162 | [19] = { | |
77d4309e | 163 | .ack = IPIC_SEPNR, |
1da177e4 LT |
164 | .mask = IPIC_SEMSR, |
165 | .prio = IPIC_SMPRR_A, | |
166 | .force = IPIC_SEFCR, | |
167 | .bit = 3, | |
168 | .prio_mask = 7, | |
169 | }, | |
170 | [20] = { | |
77d4309e | 171 | .ack = IPIC_SEPNR, |
1da177e4 LT |
172 | .mask = IPIC_SEMSR, |
173 | .prio = IPIC_SMPRR_B, | |
174 | .force = IPIC_SEFCR, | |
175 | .bit = 4, | |
176 | .prio_mask = 4, | |
177 | }, | |
178 | [21] = { | |
77d4309e | 179 | .ack = IPIC_SEPNR, |
1da177e4 LT |
180 | .mask = IPIC_SEMSR, |
181 | .prio = IPIC_SMPRR_B, | |
182 | .force = IPIC_SEFCR, | |
183 | .bit = 5, | |
184 | .prio_mask = 5, | |
185 | }, | |
186 | [22] = { | |
77d4309e | 187 | .ack = IPIC_SEPNR, |
1da177e4 LT |
188 | .mask = IPIC_SEMSR, |
189 | .prio = IPIC_SMPRR_B, | |
190 | .force = IPIC_SEFCR, | |
191 | .bit = 6, | |
192 | .prio_mask = 6, | |
193 | }, | |
194 | [23] = { | |
77d4309e | 195 | .ack = IPIC_SEPNR, |
1da177e4 LT |
196 | .mask = IPIC_SEMSR, |
197 | .prio = IPIC_SMPRR_B, | |
198 | .force = IPIC_SEFCR, | |
199 | .bit = 7, | |
200 | .prio_mask = 7, | |
201 | }, | |
202 | [32] = { | |
1da177e4 LT |
203 | .mask = IPIC_SIMSR_H, |
204 | .prio = IPIC_SIPRR_A, | |
205 | .force = IPIC_SIFCR_H, | |
206 | .bit = 0, | |
207 | .prio_mask = 0, | |
208 | }, | |
209 | [33] = { | |
1da177e4 LT |
210 | .mask = IPIC_SIMSR_H, |
211 | .prio = IPIC_SIPRR_A, | |
212 | .force = IPIC_SIFCR_H, | |
213 | .bit = 1, | |
214 | .prio_mask = 1, | |
215 | }, | |
216 | [34] = { | |
1da177e4 LT |
217 | .mask = IPIC_SIMSR_H, |
218 | .prio = IPIC_SIPRR_A, | |
219 | .force = IPIC_SIFCR_H, | |
220 | .bit = 2, | |
221 | .prio_mask = 2, | |
222 | }, | |
223 | [35] = { | |
1da177e4 LT |
224 | .mask = IPIC_SIMSR_H, |
225 | .prio = IPIC_SIPRR_A, | |
226 | .force = IPIC_SIFCR_H, | |
227 | .bit = 3, | |
228 | .prio_mask = 3, | |
229 | }, | |
230 | [36] = { | |
1da177e4 LT |
231 | .mask = IPIC_SIMSR_H, |
232 | .prio = IPIC_SIPRR_A, | |
233 | .force = IPIC_SIFCR_H, | |
234 | .bit = 4, | |
235 | .prio_mask = 4, | |
236 | }, | |
237 | [37] = { | |
1da177e4 LT |
238 | .mask = IPIC_SIMSR_H, |
239 | .prio = IPIC_SIPRR_A, | |
240 | .force = IPIC_SIFCR_H, | |
241 | .bit = 5, | |
242 | .prio_mask = 5, | |
243 | }, | |
244 | [38] = { | |
1da177e4 LT |
245 | .mask = IPIC_SIMSR_H, |
246 | .prio = IPIC_SIPRR_A, | |
247 | .force = IPIC_SIFCR_H, | |
248 | .bit = 6, | |
249 | .prio_mask = 6, | |
250 | }, | |
251 | [39] = { | |
1da177e4 LT |
252 | .mask = IPIC_SIMSR_H, |
253 | .prio = IPIC_SIPRR_A, | |
254 | .force = IPIC_SIFCR_H, | |
255 | .bit = 7, | |
256 | .prio_mask = 7, | |
257 | }, | |
a7267d67 JR |
258 | [40] = { |
259 | .mask = IPIC_SIMSR_H, | |
260 | .prio = IPIC_SIPRR_B, | |
261 | .force = IPIC_SIFCR_H, | |
262 | .bit = 8, | |
263 | .prio_mask = 0, | |
264 | }, | |
265 | [41] = { | |
266 | .mask = IPIC_SIMSR_H, | |
267 | .prio = IPIC_SIPRR_B, | |
268 | .force = IPIC_SIFCR_H, | |
269 | .bit = 9, | |
270 | .prio_mask = 1, | |
271 | }, | |
f03ca957 | 272 | [42] = { |
f03ca957 LY |
273 | .mask = IPIC_SIMSR_H, |
274 | .prio = IPIC_SIPRR_B, | |
275 | .force = IPIC_SIFCR_H, | |
276 | .bit = 10, | |
277 | .prio_mask = 2, | |
278 | }, | |
a7267d67 JR |
279 | [43] = { |
280 | .mask = IPIC_SIMSR_H, | |
281 | .prio = IPIC_SIPRR_B, | |
282 | .force = IPIC_SIFCR_H, | |
283 | .bit = 11, | |
284 | .prio_mask = 3, | |
285 | }, | |
f03ca957 | 286 | [44] = { |
f03ca957 LY |
287 | .mask = IPIC_SIMSR_H, |
288 | .prio = IPIC_SIPRR_B, | |
289 | .force = IPIC_SIFCR_H, | |
290 | .bit = 12, | |
291 | .prio_mask = 4, | |
292 | }, | |
293 | [45] = { | |
f03ca957 LY |
294 | .mask = IPIC_SIMSR_H, |
295 | .prio = IPIC_SIPRR_B, | |
296 | .force = IPIC_SIFCR_H, | |
297 | .bit = 13, | |
298 | .prio_mask = 5, | |
299 | }, | |
300 | [46] = { | |
f03ca957 LY |
301 | .mask = IPIC_SIMSR_H, |
302 | .prio = IPIC_SIPRR_B, | |
303 | .force = IPIC_SIFCR_H, | |
304 | .bit = 14, | |
305 | .prio_mask = 6, | |
306 | }, | |
307 | [47] = { | |
f03ca957 LY |
308 | .mask = IPIC_SIMSR_H, |
309 | .prio = IPIC_SIPRR_B, | |
310 | .force = IPIC_SIFCR_H, | |
311 | .bit = 15, | |
312 | .prio_mask = 7, | |
313 | }, | |
1da177e4 | 314 | [48] = { |
446183e4 | 315 | .ack = IPIC_SEPNR, |
1da177e4 LT |
316 | .mask = IPIC_SEMSR, |
317 | .prio = IPIC_SMPRR_A, | |
318 | .force = IPIC_SEFCR, | |
319 | .bit = 0, | |
320 | .prio_mask = 4, | |
321 | }, | |
322 | [64] = { | |
1da177e4 LT |
323 | .mask = IPIC_SIMSR_L, |
324 | .prio = IPIC_SMPRR_A, | |
325 | .force = IPIC_SIFCR_L, | |
326 | .bit = 0, | |
327 | .prio_mask = 0, | |
328 | }, | |
329 | [65] = { | |
1da177e4 LT |
330 | .mask = IPIC_SIMSR_L, |
331 | .prio = IPIC_SMPRR_A, | |
332 | .force = IPIC_SIFCR_L, | |
333 | .bit = 1, | |
334 | .prio_mask = 1, | |
335 | }, | |
336 | [66] = { | |
1da177e4 LT |
337 | .mask = IPIC_SIMSR_L, |
338 | .prio = IPIC_SMPRR_A, | |
339 | .force = IPIC_SIFCR_L, | |
340 | .bit = 2, | |
341 | .prio_mask = 2, | |
342 | }, | |
343 | [67] = { | |
1da177e4 LT |
344 | .mask = IPIC_SIMSR_L, |
345 | .prio = IPIC_SMPRR_A, | |
346 | .force = IPIC_SIFCR_L, | |
347 | .bit = 3, | |
348 | .prio_mask = 3, | |
349 | }, | |
350 | [68] = { | |
1da177e4 LT |
351 | .mask = IPIC_SIMSR_L, |
352 | .prio = IPIC_SMPRR_B, | |
353 | .force = IPIC_SIFCR_L, | |
354 | .bit = 4, | |
355 | .prio_mask = 0, | |
356 | }, | |
357 | [69] = { | |
1da177e4 LT |
358 | .mask = IPIC_SIMSR_L, |
359 | .prio = IPIC_SMPRR_B, | |
360 | .force = IPIC_SIFCR_L, | |
361 | .bit = 5, | |
362 | .prio_mask = 1, | |
363 | }, | |
364 | [70] = { | |
1da177e4 LT |
365 | .mask = IPIC_SIMSR_L, |
366 | .prio = IPIC_SMPRR_B, | |
367 | .force = IPIC_SIFCR_L, | |
368 | .bit = 6, | |
369 | .prio_mask = 2, | |
370 | }, | |
371 | [71] = { | |
1da177e4 LT |
372 | .mask = IPIC_SIMSR_L, |
373 | .prio = IPIC_SMPRR_B, | |
374 | .force = IPIC_SIFCR_L, | |
375 | .bit = 7, | |
376 | .prio_mask = 3, | |
377 | }, | |
378 | [72] = { | |
1da177e4 LT |
379 | .mask = IPIC_SIMSR_L, |
380 | .prio = 0, | |
381 | .force = IPIC_SIFCR_L, | |
382 | .bit = 8, | |
383 | }, | |
384 | [73] = { | |
1da177e4 LT |
385 | .mask = IPIC_SIMSR_L, |
386 | .prio = 0, | |
387 | .force = IPIC_SIFCR_L, | |
388 | .bit = 9, | |
389 | }, | |
390 | [74] = { | |
1da177e4 LT |
391 | .mask = IPIC_SIMSR_L, |
392 | .prio = 0, | |
393 | .force = IPIC_SIFCR_L, | |
394 | .bit = 10, | |
395 | }, | |
396 | [75] = { | |
1da177e4 LT |
397 | .mask = IPIC_SIMSR_L, |
398 | .prio = 0, | |
399 | .force = IPIC_SIFCR_L, | |
400 | .bit = 11, | |
401 | }, | |
402 | [76] = { | |
1da177e4 LT |
403 | .mask = IPIC_SIMSR_L, |
404 | .prio = 0, | |
405 | .force = IPIC_SIFCR_L, | |
406 | .bit = 12, | |
407 | }, | |
408 | [77] = { | |
1da177e4 LT |
409 | .mask = IPIC_SIMSR_L, |
410 | .prio = 0, | |
411 | .force = IPIC_SIFCR_L, | |
412 | .bit = 13, | |
413 | }, | |
414 | [78] = { | |
1da177e4 LT |
415 | .mask = IPIC_SIMSR_L, |
416 | .prio = 0, | |
417 | .force = IPIC_SIFCR_L, | |
418 | .bit = 14, | |
419 | }, | |
420 | [79] = { | |
1da177e4 LT |
421 | .mask = IPIC_SIMSR_L, |
422 | .prio = 0, | |
423 | .force = IPIC_SIFCR_L, | |
424 | .bit = 15, | |
425 | }, | |
426 | [80] = { | |
1da177e4 LT |
427 | .mask = IPIC_SIMSR_L, |
428 | .prio = 0, | |
429 | .force = IPIC_SIFCR_L, | |
430 | .bit = 16, | |
431 | }, | |
f03ca957 | 432 | [81] = { |
f03ca957 LY |
433 | .mask = IPIC_SIMSR_L, |
434 | .prio = 0, | |
435 | .force = IPIC_SIFCR_L, | |
436 | .bit = 17, | |
437 | }, | |
438 | [82] = { | |
f03ca957 LY |
439 | .mask = IPIC_SIMSR_L, |
440 | .prio = 0, | |
441 | .force = IPIC_SIFCR_L, | |
442 | .bit = 18, | |
443 | }, | |
a7267d67 JR |
444 | [83] = { |
445 | .mask = IPIC_SIMSR_L, | |
446 | .prio = 0, | |
447 | .force = IPIC_SIFCR_L, | |
448 | .bit = 19, | |
449 | }, | |
1da177e4 | 450 | [84] = { |
1da177e4 LT |
451 | .mask = IPIC_SIMSR_L, |
452 | .prio = 0, | |
453 | .force = IPIC_SIFCR_L, | |
454 | .bit = 20, | |
455 | }, | |
456 | [85] = { | |
1da177e4 LT |
457 | .mask = IPIC_SIMSR_L, |
458 | .prio = 0, | |
459 | .force = IPIC_SIFCR_L, | |
460 | .bit = 21, | |
461 | }, | |
f03ca957 | 462 | [86] = { |
f03ca957 LY |
463 | .mask = IPIC_SIMSR_L, |
464 | .prio = 0, | |
465 | .force = IPIC_SIFCR_L, | |
466 | .bit = 22, | |
467 | }, | |
468 | [87] = { | |
f03ca957 LY |
469 | .mask = IPIC_SIMSR_L, |
470 | .prio = 0, | |
471 | .force = IPIC_SIFCR_L, | |
472 | .bit = 23, | |
473 | }, | |
474 | [88] = { | |
f03ca957 LY |
475 | .mask = IPIC_SIMSR_L, |
476 | .prio = 0, | |
477 | .force = IPIC_SIFCR_L, | |
478 | .bit = 24, | |
479 | }, | |
480 | [89] = { | |
f03ca957 LY |
481 | .mask = IPIC_SIMSR_L, |
482 | .prio = 0, | |
483 | .force = IPIC_SIFCR_L, | |
484 | .bit = 25, | |
485 | }, | |
1da177e4 | 486 | [90] = { |
1da177e4 LT |
487 | .mask = IPIC_SIMSR_L, |
488 | .prio = 0, | |
489 | .force = IPIC_SIFCR_L, | |
490 | .bit = 26, | |
491 | }, | |
492 | [91] = { | |
1da177e4 LT |
493 | .mask = IPIC_SIMSR_L, |
494 | .prio = 0, | |
495 | .force = IPIC_SIFCR_L, | |
496 | .bit = 27, | |
497 | }, | |
8cf6b195 KP |
498 | [94] = { |
499 | .mask = IPIC_SIMSR_L, | |
500 | .prio = 0, | |
501 | .force = IPIC_SIFCR_L, | |
502 | .bit = 30, | |
503 | }, | |
1da177e4 LT |
504 | }; |
505 | ||
506 | static inline u32 ipic_read(volatile u32 __iomem *base, unsigned int reg) | |
507 | { | |
508 | return in_be32(base + (reg >> 2)); | |
509 | } | |
510 | ||
511 | static inline void ipic_write(volatile u32 __iomem *base, unsigned int reg, u32 value) | |
512 | { | |
513 | out_be32(base + (reg >> 2), value); | |
514 | } | |
515 | ||
b9f0f1bb | 516 | static inline struct ipic * ipic_from_irq(unsigned int virq) |
1da177e4 LT |
517 | { |
518 | return primary_ipic; | |
519 | } | |
520 | ||
687228ad | 521 | static void ipic_unmask_irq(struct irq_data *d) |
1da177e4 | 522 | { |
687228ad | 523 | struct ipic *ipic = ipic_from_irq(d->irq); |
476eb491 | 524 | unsigned int src = irqd_to_hwirq(d); |
b9f0f1bb | 525 | unsigned long flags; |
1da177e4 LT |
526 | u32 temp; |
527 | ||
a9e8bf21 | 528 | raw_spin_lock_irqsave(&ipic_lock, flags); |
b9f0f1bb | 529 | |
1da177e4 LT |
530 | temp = ipic_read(ipic->regs, ipic_info[src].mask); |
531 | temp |= (1 << (31 - ipic_info[src].bit)); | |
532 | ipic_write(ipic->regs, ipic_info[src].mask, temp); | |
b9f0f1bb | 533 | |
a9e8bf21 | 534 | raw_spin_unlock_irqrestore(&ipic_lock, flags); |
1da177e4 LT |
535 | } |
536 | ||
687228ad | 537 | static void ipic_mask_irq(struct irq_data *d) |
1da177e4 | 538 | { |
687228ad | 539 | struct ipic *ipic = ipic_from_irq(d->irq); |
476eb491 | 540 | unsigned int src = irqd_to_hwirq(d); |
b9f0f1bb | 541 | unsigned long flags; |
1da177e4 LT |
542 | u32 temp; |
543 | ||
a9e8bf21 | 544 | raw_spin_lock_irqsave(&ipic_lock, flags); |
b9f0f1bb | 545 | |
1da177e4 LT |
546 | temp = ipic_read(ipic->regs, ipic_info[src].mask); |
547 | temp &= ~(1 << (31 - ipic_info[src].bit)); | |
548 | ipic_write(ipic->regs, ipic_info[src].mask, temp); | |
b9f0f1bb | 549 | |
77d4309e LY |
550 | /* mb() can't guarantee that masking is finished. But it does finish |
551 | * for nearly all cases. */ | |
552 | mb(); | |
553 | ||
a9e8bf21 | 554 | raw_spin_unlock_irqrestore(&ipic_lock, flags); |
1da177e4 LT |
555 | } |
556 | ||
687228ad | 557 | static void ipic_ack_irq(struct irq_data *d) |
1da177e4 | 558 | { |
687228ad | 559 | struct ipic *ipic = ipic_from_irq(d->irq); |
476eb491 | 560 | unsigned int src = irqd_to_hwirq(d); |
b9f0f1bb | 561 | unsigned long flags; |
1da177e4 LT |
562 | u32 temp; |
563 | ||
a9e8bf21 | 564 | raw_spin_lock_irqsave(&ipic_lock, flags); |
1da177e4 | 565 | |
30c40469 | 566 | temp = 1 << (31 - ipic_info[src].bit); |
77d4309e LY |
567 | ipic_write(ipic->regs, ipic_info[src].ack, temp); |
568 | ||
569 | /* mb() can't guarantee that ack is finished. But it does finish | |
570 | * for nearly all cases. */ | |
571 | mb(); | |
b9f0f1bb | 572 | |
a9e8bf21 | 573 | raw_spin_unlock_irqrestore(&ipic_lock, flags); |
1da177e4 LT |
574 | } |
575 | ||
687228ad | 576 | static void ipic_mask_irq_and_ack(struct irq_data *d) |
1da177e4 | 577 | { |
687228ad | 578 | struct ipic *ipic = ipic_from_irq(d->irq); |
476eb491 | 579 | unsigned int src = irqd_to_hwirq(d); |
b9f0f1bb KP |
580 | unsigned long flags; |
581 | u32 temp; | |
582 | ||
a9e8bf21 | 583 | raw_spin_lock_irqsave(&ipic_lock, flags); |
b9f0f1bb KP |
584 | |
585 | temp = ipic_read(ipic->regs, ipic_info[src].mask); | |
586 | temp &= ~(1 << (31 - ipic_info[src].bit)); | |
587 | ipic_write(ipic->regs, ipic_info[src].mask, temp); | |
588 | ||
30c40469 | 589 | temp = 1 << (31 - ipic_info[src].bit); |
77d4309e LY |
590 | ipic_write(ipic->regs, ipic_info[src].ack, temp); |
591 | ||
592 | /* mb() can't guarantee that ack is finished. But it does finish | |
593 | * for nearly all cases. */ | |
594 | mb(); | |
b9f0f1bb | 595 | |
a9e8bf21 | 596 | raw_spin_unlock_irqrestore(&ipic_lock, flags); |
1da177e4 LT |
597 | } |
598 | ||
687228ad | 599 | static int ipic_set_irq_type(struct irq_data *d, unsigned int flow_type) |
b9f0f1bb | 600 | { |
687228ad | 601 | struct ipic *ipic = ipic_from_irq(d->irq); |
476eb491 | 602 | unsigned int src = irqd_to_hwirq(d); |
b9f0f1bb KP |
603 | unsigned int vold, vnew, edibit; |
604 | ||
605 | if (flow_type == IRQ_TYPE_NONE) | |
606 | flow_type = IRQ_TYPE_LEVEL_LOW; | |
607 | ||
608 | /* ipic supports only low assertion and high-to-low change senses | |
609 | */ | |
610 | if (!(flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING))) { | |
611 | printk(KERN_ERR "ipic: sense type 0x%x not supported\n", | |
612 | flow_type); | |
613 | return -EINVAL; | |
614 | } | |
77d4309e LY |
615 | /* ipic supports only edge mode on external interrupts */ |
616 | if ((flow_type & IRQ_TYPE_EDGE_FALLING) && !ipic_info[src].ack) { | |
617 | printk(KERN_ERR "ipic: edge sense not supported on internal " | |
618 | "interrupts\n"); | |
619 | return -EINVAL; | |
ecf4b196 | 620 | |
77d4309e | 621 | } |
b9f0f1bb | 622 | |
ecf4b196 | 623 | irqd_set_trigger_type(d, flow_type); |
b9f0f1bb | 624 | if (flow_type & IRQ_TYPE_LEVEL_LOW) { |
9758a7b0 | 625 | irq_set_handler_locked(d, handle_level_irq); |
ecf4b196 | 626 | d->chip = &ipic_level_irq_chip; |
b9f0f1bb | 627 | } else { |
9758a7b0 | 628 | irq_set_handler_locked(d, handle_edge_irq); |
ecf4b196 | 629 | d->chip = &ipic_edge_irq_chip; |
b9f0f1bb KP |
630 | } |
631 | ||
632 | /* only EXT IRQ senses are programmable on ipic | |
633 | * internal IRQ senses are LEVEL_LOW | |
634 | */ | |
635 | if (src == IPIC_IRQ_EXT0) | |
636 | edibit = 15; | |
637 | else | |
638 | if (src >= IPIC_IRQ_EXT1 && src <= IPIC_IRQ_EXT7) | |
639 | edibit = (14 - (src - IPIC_IRQ_EXT1)); | |
640 | else | |
641 | return (flow_type & IRQ_TYPE_LEVEL_LOW) ? 0 : -EINVAL; | |
642 | ||
643 | vold = ipic_read(ipic->regs, IPIC_SECNR); | |
644 | if ((flow_type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_FALLING) { | |
645 | vnew = vold | (1 << edibit); | |
646 | } else { | |
647 | vnew = vold & ~(1 << edibit); | |
648 | } | |
649 | if (vold != vnew) | |
650 | ipic_write(ipic->regs, IPIC_SECNR, vnew); | |
ecf4b196 | 651 | return IRQ_SET_MASK_OK_NOCOPY; |
b9f0f1bb KP |
652 | } |
653 | ||
77d4309e LY |
654 | /* level interrupts and edge interrupts have different ack operations */ |
655 | static struct irq_chip ipic_level_irq_chip = { | |
fc380c0c | 656 | .name = "IPIC", |
687228ad LB |
657 | .irq_unmask = ipic_unmask_irq, |
658 | .irq_mask = ipic_mask_irq, | |
659 | .irq_mask_ack = ipic_mask_irq, | |
660 | .irq_set_type = ipic_set_irq_type, | |
77d4309e LY |
661 | }; |
662 | ||
663 | static struct irq_chip ipic_edge_irq_chip = { | |
fc380c0c | 664 | .name = "IPIC", |
687228ad LB |
665 | .irq_unmask = ipic_unmask_irq, |
666 | .irq_mask = ipic_mask_irq, | |
667 | .irq_mask_ack = ipic_mask_irq_and_ack, | |
668 | .irq_ack = ipic_ack_irq, | |
669 | .irq_set_type = ipic_set_irq_type, | |
b9f0f1bb KP |
670 | }; |
671 | ||
ad3aedfb MZ |
672 | static int ipic_host_match(struct irq_domain *h, struct device_node *node, |
673 | enum irq_domain_bus_token bus_token) | |
b9f0f1bb | 674 | { |
b9f0f1bb | 675 | /* Exact match, unless ipic node is NULL */ |
5d4c9bc7 MZ |
676 | struct device_node *of_node = irq_domain_get_of_node(h); |
677 | return of_node == NULL || of_node == node; | |
b9f0f1bb KP |
678 | } |
679 | ||
bae1d8f1 | 680 | static int ipic_host_map(struct irq_domain *h, unsigned int virq, |
b9f0f1bb KP |
681 | irq_hw_number_t hw) |
682 | { | |
683 | struct ipic *ipic = h->host_data; | |
b9f0f1bb | 684 | |
ec775d0e TG |
685 | irq_set_chip_data(virq, ipic); |
686 | irq_set_chip_and_handler(virq, &ipic_level_irq_chip, handle_level_irq); | |
b9f0f1bb KP |
687 | |
688 | /* Set default irq type */ | |
ec775d0e | 689 | irq_set_irq_type(virq, IRQ_TYPE_NONE); |
b9f0f1bb KP |
690 | |
691 | return 0; | |
692 | } | |
693 | ||
202648a6 | 694 | static const struct irq_domain_ops ipic_host_ops = { |
b9f0f1bb KP |
695 | .match = ipic_host_match, |
696 | .map = ipic_host_map, | |
ff8c3ab8 | 697 | .xlate = irq_domain_xlate_onetwocell, |
1da177e4 LT |
698 | }; |
699 | ||
126186a0 | 700 | struct ipic * __init ipic_init(struct device_node *node, unsigned int flags) |
1da177e4 | 701 | { |
b9f0f1bb KP |
702 | struct ipic *ipic; |
703 | struct resource res; | |
704 | u32 temp = 0, ret; | |
705 | ||
84f1c1e0 ME |
706 | ret = of_address_to_resource(node, 0, &res); |
707 | if (ret) | |
708 | return NULL; | |
709 | ||
ea96025a | 710 | ipic = kzalloc(sizeof(*ipic), GFP_KERNEL); |
b9f0f1bb | 711 | if (ipic == NULL) |
126186a0 | 712 | return NULL; |
b9f0f1bb | 713 | |
a8db8cf0 GL |
714 | ipic->irqhost = irq_domain_add_linear(node, NR_IPIC_INTS, |
715 | &ipic_host_ops, ipic); | |
7a626b66 JL |
716 | if (ipic->irqhost == NULL) { |
717 | kfree(ipic); | |
126186a0 | 718 | return NULL; |
7a626b66 | 719 | } |
b9f0f1bb | 720 | |
28f65c11 | 721 | ipic->regs = ioremap(res.start, resource_size(&res)); |
1da177e4 | 722 | |
b9f0f1bb KP |
723 | /* init hw */ |
724 | ipic_write(ipic->regs, IPIC_SICNR, 0x0); | |
1da177e4 LT |
725 | |
726 | /* default priority scheme is grouped. If spread mode is required | |
727 | * configure SICFR accordingly */ | |
728 | if (flags & IPIC_SPREADMODE_GRP_A) | |
729 | temp |= SICFR_IPSA; | |
f03ca957 LY |
730 | if (flags & IPIC_SPREADMODE_GRP_B) |
731 | temp |= SICFR_IPSB; | |
732 | if (flags & IPIC_SPREADMODE_GRP_C) | |
733 | temp |= SICFR_IPSC; | |
1da177e4 LT |
734 | if (flags & IPIC_SPREADMODE_GRP_D) |
735 | temp |= SICFR_IPSD; | |
736 | if (flags & IPIC_SPREADMODE_MIX_A) | |
737 | temp |= SICFR_MPSA; | |
738 | if (flags & IPIC_SPREADMODE_MIX_B) | |
739 | temp |= SICFR_MPSB; | |
740 | ||
f03ca957 | 741 | ipic_write(ipic->regs, IPIC_SICFR, temp); |
1da177e4 LT |
742 | |
743 | /* handle MCP route */ | |
744 | temp = 0; | |
745 | if (flags & IPIC_DISABLE_MCP_OUT) | |
746 | temp = SERCR_MCPR; | |
b9f0f1bb | 747 | ipic_write(ipic->regs, IPIC_SERCR, temp); |
1da177e4 LT |
748 | |
749 | /* handle routing of IRQ0 to MCP */ | |
b9f0f1bb | 750 | temp = ipic_read(ipic->regs, IPIC_SEMSR); |
1da177e4 LT |
751 | |
752 | if (flags & IPIC_IRQ0_MCP) | |
753 | temp |= SEMSR_SIRQ0; | |
754 | else | |
755 | temp &= ~SEMSR_SIRQ0; | |
756 | ||
b9f0f1bb | 757 | ipic_write(ipic->regs, IPIC_SEMSR, temp); |
1da177e4 | 758 | |
b9f0f1bb KP |
759 | primary_ipic = ipic; |
760 | irq_set_default_host(primary_ipic->irqhost); | |
1da177e4 | 761 | |
8640d3bf SAS |
762 | ipic_write(ipic->regs, IPIC_SIMSR_H, 0); |
763 | ipic_write(ipic->regs, IPIC_SIMSR_L, 0); | |
764 | ||
b9f0f1bb KP |
765 | printk ("IPIC (%d IRQ sources) at %p\n", NR_IPIC_INTS, |
766 | primary_ipic->regs); | |
126186a0 KG |
767 | |
768 | return ipic; | |
1da177e4 LT |
769 | } |
770 | ||
6c552983 | 771 | void __init ipic_set_default_priority(void) |
1da177e4 | 772 | { |
f03ca957 LY |
773 | ipic_write(primary_ipic->regs, IPIC_SIPRR_A, IPIC_PRIORITY_DEFAULT); |
774 | ipic_write(primary_ipic->regs, IPIC_SIPRR_B, IPIC_PRIORITY_DEFAULT); | |
775 | ipic_write(primary_ipic->regs, IPIC_SIPRR_C, IPIC_PRIORITY_DEFAULT); | |
776 | ipic_write(primary_ipic->regs, IPIC_SIPRR_D, IPIC_PRIORITY_DEFAULT); | |
777 | ipic_write(primary_ipic->regs, IPIC_SMPRR_A, IPIC_PRIORITY_DEFAULT); | |
778 | ipic_write(primary_ipic->regs, IPIC_SMPRR_B, IPIC_PRIORITY_DEFAULT); | |
1da177e4 LT |
779 | } |
780 | ||
1da177e4 LT |
781 | u32 ipic_get_mcp_status(void) |
782 | { | |
6beb3381 | 783 | return primary_ipic ? ipic_read(primary_ipic->regs, IPIC_SERSR) : 0; |
1da177e4 LT |
784 | } |
785 | ||
786 | void ipic_clear_mcp_status(u32 mask) | |
787 | { | |
6b148a7c | 788 | ipic_write(primary_ipic->regs, IPIC_SERSR, mask); |
1da177e4 LT |
789 | } |
790 | ||
ef24ba70 | 791 | /* Return an interrupt vector or 0 if no interrupt is pending. */ |
35a84c2f | 792 | unsigned int ipic_get_irq(void) |
1da177e4 LT |
793 | { |
794 | int irq; | |
795 | ||
b9f0f1bb KP |
796 | BUG_ON(primary_ipic == NULL); |
797 | ||
798 | #define IPIC_SIVCR_VECTOR_MASK 0x7f | |
799 | irq = ipic_read(primary_ipic->regs, IPIC_SIVCR) & IPIC_SIVCR_VECTOR_MASK; | |
1da177e4 LT |
800 | |
801 | if (irq == 0) /* 0 --> no irq is pending */ | |
ef24ba70 | 802 | return 0; |
1da177e4 | 803 | |
b9f0f1bb | 804 | return irq_linear_revmap(primary_ipic->irqhost, irq); |
1da177e4 LT |
805 | } |
806 | ||
e2a02ba6 | 807 | #ifdef CONFIG_SUSPEND |
d49747bd SW |
808 | static struct { |
809 | u32 sicfr; | |
810 | u32 siprr[2]; | |
811 | u32 simsr[2]; | |
812 | u32 sicnr; | |
813 | u32 smprr[2]; | |
814 | u32 semsr; | |
815 | u32 secnr; | |
816 | u32 sermr; | |
817 | u32 sercr; | |
818 | } ipic_saved_state; | |
819 | ||
f5a592f7 | 820 | static int ipic_suspend(void) |
d49747bd SW |
821 | { |
822 | struct ipic *ipic = primary_ipic; | |
823 | ||
824 | ipic_saved_state.sicfr = ipic_read(ipic->regs, IPIC_SICFR); | |
825 | ipic_saved_state.siprr[0] = ipic_read(ipic->regs, IPIC_SIPRR_A); | |
826 | ipic_saved_state.siprr[1] = ipic_read(ipic->regs, IPIC_SIPRR_D); | |
827 | ipic_saved_state.simsr[0] = ipic_read(ipic->regs, IPIC_SIMSR_H); | |
828 | ipic_saved_state.simsr[1] = ipic_read(ipic->regs, IPIC_SIMSR_L); | |
829 | ipic_saved_state.sicnr = ipic_read(ipic->regs, IPIC_SICNR); | |
830 | ipic_saved_state.smprr[0] = ipic_read(ipic->regs, IPIC_SMPRR_A); | |
831 | ipic_saved_state.smprr[1] = ipic_read(ipic->regs, IPIC_SMPRR_B); | |
832 | ipic_saved_state.semsr = ipic_read(ipic->regs, IPIC_SEMSR); | |
833 | ipic_saved_state.secnr = ipic_read(ipic->regs, IPIC_SECNR); | |
834 | ipic_saved_state.sermr = ipic_read(ipic->regs, IPIC_SERMR); | |
835 | ipic_saved_state.sercr = ipic_read(ipic->regs, IPIC_SERCR); | |
836 | ||
837 | if (fsl_deep_sleep()) { | |
838 | /* In deep sleep, make sure there can be no | |
839 | * pending interrupts, as this can cause | |
840 | * problems on 831x. | |
841 | */ | |
842 | ipic_write(ipic->regs, IPIC_SIMSR_H, 0); | |
843 | ipic_write(ipic->regs, IPIC_SIMSR_L, 0); | |
844 | ipic_write(ipic->regs, IPIC_SEMSR, 0); | |
845 | ipic_write(ipic->regs, IPIC_SERMR, 0); | |
846 | } | |
847 | ||
848 | return 0; | |
849 | } | |
850 | ||
f5a592f7 | 851 | static void ipic_resume(void) |
d49747bd SW |
852 | { |
853 | struct ipic *ipic = primary_ipic; | |
854 | ||
855 | ipic_write(ipic->regs, IPIC_SICFR, ipic_saved_state.sicfr); | |
856 | ipic_write(ipic->regs, IPIC_SIPRR_A, ipic_saved_state.siprr[0]); | |
857 | ipic_write(ipic->regs, IPIC_SIPRR_D, ipic_saved_state.siprr[1]); | |
858 | ipic_write(ipic->regs, IPIC_SIMSR_H, ipic_saved_state.simsr[0]); | |
859 | ipic_write(ipic->regs, IPIC_SIMSR_L, ipic_saved_state.simsr[1]); | |
860 | ipic_write(ipic->regs, IPIC_SICNR, ipic_saved_state.sicnr); | |
861 | ipic_write(ipic->regs, IPIC_SMPRR_A, ipic_saved_state.smprr[0]); | |
862 | ipic_write(ipic->regs, IPIC_SMPRR_B, ipic_saved_state.smprr[1]); | |
863 | ipic_write(ipic->regs, IPIC_SEMSR, ipic_saved_state.semsr); | |
864 | ipic_write(ipic->regs, IPIC_SECNR, ipic_saved_state.secnr); | |
865 | ipic_write(ipic->regs, IPIC_SERMR, ipic_saved_state.sermr); | |
866 | ipic_write(ipic->regs, IPIC_SERCR, ipic_saved_state.sercr); | |
d49747bd SW |
867 | } |
868 | #else | |
869 | #define ipic_suspend NULL | |
870 | #define ipic_resume NULL | |
871 | #endif | |
872 | ||
f5a592f7 | 873 | static struct syscore_ops ipic_syscore_ops = { |
d49747bd SW |
874 | .suspend = ipic_suspend, |
875 | .resume = ipic_resume, | |
1da177e4 LT |
876 | }; |
877 | ||
f5a592f7 | 878 | static int __init init_ipic_syscore(void) |
1da177e4 | 879 | { |
1428a9fa | 880 | if (!primary_ipic || !primary_ipic->regs) |
1da177e4 | 881 | return -ENODEV; |
1da177e4 | 882 | |
f5a592f7 RW |
883 | printk(KERN_DEBUG "Registering ipic system core operations\n"); |
884 | register_syscore_ops(&ipic_syscore_ops); | |
885 | ||
1da177e4 LT |
886 | return 0; |
887 | } | |
888 | ||
f5a592f7 | 889 | subsys_initcall(init_ipic_syscore); |