]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - arch/powerpc/cpu/mpc5xxx/interrupts.c
Add GPL-2.0+ SPDX-License-Identifier to source files
[karo-tx-uboot.git] / arch / powerpc / cpu / mpc5xxx / interrupts.c
1 /*
2  * (C) Copyright 2006
3  * Detlev Zundel, DENX Software Engineering, dzu@denx.de
4  *
5  * (C) Copyright -2003
6  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
7  *
8  * (C) Copyright 2001
9  * Josh Huber <huber@mclx.com>, Mission Critical Linux, Inc.
10  *
11  * SPDX-License-Identifier:     GPL-2.0+
12  */
13
14 /* this section was ripped out of arch/powerpc/syslib/mpc52xx_pic.c in the
15  * Linux 2.6 source with the following copyright.
16  *
17  * Based on (well, mostly copied from) the code from the 2.4 kernel by
18  * Dale Farnsworth <dfarnsworth@mvista.com> and Kent Borg.
19  *
20  * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
21  * Copyright (C) 2003 Montavista Software, Inc
22  */
23
24 #include <common.h>
25 #include <asm/processor.h>
26 #include <asm/io.h>
27 #include <command.h>
28
29 struct irq_action {
30         interrupt_handler_t *handler;
31         void *arg;
32         ulong count;
33 };
34
35 static struct irq_action irq_handlers[NR_IRQS];
36
37 static struct mpc5xxx_intr *intr;
38 static struct mpc5xxx_sdma *sdma;
39
40 static void mpc5xxx_ic_disable(unsigned int irq)
41 {
42         u32 val;
43
44         if (irq == MPC5XXX_IRQ0) {
45                 val = in_be32(&intr->ctrl);
46                 val &= ~(1 << 11);
47                 out_be32(&intr->ctrl, val);
48         } else if (irq < MPC5XXX_IRQ1) {
49                 BUG();
50         } else if (irq <= MPC5XXX_IRQ3) {
51                 val = in_be32(&intr->ctrl);
52                 val &= ~(1 << (10 - (irq - MPC5XXX_IRQ1)));
53                 out_be32(&intr->ctrl, val);
54         } else if (irq < MPC5XXX_SDMA_IRQ_BASE) {
55                 val = in_be32(&intr->main_mask);
56                 val |= 1 << (16 - (irq - MPC5XXX_MAIN_IRQ_BASE));
57                 out_be32(&intr->main_mask, val);
58         } else if (irq < MPC5XXX_PERP_IRQ_BASE) {
59                 val = in_be32(&sdma->IntMask);
60                 val |= 1 << (irq - MPC5XXX_SDMA_IRQ_BASE);
61                 out_be32(&sdma->IntMask, val);
62         } else {
63                 val = in_be32(&intr->per_mask);
64                 val |= 1 << (31 - (irq - MPC5XXX_PERP_IRQ_BASE));
65                 out_be32(&intr->per_mask, val);
66         }
67 }
68
69 static void mpc5xxx_ic_enable(unsigned int irq)
70 {
71         u32 val;
72
73         if (irq == MPC5XXX_IRQ0) {
74                 val = in_be32(&intr->ctrl);
75                 val |= 1 << 11;
76                 out_be32(&intr->ctrl, val);
77         } else if (irq < MPC5XXX_IRQ1) {
78                 BUG();
79         } else if (irq <= MPC5XXX_IRQ3) {
80                 val = in_be32(&intr->ctrl);
81                 val |= 1 << (10 - (irq - MPC5XXX_IRQ1));
82                 out_be32(&intr->ctrl, val);
83         } else if (irq < MPC5XXX_SDMA_IRQ_BASE) {
84                 val = in_be32(&intr->main_mask);
85                 val &= ~(1 << (16 - (irq - MPC5XXX_MAIN_IRQ_BASE)));
86                 out_be32(&intr->main_mask, val);
87         } else if (irq < MPC5XXX_PERP_IRQ_BASE) {
88                 val = in_be32(&sdma->IntMask);
89                 val &= ~(1 << (irq - MPC5XXX_SDMA_IRQ_BASE));
90                 out_be32(&sdma->IntMask, val);
91         } else {
92                 val = in_be32(&intr->per_mask);
93                 val &= ~(1 << (31 - (irq - MPC5XXX_PERP_IRQ_BASE)));
94                 out_be32(&intr->per_mask, val);
95         }
96 }
97
98 static void mpc5xxx_ic_ack(unsigned int irq)
99 {
100         u32 val;
101
102         /*
103          * Only some irqs are reset here, others in interrupting hardware.
104          */
105
106         switch (irq) {
107         case MPC5XXX_IRQ0:
108                 val = in_be32(&intr->ctrl);
109                 val |= 0x08000000;
110                 out_be32(&intr->ctrl, val);
111                 break;
112         case MPC5XXX_CCS_IRQ:
113                 val = in_be32(&intr->enc_status);
114                 val |= 0x00000400;
115                 out_be32(&intr->enc_status, val);
116                 break;
117         case MPC5XXX_IRQ1:
118                 val = in_be32(&intr->ctrl);
119                 val |= 0x04000000;
120                 out_be32(&intr->ctrl, val);
121                 break;
122         case MPC5XXX_IRQ2:
123                 val = in_be32(&intr->ctrl);
124                 val |= 0x02000000;
125                 out_be32(&intr->ctrl, val);
126                 break;
127         case MPC5XXX_IRQ3:
128                 val = in_be32(&intr->ctrl);
129                 val |= 0x01000000;
130                 out_be32(&intr->ctrl, val);
131                 break;
132         default:
133                 if (irq >= MPC5XXX_SDMA_IRQ_BASE
134                     && irq < (MPC5XXX_SDMA_IRQ_BASE + MPC5XXX_SDMA_IRQ_NUM)) {
135                         out_be32(&sdma->IntPend,
136                                  1 << (irq - MPC5XXX_SDMA_IRQ_BASE));
137                 }
138                 break;
139         }
140 }
141
142 static void mpc5xxx_ic_disable_and_ack(unsigned int irq)
143 {
144         mpc5xxx_ic_disable(irq);
145         mpc5xxx_ic_ack(irq);
146 }
147
148 static void mpc5xxx_ic_end(unsigned int irq)
149 {
150         mpc5xxx_ic_enable(irq);
151 }
152
153 void mpc5xxx_init_irq(void)
154 {
155         u32 intr_ctrl;
156
157         /* Remap the necessary zones */
158         intr = (struct mpc5xxx_intr *)(MPC5XXX_ICTL);
159         sdma = (struct mpc5xxx_sdma *)(MPC5XXX_SDMA);
160
161         /* Disable all interrupt sources. */
162         out_be32(&sdma->IntPend, 0xffffffff);   /* 1 means clear pending */
163         out_be32(&sdma->IntMask, 0xffffffff);   /* 1 means disabled */
164         out_be32(&intr->per_mask, 0x7ffffc00);  /* 1 means disabled */
165         out_be32(&intr->main_mask, 0x00010fff); /* 1 means disabled */
166         intr_ctrl = in_be32(&intr->ctrl);
167         intr_ctrl |= 0x0f000000 |       /* clear IRQ 0-3 */
168             0x00ff0000 |        /* IRQ 0-3 level sensitive low active */
169             0x00001000 |        /* MEE master external enable */
170             0x00000000 |        /* 0 means disable IRQ 0-3 */
171             0x00000001;         /* CEb route critical normally */
172         out_be32(&intr->ctrl, intr_ctrl);
173
174         /* Zero a bunch of the priority settings.  */
175         out_be32(&intr->per_pri1, 0);
176         out_be32(&intr->per_pri2, 0);
177         out_be32(&intr->per_pri3, 0);
178         out_be32(&intr->main_pri1, 0);
179         out_be32(&intr->main_pri2, 0);
180 }
181
182 int mpc5xxx_get_irq(struct pt_regs *regs)
183 {
184         u32 status;
185         int irq = -1;
186
187         status = in_be32(&intr->enc_status);
188
189         if (status & 0x00000400) {      /* critical */
190                 irq = (status >> 8) & 0x3;
191                 if (irq == 2)   /* high priority peripheral */
192                         goto peripheral;
193                 irq += MPC5XXX_CRIT_IRQ_BASE;
194         } else if (status & 0x00200000) {       /* main */
195                 irq = (status >> 16) & 0x1f;
196                 if (irq == 4)   /* low priority peripheral */
197                         goto peripheral;
198                 irq += MPC5XXX_MAIN_IRQ_BASE;
199         } else if (status & 0x20000000) {       /* peripheral */
200               peripheral:
201                 irq = (status >> 24) & 0x1f;
202                 if (irq == 0) { /* bestcomm */
203                         status = in_be32(&sdma->IntPend);
204                         irq = ffs(status) + MPC5XXX_SDMA_IRQ_BASE - 1;
205                 } else
206                         irq += MPC5XXX_PERP_IRQ_BASE;
207         }
208
209         return irq;
210 }
211
212 /****************************************************************************/
213
214 int interrupt_init_cpu(ulong * decrementer_count)
215 {
216         *decrementer_count = get_tbclk() / CONFIG_SYS_HZ;
217
218         mpc5xxx_init_irq();
219
220         return (0);
221 }
222
223 /****************************************************************************/
224
225 /*
226  * Handle external interrupts
227  */
228 void external_interrupt(struct pt_regs *regs)
229 {
230         int irq, unmask = 1;
231
232         irq = mpc5xxx_get_irq(regs);
233
234         mpc5xxx_ic_disable_and_ack(irq);
235
236         enable_interrupts();
237
238         if (irq_handlers[irq].handler != NULL)
239                 (*irq_handlers[irq].handler) (irq_handlers[irq].arg);
240         else {
241                 printf("\nBogus External Interrupt IRQ %d\n", irq);
242                 /*
243                  * turn off the bogus interrupt, otherwise it
244                  * might repeat forever
245                  */
246                 unmask = 0;
247         }
248
249         if (unmask)
250                 mpc5xxx_ic_end(irq);
251 }
252
253 void timer_interrupt_cpu(struct pt_regs *regs)
254 {
255         /* nothing to do here */
256         return;
257 }
258
259 /****************************************************************************/
260
261 /*
262  * Install and free a interrupt handler.
263  */
264
265 void irq_install_handler(int irq, interrupt_handler_t * handler, void *arg)
266 {
267         if (irq < 0 || irq >= NR_IRQS) {
268                 printf("irq_install_handler: bad irq number %d\n", irq);
269                 return;
270         }
271
272         if (irq_handlers[irq].handler != NULL)
273                 printf("irq_install_handler: 0x%08lx replacing 0x%08lx\n",
274                        (ulong) handler, (ulong) irq_handlers[irq].handler);
275
276         irq_handlers[irq].handler = handler;
277         irq_handlers[irq].arg = arg;
278
279         mpc5xxx_ic_enable(irq);
280 }
281
282 void irq_free_handler(int irq)
283 {
284         if (irq < 0 || irq >= NR_IRQS) {
285                 printf("irq_free_handler: bad irq number %d\n", irq);
286                 return;
287         }
288
289         mpc5xxx_ic_disable(irq);
290
291         irq_handlers[irq].handler = NULL;
292         irq_handlers[irq].arg = NULL;
293 }
294
295 /****************************************************************************/
296
297 #if defined(CONFIG_CMD_IRQ)
298 void do_irqinfo(cmd_tbl_t * cmdtp, bd_t * bd, int flag, int argc, char * const argv[])
299 {
300         int irq, re_enable;
301         u32 intr_ctrl;
302         char *irq_config[] = { "level sensitive, active high",
303                 "edge sensitive, rising active edge",
304                 "edge sensitive, falling active edge",
305                 "level sensitive, active low"
306         };
307
308         re_enable = disable_interrupts();
309
310         intr_ctrl = in_be32(&intr->ctrl);
311         printf("Interrupt configuration:\n");
312
313         for (irq = 0; irq <= 3; irq++) {
314                 printf("IRQ%d: %s\n", irq,
315                        irq_config[(intr_ctrl >> (22 - 2 * irq)) & 0x3]);
316         }
317
318         puts("\nInterrupt-Information:\n" "Nr  Routine   Arg       Count\n");
319
320         for (irq = 0; irq < NR_IRQS; irq++)
321                 if (irq_handlers[irq].handler != NULL)
322                         printf("%02d  %08lx  %08lx  %ld\n", irq,
323                                (ulong) irq_handlers[irq].handler,
324                                (ulong) irq_handlers[irq].arg,
325                                irq_handlers[irq].count);
326
327         if (re_enable)
328                 enable_interrupts();
329 }
330 #endif