[PATCH] ppc64: POWER5+ oprofile support
[linux-2.6] / arch / ppc / syslib / ppc4xx_pic.c
1 /*
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>
28 #include <asm/machdep.h>
29
30 /* See comment in include/arch-ppc/ppc4xx_pic.h
31  * for more info about these two variables
32  */
33 extern struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[NR_UICS]
34     __attribute__ ((weak));
35 extern 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)
41 #define IRQ_MASK_UIC3(irq)              IRQ_MASK_UICx(irq)
42
43 #define UIC_HANDLERS(n)                                                 \
44 static void ppc4xx_uic##n##_enable(unsigned int irq)                    \
45 {                                                                       \
46         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;                                 \
50         mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]);             \
51 }                                                                       \
52                                                                         \
53 static 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                                                                         \
60 static 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                                                                         \
69 static 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
92 #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);
97 UIC_HANDLERS(0);
98 UIC_HANDLERS(1);
99 UIC_HANDLERS(2);
100 UIC_HANDLERS(3);
101
102 static 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
115 static 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
124 #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);
127 UIC_HANDLERS(0);
128 UIC_HANDLERS(1);
129 UIC_HANDLERS(2);
130
131 static 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
144 static void __init ppc4xx_pic_impl_init(void)
145 {
146 #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
150         /* 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);
161 UIC_HANDLERS(0);
162 UIC_HANDLERS(1);
163
164 static 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
173 static 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
183 UIC_HANDLERS(0);
184
185 static 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
191 static inline void ppc4xx_pic_impl_init(void)
192 {
193 }
194 #endif
195
196 static 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 },
205 #if NR_UICS > 3
206         { .decl = DECLARE_UIC(3), .base = UIC3 },
207 #endif
208 #endif
209 #endif
210 };
211
212 static 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
218 void __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 }