[PATCH] ppc64: POWER5+ oprofile support
[linux-2.6] / arch / ppc / syslib / mpc52xx_pic.c
1 /*
2  * arch/ppc/syslib/mpc52xx_pic.c
3  *
4  * Programmable Interrupt Controller functions for the Freescale MPC52xx 
5  * embedded CPU.
6  *
7  * 
8  * Maintainer : Sylvain Munaut <tnt@246tNt.com>
9  *
10  * Based on (well, mostly copied from) the code from the 2.4 kernel by
11  * Dale Farnsworth <dfarnsworth@mvista.com> and Kent Borg.
12  * 
13  * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
14  * Copyright (C) 2003 Montavista Software, Inc
15  * 
16  * This file is licensed under the terms of the GNU General Public License
17  * version 2. This program is licensed "as is" without any warranty of any
18  * kind, whether express or implied.
19  */
20
21 #include <linux/stddef.h>
22 #include <linux/init.h>
23 #include <linux/sched.h>
24 #include <linux/signal.h>
25 #include <linux/stddef.h>
26 #include <linux/delay.h>
27 #include <linux/irq.h>
28
29 #include <asm/io.h>
30 #include <asm/processor.h>
31 #include <asm/system.h>
32 #include <asm/irq.h>
33 #include <asm/mpc52xx.h>
34
35
36 static struct mpc52xx_intr __iomem *intr;
37 static struct mpc52xx_sdma __iomem *sdma;
38
39 static void
40 mpc52xx_ic_disable(unsigned int irq)
41 {
42         u32 val;
43
44         if (irq == MPC52xx_IRQ0) {
45                 val = in_be32(&intr->ctrl);
46                 val &= ~(1 << 11);
47                 out_be32(&intr->ctrl, val);
48         }
49         else if (irq < MPC52xx_IRQ1) {
50                 BUG();
51         }
52         else if (irq <= MPC52xx_IRQ3) {
53                 val = in_be32(&intr->ctrl);
54                 val &= ~(1 << (10 - (irq - MPC52xx_IRQ1)));
55                 out_be32(&intr->ctrl, val);
56         }
57         else if (irq < MPC52xx_SDMA_IRQ_BASE) {
58                 val = in_be32(&intr->main_mask);
59                 val |= 1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE));
60                 out_be32(&intr->main_mask, val);
61         }
62         else if (irq < MPC52xx_PERP_IRQ_BASE) {
63                 val = in_be32(&sdma->IntMask);
64                 val |= 1 << (irq - MPC52xx_SDMA_IRQ_BASE);
65                 out_be32(&sdma->IntMask, val);
66         }
67         else {
68                 val = in_be32(&intr->per_mask);
69                 val |= 1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE));
70                 out_be32(&intr->per_mask, val);
71         }
72 }
73
74 static void
75 mpc52xx_ic_enable(unsigned int irq)
76 {
77         u32 val;
78
79         if (irq == MPC52xx_IRQ0) {
80                 val = in_be32(&intr->ctrl);
81                 val |= 1 << 11;
82                 out_be32(&intr->ctrl, val);
83         }
84         else if (irq < MPC52xx_IRQ1) {
85                 BUG();
86         }
87         else if (irq <= MPC52xx_IRQ3) {
88                 val = in_be32(&intr->ctrl);
89                 val |= 1 << (10 - (irq - MPC52xx_IRQ1));
90                 out_be32(&intr->ctrl, val);
91         }
92         else if (irq < MPC52xx_SDMA_IRQ_BASE) {
93                 val = in_be32(&intr->main_mask);
94                 val &= ~(1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE)));
95                 out_be32(&intr->main_mask, val);
96         }
97         else if (irq < MPC52xx_PERP_IRQ_BASE) {
98                 val = in_be32(&sdma->IntMask);
99                 val &= ~(1 << (irq - MPC52xx_SDMA_IRQ_BASE));
100                 out_be32(&sdma->IntMask, val);
101         }
102         else {
103                 val = in_be32(&intr->per_mask);
104                 val &= ~(1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE)));
105                 out_be32(&intr->per_mask, val);
106         }
107 }
108
109 static void
110 mpc52xx_ic_ack(unsigned int irq)
111 {
112         u32 val;
113
114         /*
115          * Only some irqs are reset here, others in interrupting hardware.
116          */
117
118         switch (irq) {
119         case MPC52xx_IRQ0:
120                 val = in_be32(&intr->ctrl);
121                 val |= 0x08000000;
122                 out_be32(&intr->ctrl, val);
123                 break;
124         case MPC52xx_CCS_IRQ:
125                 val = in_be32(&intr->enc_status);
126                 val |= 0x00000400;
127                 out_be32(&intr->enc_status, val);
128                 break;
129         case MPC52xx_IRQ1:
130                 val = in_be32(&intr->ctrl);
131                 val |= 0x04000000;
132                 out_be32(&intr->ctrl, val);
133                 break;
134         case MPC52xx_IRQ2:
135                 val = in_be32(&intr->ctrl);
136                 val |= 0x02000000;
137                 out_be32(&intr->ctrl, val);
138                 break;
139         case MPC52xx_IRQ3:
140                 val = in_be32(&intr->ctrl);
141                 val |= 0x01000000;
142                 out_be32(&intr->ctrl, val);
143                 break;
144         default:
145                 if (irq >= MPC52xx_SDMA_IRQ_BASE
146                     && irq < (MPC52xx_SDMA_IRQ_BASE + MPC52xx_SDMA_IRQ_NUM)) {
147                         out_be32(&sdma->IntPend,
148                                  1 << (irq - MPC52xx_SDMA_IRQ_BASE));
149                 }
150                 break;
151         }
152 }
153
154 static void
155 mpc52xx_ic_disable_and_ack(unsigned int irq)
156 {
157         mpc52xx_ic_disable(irq);
158         mpc52xx_ic_ack(irq);
159 }
160
161 static void
162 mpc52xx_ic_end(unsigned int irq)
163 {
164         if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
165                 mpc52xx_ic_enable(irq);
166 }
167
168 static struct hw_interrupt_type mpc52xx_ic = {
169         .typename       = " MPC52xx  ",
170         .enable         = mpc52xx_ic_enable,
171         .disable        = mpc52xx_ic_disable,
172         .ack            = mpc52xx_ic_disable_and_ack,
173         .end            = mpc52xx_ic_end,
174 };
175
176 void __init
177 mpc52xx_init_irq(void)
178 {
179         int i;
180         u32 intr_ctrl;
181
182         /* Remap the necessary zones */
183         intr = ioremap(MPC52xx_PA(MPC52xx_INTR_OFFSET), MPC52xx_INTR_SIZE);
184         sdma = ioremap(MPC52xx_PA(MPC52xx_SDMA_OFFSET), MPC52xx_SDMA_SIZE);
185
186         if ((intr==NULL) || (sdma==NULL))
187                 panic("Can't ioremap PIC/SDMA register for init_irq !");
188
189         /* Disable all interrupt sources. */
190         out_be32(&sdma->IntPend, 0xffffffff);   /* 1 means clear pending */
191         out_be32(&sdma->IntMask, 0xffffffff);   /* 1 means disabled */
192         out_be32(&intr->per_mask, 0x7ffffc00);  /* 1 means disabled */
193         out_be32(&intr->main_mask, 0x00010fff); /* 1 means disabled */
194         intr_ctrl = in_be32(&intr->ctrl);
195         intr_ctrl &=    0x00ff0000;     /* Keeps IRQ[0-3] config */
196         intr_ctrl |=    0x0f000000 |    /* clear IRQ 0-3 */
197                         0x00001000 |    /* MEE master external enable */
198                         0x00000000 |    /* 0 means disable IRQ 0-3 */
199                         0x00000001;     /* CEb route critical normally */
200         out_be32(&intr->ctrl, intr_ctrl);
201
202         /* Zero a bunch of the priority settings.  */
203         out_be32(&intr->per_pri1, 0);
204         out_be32(&intr->per_pri2, 0);
205         out_be32(&intr->per_pri3, 0);
206         out_be32(&intr->main_pri1, 0);
207         out_be32(&intr->main_pri2, 0);
208
209         /* Initialize irq_desc[i].handler's with mpc52xx_ic. */
210         for (i = 0; i < NR_IRQS; i++) {
211                 irq_desc[i].handler = &mpc52xx_ic;
212                 irq_desc[i].status = IRQ_LEVEL;
213         }
214
215         #define IRQn_MODE(intr_ctrl,irq) (((intr_ctrl) >> (22-(i<<1))) & 0x03)
216         for (i=0 ; i<4 ; i++) {
217                 int mode;
218                 mode = IRQn_MODE(intr_ctrl,i);
219                 if ((mode == 0x1) || (mode == 0x2))
220                         irq_desc[i?MPC52xx_IRQ1+i-1:MPC52xx_IRQ0].status = 0;
221         }
222 }
223
224 int
225 mpc52xx_get_irq(struct pt_regs *regs)
226 {
227         u32 status;
228         int irq = -1;
229
230         status = in_be32(&intr->enc_status);
231
232         if (status & 0x00000400) {              /* critical */
233                 irq = (status >> 8) & 0x3;
234                 if (irq == 2)                   /* high priority peripheral */
235                         goto peripheral;
236                 irq += MPC52xx_CRIT_IRQ_BASE;
237         }
238         else if (status & 0x00200000) {         /* main */
239                 irq = (status >> 16) & 0x1f;
240                 if (irq == 4)                   /* low priority peripheral */
241                         goto peripheral;
242                 irq += MPC52xx_MAIN_IRQ_BASE;
243         }
244         else if (status & 0x20000000) {         /* peripheral */
245 peripheral:
246                 irq = (status >> 24) & 0x1f;
247                 if (irq == 0) {                 /* bestcomm */
248                         status = in_be32(&sdma->IntPend);
249                         irq = ffs(status) + MPC52xx_SDMA_IRQ_BASE-1;
250                 }
251                 else
252                         irq += MPC52xx_PERP_IRQ_BASE;
253         }
254
255         return irq;
256 }
257