Merge tag 'r8169-upstream-20061204-00' of git://electric-eye.fr.zoreil.com/home/romie...
[linux-2.6] / arch / ppc / syslib / ppc4xx_pic.c
1 /*
2  * 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/init.h>
17 #include <linux/sched.h>
18 #include <linux/signal.h>
19 #include <linux/stddef.h>
20
21 #include <asm/processor.h>
22 #include <asm/system.h>
23 #include <asm/irq.h>
24 #include <asm/ppc4xx_pic.h>
25 #include <asm/machdep.h>
26
27 /* See comment in include/arch-ppc/ppc4xx_pic.h
28  * for more info about these two variables
29  */
30 extern struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[NR_UICS]
31     __attribute__ ((weak));
32 extern unsigned char ppc4xx_uic_ext_irq_cfg[] __attribute__ ((weak));
33
34 #define IRQ_MASK_UIC0(irq)              (1 << (31 - (irq)))
35 #define IRQ_MASK_UICx(irq)              (1 << (31 - ((irq) & 0x1f)))
36 #define IRQ_MASK_UIC1(irq)              IRQ_MASK_UICx(irq)
37 #define IRQ_MASK_UIC2(irq)              IRQ_MASK_UICx(irq)
38 #define IRQ_MASK_UIC3(irq)              IRQ_MASK_UICx(irq)
39
40 #define UIC_HANDLERS(n)                                                 \
41 static void ppc4xx_uic##n##_enable(unsigned int irq)                    \
42 {                                                                       \
43         u32 mask = IRQ_MASK_UIC##n(irq);                                \
44         if (irq_desc[irq].status & IRQ_LEVEL)                           \
45                 mtdcr(DCRN_UIC_SR(UIC##n), mask);                       \
46         ppc_cached_irq_mask[n] |= mask;                                 \
47         mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]);             \
48 }                                                                       \
49                                                                         \
50 static void ppc4xx_uic##n##_disable(unsigned int irq)                   \
51 {                                                                       \
52         ppc_cached_irq_mask[n] &= ~IRQ_MASK_UIC##n(irq);                \
53         mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]);             \
54         ACK_UIC##n##_PARENT                                             \
55 }                                                                       \
56                                                                         \
57 static void ppc4xx_uic##n##_ack(unsigned int irq)                       \
58 {                                                                       \
59         u32 mask = IRQ_MASK_UIC##n(irq);                                \
60         ppc_cached_irq_mask[n] &= ~mask;                                \
61         mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]);             \
62         mtdcr(DCRN_UIC_SR(UIC##n), mask);                               \
63         ACK_UIC##n##_PARENT                                             \
64 }                                                                       \
65                                                                         \
66 static void ppc4xx_uic##n##_end(unsigned int irq)                       \
67 {                                                                       \
68         unsigned int status = irq_desc[irq].status;                     \
69         u32 mask = IRQ_MASK_UIC##n(irq);                                \
70         if (status & IRQ_LEVEL) {                                       \
71                 mtdcr(DCRN_UIC_SR(UIC##n), mask);                       \
72                 ACK_UIC##n##_PARENT                                     \
73         }                                                               \
74         if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {              \
75                 ppc_cached_irq_mask[n] |= mask;                         \
76                 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]);     \
77         }                                                               \
78 }
79
80 #define DECLARE_UIC(n)                                                  \
81 {                                                                       \
82         .typename       = "UIC"#n,                                      \
83         .enable         = ppc4xx_uic##n##_enable,                       \
84         .disable        = ppc4xx_uic##n##_disable,                      \
85         .ack            = ppc4xx_uic##n##_ack,                          \
86         .end            = ppc4xx_uic##n##_end,                          \
87 }                                                                       \
88
89 #if NR_UICS == 4
90 #define ACK_UIC0_PARENT
91 #define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
92 #define ACK_UIC2_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC2NC);
93 #define ACK_UIC3_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC3NC);
94 UIC_HANDLERS(0);
95 UIC_HANDLERS(1);
96 UIC_HANDLERS(2);
97 UIC_HANDLERS(3);
98
99 static int ppc4xx_pic_get_irq(void)
100 {
101         u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
102         if (uic0 & UIC0_UIC1NC)
103                 return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
104         else if (uic0 & UIC0_UIC2NC)
105                 return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2)));
106         else if (uic0 & UIC0_UIC3NC)
107                 return 128 - ffs(mfdcr(DCRN_UIC_MSR(UIC3)));
108         else
109                 return uic0 ? 32 - ffs(uic0) : -1;
110 }
111
112 static void __init ppc4xx_pic_impl_init(void)
113 {
114         /* Enable cascade interrupts in UIC0 */
115         ppc_cached_irq_mask[0] |= UIC0_UIC1NC | UIC0_UIC2NC | UIC0_UIC3NC;
116         mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC | UIC0_UIC2NC | UIC0_UIC3NC);
117         mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[0]);
118 }
119
120 #elif NR_UICS == 3
121 #define ACK_UIC0_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC0NC);
122 #define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC1NC);
123 #define ACK_UIC2_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC2NC);
124 UIC_HANDLERS(0);
125 UIC_HANDLERS(1);
126 UIC_HANDLERS(2);
127
128 static int ppc4xx_pic_get_irq(void)
129 {
130         u32 uicb = mfdcr(DCRN_UIC_MSR(UICB));
131         if (uicb & UICB_UIC0NC)
132                 return 32 - ffs(mfdcr(DCRN_UIC_MSR(UIC0)));
133         else if (uicb & UICB_UIC1NC)
134                 return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
135         else if (uicb & UICB_UIC2NC)
136                 return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2)));
137         else
138                 return -1;
139 }
140
141 static void __init ppc4xx_pic_impl_init(void)
142 {
143 #if defined(CONFIG_440GX)
144         /* Disable 440GP compatibility mode if it was enabled in firmware */
145         SDR_WRITE(DCRN_SDR_MFR, SDR_READ(DCRN_SDR_MFR) & ~DCRN_SDR_MFR_PCM);
146 #endif
147         /* Configure Base UIC */
148         mtdcr(DCRN_UIC_CR(UICB), 0);
149         mtdcr(DCRN_UIC_TR(UICB), 0);
150         mtdcr(DCRN_UIC_PR(UICB), 0xffffffff);
151         mtdcr(DCRN_UIC_SR(UICB), 0xffffffff);
152         mtdcr(DCRN_UIC_ER(UICB), UICB_UIC0NC | UICB_UIC1NC | UICB_UIC2NC);
153 }
154
155 #elif NR_UICS == 2
156 #define ACK_UIC0_PARENT
157 #define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
158 UIC_HANDLERS(0);
159 UIC_HANDLERS(1);
160
161 static int ppc4xx_pic_get_irq(void)
162 {
163         u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
164         if (uic0 & UIC0_UIC1NC)
165                 return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
166         else
167                 return uic0 ? 32 - ffs(uic0) : -1;
168 }
169
170 static void __init ppc4xx_pic_impl_init(void)
171 {
172         /* Enable cascade interrupt in UIC0 */
173         ppc_cached_irq_mask[0] |= UIC0_UIC1NC;
174         mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
175         mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[0]);
176 }
177
178 #elif NR_UICS == 1
179 #define ACK_UIC0_PARENT
180 UIC_HANDLERS(0);
181
182 static int ppc4xx_pic_get_irq(void)
183 {
184         u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
185         return uic0 ? 32 - ffs(uic0) : -1;
186 }
187
188 static inline void ppc4xx_pic_impl_init(void)
189 {
190 }
191 #endif
192
193 static struct ppc4xx_uic_impl {
194         struct hw_interrupt_type decl;
195         int base;                       /* Base DCR number */
196 } __uic[] = {
197         { .decl = DECLARE_UIC(0), .base = UIC0 },
198 #if NR_UICS > 1
199         { .decl = DECLARE_UIC(1), .base = UIC1 },
200 #if NR_UICS > 2
201         { .decl = DECLARE_UIC(2), .base = UIC2 },
202 #if NR_UICS > 3
203         { .decl = DECLARE_UIC(3), .base = UIC3 },
204 #endif
205 #endif
206 #endif
207 };
208
209 static inline int is_level_sensitive(int irq)
210 {
211         u32 tr = mfdcr(DCRN_UIC_TR(__uic[irq >> 5].base));
212         return (tr & IRQ_MASK_UICx(irq)) == 0;
213 }
214
215 void __init ppc4xx_pic_init(void)
216 {
217         int i;
218         unsigned char *eirqs = ppc4xx_uic_ext_irq_cfg;
219
220         for (i = 0; i < NR_UICS; ++i) {
221                 int base = __uic[i].base;
222
223                 /* Disable everything by default */
224                 ppc_cached_irq_mask[i] = 0;
225                 mtdcr(DCRN_UIC_ER(base), 0);
226
227                 /* We don't use critical interrupts */
228                 mtdcr(DCRN_UIC_CR(base), 0);
229
230                 /* Configure polarity and triggering */
231                 if (ppc4xx_core_uic_cfg) {
232                         struct ppc4xx_uic_settings *p = ppc4xx_core_uic_cfg + i;
233                         u32 mask = p->ext_irq_mask;
234                         u32 pr = mfdcr(DCRN_UIC_PR(base)) & mask;
235                         u32 tr = mfdcr(DCRN_UIC_TR(base)) & mask;
236
237                         /* "Fixed" interrupts (on-chip devices) */
238                         pr |= p->polarity & ~mask;
239                         tr |= p->triggering & ~mask;
240
241                         /* Merge external IRQs settings if board port
242                          * provided them
243                          */
244                         if (eirqs && mask) {
245                                 pr &= ~mask;
246                                 tr &= ~mask;
247                                 while (mask) {
248                                         /* Extract current external IRQ mask */
249                                         u32 eirq_mask = 1 << __ilog2(mask);
250
251                                         if (!(*eirqs & IRQ_SENSE_LEVEL))
252                                                 tr |= eirq_mask;
253
254                                         if (*eirqs & IRQ_POLARITY_POSITIVE)
255                                                 pr |= eirq_mask;
256
257                                         mask &= ~eirq_mask;
258                                         ++eirqs;
259                                 }
260                         }
261                         mtdcr(DCRN_UIC_PR(base), pr);
262                         mtdcr(DCRN_UIC_TR(base), tr);
263                 }
264
265                 /* ACK any pending interrupts to prevent false
266                  * triggering after first enable
267                  */
268                 mtdcr(DCRN_UIC_SR(base), 0xffffffff);
269         }
270
271         /* Perform optional implementation specific setup
272          * (e.g. enable cascade interrupts for multi-UIC configurations)
273          */
274         ppc4xx_pic_impl_init();
275
276         /* Attach low-level handlers */
277         for (i = 0; i < (NR_UICS << 5); ++i) {
278                 irq_desc[i].chip = &__uic[i >> 5].decl;
279                 if (is_level_sensitive(i))
280                         irq_desc[i].status |= IRQ_LEVEL;
281         }
282
283         ppc_md.get_irq = ppc4xx_pic_get_irq;
284 }