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