2 * Interrupt controller driver for PowerPC 4xx-based processors.
4 * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
5 * Copyright (c) 2004, 2005 Zultys Technologies
7 * Based on original code by
8 * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
9 * Armin Custer <akuster@mvista.com>
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
16 #include <linux/config.h>
17 #include <linux/init.h>
18 #include <linux/sched.h>
19 #include <linux/signal.h>
20 #include <linux/stddef.h>
22 #include <asm/processor.h>
23 #include <asm/system.h>
25 #include <asm/ppc4xx_pic.h>
26 #include <asm/machdep.h>
28 /* See comment in include/arch-ppc/ppc4xx_pic.h
29 * for more info about these two variables
31 extern struct ppc4xx_uic_settings ppc4xx_core_uic_cfg
[NR_UICS
]
32 __attribute__ ((weak
));
33 extern unsigned char ppc4xx_uic_ext_irq_cfg
[] __attribute__ ((weak
));
35 #define IRQ_MASK_UIC0(irq) (1 << (31 - (irq)))
36 #define IRQ_MASK_UICx(irq) (1 << (31 - ((irq) & 0x1f)))
37 #define IRQ_MASK_UIC1(irq) IRQ_MASK_UICx(irq)
38 #define IRQ_MASK_UIC2(irq) IRQ_MASK_UICx(irq)
39 #define IRQ_MASK_UIC3(irq) IRQ_MASK_UICx(irq)
41 #define UIC_HANDLERS(n) \
42 static void ppc4xx_uic##n##_enable(unsigned int irq) \
44 u32 mask = IRQ_MASK_UIC##n(irq); \
45 if (irq_desc[irq].status & IRQ_LEVEL) \
46 mtdcr(DCRN_UIC_SR(UIC##n), mask); \
47 ppc_cached_irq_mask[n] |= mask; \
48 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
51 static void ppc4xx_uic##n##_disable(unsigned int irq) \
53 ppc_cached_irq_mask[n] &= ~IRQ_MASK_UIC##n(irq); \
54 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
58 static void ppc4xx_uic##n##_ack(unsigned int irq) \
60 u32 mask = IRQ_MASK_UIC##n(irq); \
61 ppc_cached_irq_mask[n] &= ~mask; \
62 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
63 mtdcr(DCRN_UIC_SR(UIC##n), mask); \
67 static void ppc4xx_uic##n##_end(unsigned int irq) \
69 unsigned int status = irq_desc[irq].status; \
70 u32 mask = IRQ_MASK_UIC##n(irq); \
71 if (status & IRQ_LEVEL) { \
72 mtdcr(DCRN_UIC_SR(UIC##n), mask); \
75 if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { \
76 ppc_cached_irq_mask[n] |= mask; \
77 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
81 #define DECLARE_UIC(n) \
83 .typename = "UIC"#n, \
84 .enable = ppc4xx_uic##n##_enable, \
85 .disable = ppc4xx_uic##n##_disable, \
86 .ack = ppc4xx_uic##n##_ack, \
87 .end = ppc4xx_uic##n##_end, \
91 #define ACK_UIC0_PARENT
92 #define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
93 #define ACK_UIC2_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC2NC);
94 #define ACK_UIC3_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC3NC);
100 static int ppc4xx_pic_get_irq(struct pt_regs
*regs
)
102 u32 uic0
= mfdcr(DCRN_UIC_MSR(UIC0
));
103 if (uic0
& UIC0_UIC1NC
)
104 return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1
)));
105 else if (uic0
& UIC0_UIC2NC
)
106 return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2
)));
107 else if (uic0
& UIC0_UIC3NC
)
108 return 128 - ffs(mfdcr(DCRN_UIC_MSR(UIC3
)));
110 return uic0
? 32 - ffs(uic0
) : -1;
113 static void __init
ppc4xx_pic_impl_init(void)
115 /* Enable cascade interrupts in UIC0 */
116 ppc_cached_irq_mask
[0] |= UIC0_UIC1NC
| UIC0_UIC2NC
| UIC0_UIC3NC
;
117 mtdcr(DCRN_UIC_SR(UIC0
), UIC0_UIC1NC
| UIC0_UIC2NC
| UIC0_UIC3NC
);
118 mtdcr(DCRN_UIC_ER(UIC0
), ppc_cached_irq_mask
[0]);
122 #define ACK_UIC0_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC0NC);
123 #define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC1NC);
124 #define ACK_UIC2_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC2NC);
129 static int ppc4xx_pic_get_irq(struct pt_regs
*regs
)
131 u32 uicb
= mfdcr(DCRN_UIC_MSR(UICB
));
132 if (uicb
& UICB_UIC0NC
)
133 return 32 - ffs(mfdcr(DCRN_UIC_MSR(UIC0
)));
134 else if (uicb
& UICB_UIC1NC
)
135 return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1
)));
136 else if (uicb
& UICB_UIC2NC
)
137 return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2
)));
142 static void __init
ppc4xx_pic_impl_init(void)
144 #if defined(CONFIG_440GX)
145 /* Disable 440GP compatibility mode if it was enabled in firmware */
146 SDR_WRITE(DCRN_SDR_MFR
, SDR_READ(DCRN_SDR_MFR
) & ~DCRN_SDR_MFR_PCM
);
148 /* Configure Base UIC */
149 mtdcr(DCRN_UIC_CR(UICB
), 0);
150 mtdcr(DCRN_UIC_TR(UICB
), 0);
151 mtdcr(DCRN_UIC_PR(UICB
), 0xffffffff);
152 mtdcr(DCRN_UIC_SR(UICB
), 0xffffffff);
153 mtdcr(DCRN_UIC_ER(UICB
), UICB_UIC0NC
| UICB_UIC1NC
| UICB_UIC2NC
);
157 #define ACK_UIC0_PARENT
158 #define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
162 static int ppc4xx_pic_get_irq(struct pt_regs
*regs
)
164 u32 uic0
= mfdcr(DCRN_UIC_MSR(UIC0
));
165 if (uic0
& UIC0_UIC1NC
)
166 return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1
)));
168 return uic0
? 32 - ffs(uic0
) : -1;
171 static void __init
ppc4xx_pic_impl_init(void)
173 /* Enable cascade interrupt in UIC0 */
174 ppc_cached_irq_mask
[0] |= UIC0_UIC1NC
;
175 mtdcr(DCRN_UIC_SR(UIC0
), UIC0_UIC1NC
);
176 mtdcr(DCRN_UIC_ER(UIC0
), ppc_cached_irq_mask
[0]);
180 #define ACK_UIC0_PARENT
183 static int ppc4xx_pic_get_irq(struct pt_regs
*regs
)
185 u32 uic0
= mfdcr(DCRN_UIC_MSR(UIC0
));
186 return uic0
? 32 - ffs(uic0
) : -1;
189 static inline void ppc4xx_pic_impl_init(void)
194 static struct ppc4xx_uic_impl
{
195 struct hw_interrupt_type decl
;
196 int base
; /* Base DCR number */
198 { .decl
= DECLARE_UIC(0), .base
= UIC0
},
200 { .decl
= DECLARE_UIC(1), .base
= UIC1
},
202 { .decl
= DECLARE_UIC(2), .base
= UIC2
},
204 { .decl
= DECLARE_UIC(3), .base
= UIC3
},
210 static inline int is_level_sensitive(int irq
)
212 u32 tr
= mfdcr(DCRN_UIC_TR(__uic
[irq
>> 5].base
));
213 return (tr
& IRQ_MASK_UICx(irq
)) == 0;
216 void __init
ppc4xx_pic_init(void)
219 unsigned char *eirqs
= ppc4xx_uic_ext_irq_cfg
;
221 for (i
= 0; i
< NR_UICS
; ++i
) {
222 int base
= __uic
[i
].base
;
224 /* Disable everything by default */
225 ppc_cached_irq_mask
[i
] = 0;
226 mtdcr(DCRN_UIC_ER(base
), 0);
228 /* We don't use critical interrupts */
229 mtdcr(DCRN_UIC_CR(base
), 0);
231 /* Configure polarity and triggering */
232 if (ppc4xx_core_uic_cfg
) {
233 struct ppc4xx_uic_settings
*p
= ppc4xx_core_uic_cfg
+ i
;
234 u32 mask
= p
->ext_irq_mask
;
235 u32 pr
= mfdcr(DCRN_UIC_PR(base
)) & mask
;
236 u32 tr
= mfdcr(DCRN_UIC_TR(base
)) & mask
;
238 /* "Fixed" interrupts (on-chip devices) */
239 pr
|= p
->polarity
& ~mask
;
240 tr
|= p
->triggering
& ~mask
;
242 /* Merge external IRQs settings if board port
249 /* Extract current external IRQ mask */
250 u32 eirq_mask
= 1 << __ilog2(mask
);
252 if (!(*eirqs
& IRQ_SENSE_LEVEL
))
255 if (*eirqs
& IRQ_POLARITY_POSITIVE
)
262 mtdcr(DCRN_UIC_PR(base
), pr
);
263 mtdcr(DCRN_UIC_TR(base
), tr
);
266 /* ACK any pending interrupts to prevent false
267 * triggering after first enable
269 mtdcr(DCRN_UIC_SR(base
), 0xffffffff);
272 /* Perform optional implementation specific setup
273 * (e.g. enable cascade interrupts for multi-UIC configurations)
275 ppc4xx_pic_impl_init();
277 /* Attach low-level handlers */
278 for (i
= 0; i
< (NR_UICS
<< 5); ++i
) {
279 irq_desc
[i
].handler
= &__uic
[i
>> 5].decl
;
280 if (is_level_sensitive(i
))
281 irq_desc
[i
].status
|= IRQ_LEVEL
;
284 ppc_md
.get_irq
= ppc4xx_pic_get_irq
;