]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/hal/powerpc/mpc5xx/v2_0/src/var_intr.c
Initial revision
[karo-tx-redboot.git] / packages / hal / powerpc / mpc5xx / v2_0 / src / var_intr.c
1 //==========================================================================
2 //
3 //      var_intr.c
4 //
5 //      PowerPC variant interrupt handlers
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s):    Bob Koninckx
44 // Contributors: Bob Koninckx
45 // Date:         2001-12-16
46 // Purpose:      PowerPC variant interrupt handlers
47 // Description:  This file contains code to handle interrupt related issues
48 //               on the PowerPC variant.
49 //
50 //####DESCRIPTIONEND####
51 //
52 //==========================================================================
53
54 #include <pkgconf/hal.h>
55 #include <cyg/hal/ppc_regs.h>
56 #include <cyg/hal/hal_arbiter.h>
57
58 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
59 // Since the interrupt sources do not have fixed vectors on the 5XX
60 // SIU, some arbitration is required.
61
62 // More than one interrupt source can be programmed to use the same
63 // vector, so all sources on the same vector have to be queried to
64 // find the one raising the interrupt. This functionality has not been
65 // implemented, but the arbiter functions for each of the SIU
66 // interrupt sources can be called in sequence without change.
67
68
69
70 // Timebase interrupt can be caused by match on either reference A
71 // or B.  
72 // Note: If only one interrupt source is assigned per vector, and only
73 // reference interrupt A or B is used, this ISR is not
74 // necessary. Attach the timerbase reference A or B ISR directly to
75 // the LVLx vector instead.
76 externC cyg_uint32
77 hal_arbitration_isr_tb (CYG_ADDRWORD vector, CYG_ADDRWORD data)
78 {
79     cyg_uint32 isr_ret;
80     cyg_uint16 tbscr;
81
82     HAL_READ_UINT16 (CYGARC_REG_IMM_TBSCR, tbscr);
83     if (tbscr & CYGARC_REG_IMM_TBSCR_REFA) {
84         isr_ret = hal_call_isr (CYGNUM_HAL_INTERRUPT_SIU_TB_A);
85 #ifdef CYGIMP_HAL_COMMON_INTERRUPTS_CHAIN
86         if (isr_ret & CYG_ISR_HANDLED)
87 #endif
88             return isr_ret;
89     }
90
91     if (tbscr & CYGARC_REG_IMM_TBSCR_REFB) {
92         isr_ret = hal_call_isr (CYGNUM_HAL_INTERRUPT_SIU_TB_B);
93 #ifdef CYGIMP_HAL_COMMON_INTERRUPTS_CHAIN
94         if (isr_ret & CYG_ISR_HANDLED)
95 #endif
96             return isr_ret;
97     }
98
99     return 0;
100 }
101
102 // Periodic interrupt.
103 // Note: If only one interrupt source is assigned per vector, this ISR
104 // is not necessary. Attach the periodic interrupt ISR directly to the
105 // LVLx vector instead.
106 externC cyg_uint32
107 hal_arbitration_isr_pit (CYG_ADDRWORD vector, CYG_ADDRWORD data)
108 {
109     cyg_uint32 isr_ret;
110     cyg_uint16 piscr;
111
112     HAL_READ_UINT16 (CYGARC_REG_IMM_PISCR, piscr);
113     if (piscr & CYGARC_REG_IMM_PISCR_PS) {
114         isr_ret = hal_call_isr (CYGNUM_HAL_INTERRUPT_SIU_PIT);
115 #ifdef CYGIMP_HAL_COMMON_INTERRUPTS_CHAIN
116         if (isr_ret & CYG_ISR_HANDLED)
117 #endif
118             return isr_ret;
119     }
120
121     return 0;
122 }
123
124 // Real time clock interrupts can be caused by the alarm or
125 // once-per-second.
126 // Note: If only one interrupt source is assigned per vector, and only
127 // the alarm or once-per-second interrupt is used, this ISR is not
128 // necessary. Attach the alarm or once-per-second ISR directly to the
129 // LVLx vector instead.
130 externC cyg_uint32
131 hal_arbitration_isr_rtc (CYG_ADDRWORD vector, CYG_ADDRWORD data)
132 {
133     cyg_uint32 isr_ret;
134     cyg_uint16 rtcsc;
135
136     HAL_READ_UINT16 (CYGARC_REG_IMM_RTCSC, rtcsc);
137     if (rtcsc & CYGARC_REG_IMM_RTCSC_SEC) {
138         isr_ret = hal_call_isr (CYGNUM_HAL_INTERRUPT_SIU_RTC_SEC);
139 #ifdef CYGIMP_HAL_COMMON_INTERRUPTS_CHAIN
140         if (isr_ret & CYG_ISR_HANDLED)
141 #endif
142             return isr_ret;
143     }
144
145     if (rtcsc & CYGARC_REG_IMM_RTCSC_ALR) {
146         isr_ret = hal_call_isr (CYGNUM_HAL_INTERRUPT_SIU_RTC_ALR);
147 #ifdef CYGIMP_HAL_COMMON_INTERRUPTS_CHAIN
148         if (isr_ret & CYG_ISR_HANDLED)
149 #endif
150             return isr_ret;
151     }
152
153     return 0;
154 }
155
156 // Default arbitration ISR for serial interrupts. Although such arbitration
157 // belongs in the serial device driver, we require this default implementation
158 // for CTRL-C interrupts to be delivered correctly to any running ROM monitor.
159 // A device driver that uses more than just receive interrupts may of course
160 // provide its own arbiter.
161 externC cyg_uint32
162 hal_arbitration_isr_sci(CYG_ADDRWORD vector, CYG_ADDRWORD data)
163 {
164     cyg_uint32 isr_ret;
165         cyg_uint16 scc_sr;
166         cyg_uint16 scc_cr;
167
168         // Try SCI0
169         HAL_READ_UINT16(CYGARC_REG_IMM_SC1SR, scc_sr);
170         HAL_READ_UINT16(CYGARC_REG_IMM_SCC1R1, scc_cr);
171         if ((scc_sr & CYGARC_REG_IMM_SCxSR_RDRF) && (scc_cr & CYGARC_REG_IMM_SCCxR1_RIE)) {
172             isr_ret = hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI0_RX);
173 #ifdef CYGIMP_HAL_COMMON_INTERRUPTS_CHAIN
174         if (isr_ret & CYG_ISR_HANDLED)
175 #endif
176                    return isr_ret;
177         }
178
179         HAL_READ_UINT16(CYGARC_REG_IMM_SC2SR, scc_sr);
180         HAL_READ_UINT16(CYGARC_REG_IMM_SCC2R1, scc_cr);
181         if ((scc_sr & CYGARC_REG_IMM_SCxSR_RDRF) && (scc_cr & CYGARC_REG_IMM_SCCxR1_RIE)) {
182             isr_ret = hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RX);
183 #ifdef CYGIMP_HAL_COMMON_INTERRUPTS_CHAIN
184         if (isr_ret & CYG_ISR_HANDLED)
185 #endif
186                         return isr_ret;
187         }
188
189         return 0;
190 }
191
192 // -------------------------------------------------------------------------
193 // IMB3 interrupt decoding
194 //
195 // All interrupt priorities higher than 7 are mapped to SIU level 7. As much
196 // as 15 interrupting devices can be behind this. If more than one IMB3 
197 // device is to be used with priorites in the range 7-31, a special kind of
198 // arbitration isr needs to be set up on SIU level 7. As this is not allways
199 // necessary, it is provided as a configuration option.
200 #ifdef CYGSEM_HAL_POWERPC_MPC5XX_IMB3_ARBITER
201 static hal_mpc5xx_arbitration_data * imb3_data_head = 0;
202
203 static cyg_uint32
204 hal_arbitration_imb3(CYG_ADDRWORD vector, CYG_ADDRWORD data)
205 {
206   hal_mpc5xx_arbitration_data * p = 
207     *(hal_mpc5xx_arbitration_data **)data;
208
209   // Try them all, highest priorities come first. An ISR should return
210   // CYG_ISR_HANDLED or CYG_ISR_CALL_DSR. An arbitration ISR will 
211   // strip the CYG_DSR_HANDLED from the ISR result, or returns 0 if
212   // no ISR could be called. This means that CYG_ISR_HANDLED implies
213   // that an ISR was called, 0 means that nothing was called.
214   // Notice that our approach tries to be efficient. We return as soon
215   // as the first interrupting source is found. This prevents from scanning 
216   // the complete table for every interrupt. If more than one module 
217   // requested at the same time, we will re-enter this procedure immediately
218   // anyway.
219   while(p)
220   {
221     if((p->arbiter(CYGNUM_HAL_INTERRUPT_SIU_LVL7, p->data))&CYG_ISR_HANDLED)
222       break;
223     else
224       p = (hal_mpc5xx_arbitration_data *)(p->reserved);
225   }
226
227   return 0;
228 }
229
230 static hal_mpc5xx_arbitration_data *
231 mpc5xx_insert(hal_mpc5xx_arbitration_data * list,
232               hal_mpc5xx_arbitration_data * data)
233 {
234   hal_mpc5xx_arbitration_data    tmp;
235   hal_mpc5xx_arbitration_data * ptmp = &tmp;
236   tmp.reserved = list;
237
238   while(ptmp->reserved)
239   {
240     if(((hal_mpc5xx_arbitration_data *)(ptmp->reserved))->priority > data->priority)
241       break;
242     ptmp = (hal_mpc5xx_arbitration_data *)(ptmp->reserved);
243   }
244
245   data->reserved = ptmp->reserved;
246   ptmp->reserved = data;
247   
248   return (hal_mpc5xx_arbitration_data *)(tmp.reserved);
249 }
250
251 // This returns either the removed object or NULL if the priority
252 // was not found in the list.
253 // If a valid pointer is returned, the new start of the list is chained to it.
254 static hal_mpc5xx_arbitration_data *
255 mpc5xx_remove(hal_mpc5xx_arbitration_data * list,
256               cyg_uint32 apriority)
257 {
258   hal_mpc5xx_arbitration_data   tmp;
259   hal_mpc5xx_arbitration_data * result = 0;
260   hal_mpc5xx_arbitration_data * ptmp = &tmp;
261   tmp.reserved = list;
262
263   while(ptmp->reserved)
264   {
265     if(((hal_mpc5xx_arbitration_data *)(ptmp->reserved))->priority == apriority)
266       break;
267       
268         // move on
269     ptmp = (hal_mpc5xx_arbitration_data *)(ptmp->reserved);
270   }
271
272   // When we come here, ptmp is either chained to NULL or to the one we were looking for.
273   if(ptmp->reserved)
274   { // remove it
275         result = (hal_mpc5xx_arbitration_data *)(ptmp->reserved);
276         result->reserved = tmp.reserved;
277         
278     ptmp->reserved = ((hal_mpc5xx_arbitration_data *)(ptmp->reserved))->reserved;
279   }
280
281   return result;
282 }
283 #endif
284
285 externC void 
286 hal_mpc5xx_install_arbitration_isr(hal_mpc5xx_arbitration_data * adata)
287 { // Find the SIU vector from the priority
288   CYG_ADDRWORD vector = 2*(1 + adata->priority);
289   
290   if(vector < CYGNUM_HAL_INTERRUPT_SIU_LVL7)
291   { // Store adata in the objects table
292     HAL_INTERRUPT_ATTACH(vector, adata->arbiter, adata->data, adata);
293     HAL_INTERRUPT_UNMASK(vector);
294   }
295   else
296   {
297 #ifdef CYGSEM_HAL_POWERPC_MPC5XX_IMB3_ARBITER  
298     // Prevent anything from coming through while manipulating
299     // the list
300     HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_LVL7);
301     imb3_data_head = mpc5xx_insert(imb3_data_head, adata);
302     HAL_INTERRUPT_UNMASK(CYGNUM_HAL_INTERRUPT_SIU_LVL7);
303 #else
304     HAL_INTERRUPT_ATTACH(CYGNUM_HAL_INTERRUPT_SIU_LVL7, adata->arbiter, adata->data, adata);
305     HAL_INTERRUPT_UNMASK(CYGNUM_HAL_INTERRUPT_SIU_LVL7);
306 #endif
307   }
308 }
309
310 externC hal_mpc5xx_arbitration_data *
311 hal_mpc5xx_remove_arbitration_isr(cyg_uint32 apriority)
312 {
313   hal_mpc5xx_arbitration_data * result = 0;
314   
315   // Find the SIU vector from the priority
316   CYG_ADDRWORD vector = 2*(1 + apriority);
317   if(vector < CYGNUM_HAL_INTERRUPT_SIU_LVL7)
318   {
319     result = (hal_mpc5xx_arbitration_data *)(hal_interrupt_objects[vector]);
320         HAL_INTERRUPT_DETACH(vector, hal_interrupt_handlers[vector]);
321   }
322   else
323   {
324 #ifdef CYGSEM_HAL_POWERPC_MPC5XX_IMB3_ARBITER  
325     // Prevent anything from coming through while manipulating the list
326     HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_LVL7);
327         result = mpc5xx_remove(imb3_data_head, apriority);
328         
329         // If something was removed, update the list.
330         if(result) imb3_data_head = result->reserved;
331     HAL_INTERRUPT_UNMASK(CYGNUM_HAL_INTERRUPT_SIU_LVL7);
332 #else
333     result = (hal_mpc5xx_arbitration_data *)(hal_interrupt_objects[CYGNUM_HAL_INTERRUPT_SIU_LVL7]);
334         HAL_INTERRUPT_DETACH(CYGNUM_HAL_INTERRUPT_SIU_LVL7, hal_interrupt_handlers[CYGNUM_HAL_INTERRUPT_SIU_LVL7]); 
335 #endif
336   }
337
338   return result;
339 }
340
341 // -------------------------------------------------------------------------
342 // Variant specific interrupt setup
343 #if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) \
344      || defined(CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT)
345 static hal_mpc5xx_arbitration_data sci_arbiter;
346 #endif
347
348 externC void
349 hal_variant_IRQ_init(void)
350 {
351   // Mask off everything. This guarantees that we can safely install a handler on the decrementer
352   // later on
353   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_IRQ0);
354   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_IRQ1);
355   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_IRQ2);
356   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_IRQ3);
357   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_IRQ4);
358   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_IRQ5);
359   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_IRQ6);
360   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_IRQ7);
361   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_LVL0);
362   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_LVL1);
363   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_LVL2);
364   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_LVL3);
365   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_LVL4);
366   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_LVL5);
367   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_LVL6);
368   HAL_INTERRUPT_MASK(CYGNUM_HAL_INTERRUPT_SIU_LVL7);
369   
370 #ifdef CYGSEM_HAL_POWERPC_MPC5XX_IMB3_ARBITER  
371   HAL_INTERRUPT_ATTACH(CYGNUM_HAL_INTERRUPT_SIU_LVL7, hal_arbitration_imb3, &imb3_data_head, 0);
372   HAL_INTERRUPT_UNMASK(CYGNUM_HAL_INTERRUPT_SIU_LVL7);
373 #endif
374
375 #if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) \
376      || defined(CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT)
377   // GDB-CTRLC
378   // Install a default arbiter for serial interrupts. This allows
379   // to make a boot monitor simply turn on the required Rx interrupt
380   // and still be delivered the necessary default isr. Without this,
381   // redboot would be informed of a level interrupt on the SIU instead
382   // of the Rx interrupt that really happened.
383   // Make sure the interrupts are set up on the correct level
384   sci_arbiter.priority = CYGNUM_HAL_ISR_SOURCE_PRIORITY_QSCI;
385   sci_arbiter.data    = 0;
386   sci_arbiter.arbiter = hal_arbitration_isr_sci;
387
388   hal_mpc5xx_install_arbitration_isr(&sci_arbiter);
389   HAL_INTERRUPT_SET_LEVEL(CYGNUM_HAL_INTERRUPT_IMB3_SCI0_RX, CYGNUM_HAL_ISR_SOURCE_PRIORITY_QSCI);      
390   HAL_INTERRUPT_SET_LEVEL(CYGNUM_HAL_INTERRUPT_IMB3_SCI0_RX, CYGNUM_HAL_ISR_SOURCE_PRIORITY_QSCI);      
391 #endif
392 }
393
394 // -------------------------------------------------------------------------
395 // EOF var_intr.c