blob: aa4165144ec27481c61ea55b286d5c51557e34a0 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * arch/ppc/syslib/ppc4xx_pic.c
3 *
4 * Interrupt controller driver for PowerPC 4xx-based processors.
5 *
6 * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
7 * Copyright (c) 2004, 2005 Zultys Technologies
8 *
9 * Based on original code by
10 * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
11 * Armin Custer <akuster@mvista.com>
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2 of the License, or (at your
16 * option) any later version.
17*/
18#include <linux/config.h>
19#include <linux/init.h>
20#include <linux/sched.h>
21#include <linux/signal.h>
22#include <linux/stddef.h>
23
24#include <asm/processor.h>
25#include <asm/system.h>
26#include <asm/irq.h>
27#include <asm/ppc4xx_pic.h>
Paul Mackerrasfd582ec2005-10-11 22:08:12 +100028#include <asm/machdep.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30/* See comment in include/arch-ppc/ppc4xx_pic.h
31 * for more info about these two variables
32 */
33extern struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[NR_UICS]
34 __attribute__ ((weak));
35extern unsigned char ppc4xx_uic_ext_irq_cfg[] __attribute__ ((weak));
36
37#define IRQ_MASK_UIC0(irq) (1 << (31 - (irq)))
38#define IRQ_MASK_UICx(irq) (1 << (31 - ((irq) & 0x1f)))
39#define IRQ_MASK_UIC1(irq) IRQ_MASK_UICx(irq)
40#define IRQ_MASK_UIC2(irq) IRQ_MASK_UICx(irq)
Roland Dreierb0f7b8b2005-11-07 00:58:13 -080041#define IRQ_MASK_UIC3(irq) IRQ_MASK_UICx(irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
43#define UIC_HANDLERS(n) \
44static void ppc4xx_uic##n##_enable(unsigned int irq) \
45{ \
Eugene Surovegin35b535d2005-04-16 15:24:15 -070046 u32 mask = IRQ_MASK_UIC##n(irq); \
47 if (irq_desc[irq].status & IRQ_LEVEL) \
48 mtdcr(DCRN_UIC_SR(UIC##n), mask); \
49 ppc_cached_irq_mask[n] |= mask; \
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
51} \
52 \
53static void ppc4xx_uic##n##_disable(unsigned int irq) \
54{ \
55 ppc_cached_irq_mask[n] &= ~IRQ_MASK_UIC##n(irq); \
56 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
57 ACK_UIC##n##_PARENT \
58} \
59 \
60static void ppc4xx_uic##n##_ack(unsigned int irq) \
61{ \
62 u32 mask = IRQ_MASK_UIC##n(irq); \
63 ppc_cached_irq_mask[n] &= ~mask; \
64 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
65 mtdcr(DCRN_UIC_SR(UIC##n), mask); \
66 ACK_UIC##n##_PARENT \
67} \
68 \
69static void ppc4xx_uic##n##_end(unsigned int irq) \
70{ \
71 unsigned int status = irq_desc[irq].status; \
72 u32 mask = IRQ_MASK_UIC##n(irq); \
73 if (status & IRQ_LEVEL) { \
74 mtdcr(DCRN_UIC_SR(UIC##n), mask); \
75 ACK_UIC##n##_PARENT \
76 } \
77 if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { \
78 ppc_cached_irq_mask[n] |= mask; \
79 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
80 } \
81}
82
83#define DECLARE_UIC(n) \
84{ \
85 .typename = "UIC"#n, \
86 .enable = ppc4xx_uic##n##_enable, \
87 .disable = ppc4xx_uic##n##_disable, \
88 .ack = ppc4xx_uic##n##_ack, \
89 .end = ppc4xx_uic##n##_end, \
90} \
91
Roland Dreierb0f7b8b2005-11-07 00:58:13 -080092#if NR_UICS == 4
93#define ACK_UIC0_PARENT
94#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
95#define ACK_UIC2_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC2NC);
96#define ACK_UIC3_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC3NC);
97UIC_HANDLERS(0);
98UIC_HANDLERS(1);
99UIC_HANDLERS(2);
100UIC_HANDLERS(3);
101
102static int ppc4xx_pic_get_irq(struct pt_regs *regs)
103{
104 u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
105 if (uic0 & UIC0_UIC1NC)
106 return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
107 else if (uic0 & UIC0_UIC2NC)
108 return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2)));
109 else if (uic0 & UIC0_UIC3NC)
110 return 128 - ffs(mfdcr(DCRN_UIC_MSR(UIC3)));
111 else
112 return uic0 ? 32 - ffs(uic0) : -1;
113}
114
115static void __init ppc4xx_pic_impl_init(void)
116{
117 /* Enable cascade interrupts in UIC0 */
118 ppc_cached_irq_mask[0] |= UIC0_UIC1NC | UIC0_UIC2NC | UIC0_UIC3NC;
119 mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC | UIC0_UIC2NC | UIC0_UIC3NC);
120 mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[0]);
121}
122
123#elif NR_UICS == 3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124#define ACK_UIC0_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC0NC);
125#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC1NC);
126#define ACK_UIC2_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC2NC);
127UIC_HANDLERS(0);
128UIC_HANDLERS(1);
129UIC_HANDLERS(2);
130
131static int ppc4xx_pic_get_irq(struct pt_regs *regs)
132{
133 u32 uicb = mfdcr(DCRN_UIC_MSR(UICB));
134 if (uicb & UICB_UIC0NC)
135 return 32 - ffs(mfdcr(DCRN_UIC_MSR(UIC0)));
136 else if (uicb & UICB_UIC1NC)
137 return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
138 else if (uicb & UICB_UIC2NC)
139 return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2)));
140 else
141 return -1;
142}
143
144static void __init ppc4xx_pic_impl_init(void)
145{
Eugene Surovegin4b1294f2005-07-05 18:54:45 -0700146#if defined(CONFIG_440GX)
147 /* Disable 440GP compatibility mode if it was enabled in firmware */
148 SDR_WRITE(DCRN_SDR_MFR, SDR_READ(DCRN_SDR_MFR) & ~DCRN_SDR_MFR_PCM);
149#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 /* Configure Base UIC */
151 mtdcr(DCRN_UIC_CR(UICB), 0);
152 mtdcr(DCRN_UIC_TR(UICB), 0);
153 mtdcr(DCRN_UIC_PR(UICB), 0xffffffff);
154 mtdcr(DCRN_UIC_SR(UICB), 0xffffffff);
155 mtdcr(DCRN_UIC_ER(UICB), UICB_UIC0NC | UICB_UIC1NC | UICB_UIC2NC);
156}
157
158#elif NR_UICS == 2
159#define ACK_UIC0_PARENT
160#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
161UIC_HANDLERS(0);
162UIC_HANDLERS(1);
163
164static int ppc4xx_pic_get_irq(struct pt_regs *regs)
165{
166 u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
167 if (uic0 & UIC0_UIC1NC)
168 return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
169 else
170 return uic0 ? 32 - ffs(uic0) : -1;
171}
172
173static void __init ppc4xx_pic_impl_init(void)
174{
175 /* Enable cascade interrupt in UIC0 */
176 ppc_cached_irq_mask[0] |= UIC0_UIC1NC;
177 mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
178 mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[0]);
179}
180
181#elif NR_UICS == 1
182#define ACK_UIC0_PARENT
183UIC_HANDLERS(0);
184
185static int ppc4xx_pic_get_irq(struct pt_regs *regs)
186{
187 u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
188 return uic0 ? 32 - ffs(uic0) : -1;
189}
190
191static inline void ppc4xx_pic_impl_init(void)
192{
193}
194#endif
195
196static struct ppc4xx_uic_impl {
197 struct hw_interrupt_type decl;
198 int base; /* Base DCR number */
199} __uic[] = {
200 { .decl = DECLARE_UIC(0), .base = UIC0 },
201#if NR_UICS > 1
202 { .decl = DECLARE_UIC(1), .base = UIC1 },
203#if NR_UICS > 2
204 { .decl = DECLARE_UIC(2), .base = UIC2 },
Roland Dreierb0f7b8b2005-11-07 00:58:13 -0800205#if NR_UICS > 3
206 { .decl = DECLARE_UIC(3), .base = UIC3 },
207#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208#endif
209#endif
210};
211
212static inline int is_level_sensitive(int irq)
213{
214 u32 tr = mfdcr(DCRN_UIC_TR(__uic[irq >> 5].base));
215 return (tr & IRQ_MASK_UICx(irq)) == 0;
216}
217
218void __init ppc4xx_pic_init(void)
219{
220 int i;
221 unsigned char *eirqs = ppc4xx_uic_ext_irq_cfg;
222
223 for (i = 0; i < NR_UICS; ++i) {
224 int base = __uic[i].base;
225
226 /* Disable everything by default */
227 ppc_cached_irq_mask[i] = 0;
228 mtdcr(DCRN_UIC_ER(base), 0);
229
230 /* We don't use critical interrupts */
231 mtdcr(DCRN_UIC_CR(base), 0);
232
233 /* Configure polarity and triggering */
234 if (ppc4xx_core_uic_cfg) {
235 struct ppc4xx_uic_settings *p = ppc4xx_core_uic_cfg + i;
236 u32 mask = p->ext_irq_mask;
237 u32 pr = mfdcr(DCRN_UIC_PR(base)) & mask;
238 u32 tr = mfdcr(DCRN_UIC_TR(base)) & mask;
239
240 /* "Fixed" interrupts (on-chip devices) */
241 pr |= p->polarity & ~mask;
242 tr |= p->triggering & ~mask;
243
244 /* Merge external IRQs settings if board port
245 * provided them
246 */
247 if (eirqs && mask) {
248 pr &= ~mask;
249 tr &= ~mask;
250 while (mask) {
251 /* Extract current external IRQ mask */
252 u32 eirq_mask = 1 << __ilog2(mask);
253
254 if (!(*eirqs & IRQ_SENSE_LEVEL))
255 tr |= eirq_mask;
256
257 if (*eirqs & IRQ_POLARITY_POSITIVE)
258 pr |= eirq_mask;
259
260 mask &= ~eirq_mask;
261 ++eirqs;
262 }
263 }
264 mtdcr(DCRN_UIC_PR(base), pr);
265 mtdcr(DCRN_UIC_TR(base), tr);
266 }
267
268 /* ACK any pending interrupts to prevent false
269 * triggering after first enable
270 */
271 mtdcr(DCRN_UIC_SR(base), 0xffffffff);
272 }
273
274 /* Perform optional implementation specific setup
275 * (e.g. enable cascade interrupts for multi-UIC configurations)
276 */
277 ppc4xx_pic_impl_init();
278
279 /* Attach low-level handlers */
280 for (i = 0; i < (NR_UICS << 5); ++i) {
281 irq_desc[i].handler = &__uic[i >> 5].decl;
282 if (is_level_sensitive(i))
283 irq_desc[i].status |= IRQ_LEVEL;
284 }
285
286 ppc_md.get_irq = ppc4xx_pic_get_irq;
287}