blob: fd9af0fc0e9f43ddce46abdba519b73e6c6c1810 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * Interrupt controller driver for PowerPC 4xx-based processors.
3 *
4 * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
5 * Copyright (c) 2004, 2005 Zultys Technologies
6 *
7 * Based on original code by
8 * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
9 * Armin Custer <akuster@mvista.com>
10 *
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.
15*/
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>
21
22#include <asm/processor.h>
23#include <asm/system.h>
24#include <asm/irq.h>
25#include <asm/ppc4xx_pic.h>
Paul Mackerrasfd582ec2005-10-11 22:08:12 +100026#include <asm/machdep.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027
28/* See comment in include/arch-ppc/ppc4xx_pic.h
29 * for more info about these two variables
30 */
31extern struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[NR_UICS]
32 __attribute__ ((weak));
33extern unsigned char ppc4xx_uic_ext_irq_cfg[] __attribute__ ((weak));
34
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)
Roland Dreierb0f7b8b2005-11-07 00:58:13 -080039#define IRQ_MASK_UIC3(irq) IRQ_MASK_UICx(irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
41#define UIC_HANDLERS(n) \
42static void ppc4xx_uic##n##_enable(unsigned int irq) \
43{ \
Eugene Surovegin35b535d2005-04-16 15:24:15 -070044 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; \
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
49} \
50 \
51static void ppc4xx_uic##n##_disable(unsigned int irq) \
52{ \
53 ppc_cached_irq_mask[n] &= ~IRQ_MASK_UIC##n(irq); \
54 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
55 ACK_UIC##n##_PARENT \
56} \
57 \
58static void ppc4xx_uic##n##_ack(unsigned int irq) \
59{ \
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); \
64 ACK_UIC##n##_PARENT \
65} \
66 \
67static void ppc4xx_uic##n##_end(unsigned int irq) \
68{ \
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); \
73 ACK_UIC##n##_PARENT \
74 } \
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]); \
78 } \
79}
80
81#define DECLARE_UIC(n) \
82{ \
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, \
88} \
89
Roland Dreierb0f7b8b2005-11-07 00:58:13 -080090#if NR_UICS == 4
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);
95UIC_HANDLERS(0);
96UIC_HANDLERS(1);
97UIC_HANDLERS(2);
98UIC_HANDLERS(3);
99
100static int ppc4xx_pic_get_irq(struct pt_regs *regs)
101{
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)));
109 else
110 return uic0 ? 32 - ffs(uic0) : -1;
111}
112
113static void __init ppc4xx_pic_impl_init(void)
114{
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]);
119}
120
121#elif NR_UICS == 3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122#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);
125UIC_HANDLERS(0);
126UIC_HANDLERS(1);
127UIC_HANDLERS(2);
128
129static int ppc4xx_pic_get_irq(struct pt_regs *regs)
130{
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)));
138 else
139 return -1;
140}
141
142static void __init ppc4xx_pic_impl_init(void)
143{
Eugene Surovegin4b1294f2005-07-05 18:54:45 -0700144#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);
147#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 /* 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);
154}
155
156#elif NR_UICS == 2
157#define ACK_UIC0_PARENT
158#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
159UIC_HANDLERS(0);
160UIC_HANDLERS(1);
161
162static int ppc4xx_pic_get_irq(struct pt_regs *regs)
163{
164 u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
165 if (uic0 & UIC0_UIC1NC)
166 return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
167 else
168 return uic0 ? 32 - ffs(uic0) : -1;
169}
170
171static void __init ppc4xx_pic_impl_init(void)
172{
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]);
177}
178
179#elif NR_UICS == 1
180#define ACK_UIC0_PARENT
181UIC_HANDLERS(0);
182
183static int ppc4xx_pic_get_irq(struct pt_regs *regs)
184{
185 u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
186 return uic0 ? 32 - ffs(uic0) : -1;
187}
188
189static inline void ppc4xx_pic_impl_init(void)
190{
191}
192#endif
193
194static struct ppc4xx_uic_impl {
195 struct hw_interrupt_type decl;
196 int base; /* Base DCR number */
197} __uic[] = {
198 { .decl = DECLARE_UIC(0), .base = UIC0 },
199#if NR_UICS > 1
200 { .decl = DECLARE_UIC(1), .base = UIC1 },
201#if NR_UICS > 2
202 { .decl = DECLARE_UIC(2), .base = UIC2 },
Roland Dreierb0f7b8b2005-11-07 00:58:13 -0800203#if NR_UICS > 3
204 { .decl = DECLARE_UIC(3), .base = UIC3 },
205#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206#endif
207#endif
208};
209
210static inline int is_level_sensitive(int irq)
211{
212 u32 tr = mfdcr(DCRN_UIC_TR(__uic[irq >> 5].base));
213 return (tr & IRQ_MASK_UICx(irq)) == 0;
214}
215
216void __init ppc4xx_pic_init(void)
217{
218 int i;
219 unsigned char *eirqs = ppc4xx_uic_ext_irq_cfg;
220
221 for (i = 0; i < NR_UICS; ++i) {
222 int base = __uic[i].base;
223
224 /* Disable everything by default */
225 ppc_cached_irq_mask[i] = 0;
226 mtdcr(DCRN_UIC_ER(base), 0);
227
228 /* We don't use critical interrupts */
229 mtdcr(DCRN_UIC_CR(base), 0);
230
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;
237
238 /* "Fixed" interrupts (on-chip devices) */
239 pr |= p->polarity & ~mask;
240 tr |= p->triggering & ~mask;
241
242 /* Merge external IRQs settings if board port
243 * provided them
244 */
245 if (eirqs && mask) {
246 pr &= ~mask;
247 tr &= ~mask;
248 while (mask) {
249 /* Extract current external IRQ mask */
250 u32 eirq_mask = 1 << __ilog2(mask);
251
252 if (!(*eirqs & IRQ_SENSE_LEVEL))
253 tr |= eirq_mask;
254
255 if (*eirqs & IRQ_POLARITY_POSITIVE)
256 pr |= eirq_mask;
257
258 mask &= ~eirq_mask;
259 ++eirqs;
260 }
261 }
262 mtdcr(DCRN_UIC_PR(base), pr);
263 mtdcr(DCRN_UIC_TR(base), tr);
264 }
265
266 /* ACK any pending interrupts to prevent false
267 * triggering after first enable
268 */
269 mtdcr(DCRN_UIC_SR(base), 0xffffffff);
270 }
271
272 /* Perform optional implementation specific setup
273 * (e.g. enable cascade interrupts for multi-UIC configurations)
274 */
275 ppc4xx_pic_impl_init();
276
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;
282 }
283
284 ppc_md.get_irq = ppc4xx_pic_get_irq;
285}