ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ppc / syslib / cpc700_pic.c
1 /*
2  * arch/ppc/syslib/cpc700_pic.c
3  *
4  * Interrupt controller support for IBM Spruce
5  *
6  * Authors: Mark Greer, Matt Porter, and Johnnie Peters
7  *          mgreer@mvista.com
8  *          mporter@mvista.com
9  *          jpeters@mvista.com
10  *
11  * 2001-2002 (c) MontaVista, Software, Inc.  This file is licensed under
12  * the terms of the GNU General Public License version 2.  This program
13  * is licensed "as is" without any warranty of any kind, whether express
14  * or implied.
15  */
16
17 #include <linux/stddef.h>
18 #include <linux/init.h>
19 #include <linux/sched.h>
20 #include <linux/signal.h>
21 #include <linux/irq.h>
22
23 #include <asm/io.h>
24 #include <asm/system.h>
25 #include <asm/irq.h>
26
27 #include "cpc700.h"
28
29 static void
30 cpc700_unmask_irq(unsigned int irq)
31 {
32         unsigned int tr_bits;
33
34         /*
35          * IRQ 31 is largest IRQ supported.
36          * IRQs 17-19 are reserved.
37          */
38         if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
39                 tr_bits = CPC700_IN_32(CPC700_UIC_UICTR);
40
41                 if ((tr_bits & (1 << (31 - irq))) == 0) {
42                         /* level trigger interrupt, clear bit in status
43                          * register */
44                         CPC700_OUT_32(CPC700_UIC_UICSR, 1 << (31 - irq));
45                 }
46
47                 /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
48                 ppc_cached_irq_mask[0] |= CPC700_UIC_IRQ_BIT(irq);
49         
50                 CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
51         }
52         return;
53 }
54
55 static void
56 cpc700_mask_irq(unsigned int irq)
57 {
58         /*
59          * IRQ 31 is largest IRQ supported.
60          * IRQs 17-19 are reserved.
61          */
62         if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
63                 /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
64                 ppc_cached_irq_mask[0] &=
65                         ~CPC700_UIC_IRQ_BIT(irq);
66
67                 CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
68         }
69         return;
70 }
71
72 static void
73 cpc700_mask_and_ack_irq(unsigned int irq)
74 {
75         u_int   bit;
76
77         /*
78          * IRQ 31 is largest IRQ supported.
79          * IRQs 17-19 are reserved.
80          */
81         if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
82                 /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
83                 bit = CPC700_UIC_IRQ_BIT(irq);
84
85                 ppc_cached_irq_mask[0] &= ~bit;
86                 CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
87                 CPC700_OUT_32(CPC700_UIC_UICSR, bit); /* Write 1 clears IRQ */
88         }
89         return;
90 }
91
92 static struct hw_interrupt_type cpc700_pic = {
93         "CPC700 PIC",
94         NULL,
95         NULL,
96         cpc700_unmask_irq,
97         cpc700_mask_irq,
98         cpc700_mask_and_ack_irq,
99         NULL,
100         NULL
101 };
102
103 __init static void
104 cpc700_pic_init_irq(unsigned int irq)
105 {
106         unsigned int tmp;
107
108         /* Set interrupt sense */
109         tmp = CPC700_IN_32(CPC700_UIC_UICTR);
110         if (cpc700_irq_assigns[irq][0] == 0) {
111                 tmp &= ~CPC700_UIC_IRQ_BIT(irq);
112         } else {
113                 tmp |= CPC700_UIC_IRQ_BIT(irq);
114         }
115         CPC700_OUT_32(CPC700_UIC_UICTR, tmp);
116
117         /* Set interrupt polarity */
118         tmp = CPC700_IN_32(CPC700_UIC_UICPR);
119         if (cpc700_irq_assigns[irq][1]) {
120                 tmp |= CPC700_UIC_IRQ_BIT(irq);
121         } else {
122                 tmp &= ~CPC700_UIC_IRQ_BIT(irq);
123         }
124         CPC700_OUT_32(CPC700_UIC_UICPR, tmp);
125
126         /* Set interrupt critical */
127         tmp = CPC700_IN_32(CPC700_UIC_UICCR);
128         tmp |= CPC700_UIC_IRQ_BIT(irq);
129         CPC700_OUT_32(CPC700_UIC_UICCR, tmp);
130                 
131         return;
132 }
133
134 __init void
135 cpc700_init_IRQ(void)
136 {
137         int i;
138
139         ppc_cached_irq_mask[0] = 0;
140         CPC700_OUT_32(CPC700_UIC_UICER, 0x00000000);    /* Disable all irq's */
141         CPC700_OUT_32(CPC700_UIC_UICSR, 0xffffffff);    /* Clear cur intrs */
142         CPC700_OUT_32(CPC700_UIC_UICCR, 0xffffffff);    /* Gen INT not MCP */
143         CPC700_OUT_32(CPC700_UIC_UICPR, 0x00000000);    /* Active low */
144         CPC700_OUT_32(CPC700_UIC_UICTR, 0x00000000);    /* Level Sensitive */
145         CPC700_OUT_32(CPC700_UIC_UICVR, CPC700_UIC_UICVCR_0_HI);
146                                                         /* IRQ 0 is highest */
147
148         for (i = 0; i < 17; i++) {
149                 irq_desc[i].handler = &cpc700_pic;
150                 cpc700_pic_init_irq(i);
151         }
152
153         for (i = 20; i < 32; i++) {
154                 irq_desc[i].handler = &cpc700_pic;
155                 cpc700_pic_init_irq(i);
156         }
157
158         return;
159 }
160
161
162
163 /*
164  * Find the highest IRQ that generating an interrupt, if any.
165  */
166 int
167 cpc700_get_irq(struct pt_regs *regs)
168 {
169         int irq = 0;
170         u_int irq_status, irq_test = 1;
171
172         irq_status = CPC700_IN_32(CPC700_UIC_UICMSR);
173
174         do
175         {
176                 if (irq_status & irq_test)
177                         break;
178                 irq++;
179                 irq_test <<= 1;
180         } while (irq < NR_IRQS);
181         
182
183         if (irq == NR_IRQS)
184             irq = 33;
185
186         return (31 - irq);
187 }