1 //==========================================================================
5 // PowerPC variant interrupt handlers
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.
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.
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
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.
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.
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.
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####
43 // Author(s): Bob Koninckx
44 // Contributors: Bob Koninckx
46 // Purpose: PowerPC variant interrupt handlers
47 // Description: This file contains code to handle interrupt related issues
48 // on the PowerPC variant.
50 //####DESCRIPTIONEND####
52 //==========================================================================
54 #include <pkgconf/hal.h>
55 #include <cyg/hal/ppc_regs.h>
56 #include <cyg/hal/hal_arbiter.h>
58 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
59 // Since the interrupt sources do not have fixed vectors on the 5XX
60 // SIU, some arbitration is required.
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.
70 // Timebase interrupt can be caused by match on either reference A
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.
77 hal_arbitration_isr_tb (CYG_ADDRWORD vector, CYG_ADDRWORD data)
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)
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)
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.
107 hal_arbitration_isr_pit (CYG_ADDRWORD vector, CYG_ADDRWORD data)
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)
124 // Real time clock interrupts can be caused by the alarm or
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.
131 hal_arbitration_isr_rtc (CYG_ADDRWORD vector, CYG_ADDRWORD data)
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)
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)
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.
162 hal_arbitration_isr_sci(CYG_ADDRWORD vector, CYG_ADDRWORD data)
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)
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)
192 // -------------------------------------------------------------------------
193 // IMB3 interrupt decoding
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;
204 hal_arbitration_imb3(CYG_ADDRWORD vector, CYG_ADDRWORD data)
206 hal_mpc5xx_arbitration_data * p =
207 *(hal_mpc5xx_arbitration_data **)data;
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
221 if((p->arbiter(CYGNUM_HAL_INTERRUPT_SIU_LVL7, p->data))&CYG_ISR_HANDLED)
224 p = (hal_mpc5xx_arbitration_data *)(p->reserved);
230 static hal_mpc5xx_arbitration_data *
231 mpc5xx_insert(hal_mpc5xx_arbitration_data * list,
232 hal_mpc5xx_arbitration_data * data)
234 hal_mpc5xx_arbitration_data tmp;
235 hal_mpc5xx_arbitration_data * ptmp = &tmp;
238 while(ptmp->reserved)
240 if(((hal_mpc5xx_arbitration_data *)(ptmp->reserved))->priority > data->priority)
242 ptmp = (hal_mpc5xx_arbitration_data *)(ptmp->reserved);
245 data->reserved = ptmp->reserved;
246 ptmp->reserved = data;
248 return (hal_mpc5xx_arbitration_data *)(tmp.reserved);
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)
258 hal_mpc5xx_arbitration_data tmp;
259 hal_mpc5xx_arbitration_data * result = 0;
260 hal_mpc5xx_arbitration_data * ptmp = &tmp;
263 while(ptmp->reserved)
265 if(((hal_mpc5xx_arbitration_data *)(ptmp->reserved))->priority == apriority)
269 ptmp = (hal_mpc5xx_arbitration_data *)(ptmp->reserved);
272 // When we come here, ptmp is either chained to NULL or to the one we were looking for.
275 result = (hal_mpc5xx_arbitration_data *)(ptmp->reserved);
276 result->reserved = tmp.reserved;
278 ptmp->reserved = ((hal_mpc5xx_arbitration_data *)(ptmp->reserved))->reserved;
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);
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);
297 #ifdef CYGSEM_HAL_POWERPC_MPC5XX_IMB3_ARBITER
298 // Prevent anything from coming through while manipulating
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);
304 HAL_INTERRUPT_ATTACH(CYGNUM_HAL_INTERRUPT_SIU_LVL7, adata->arbiter, adata->data, adata);
305 HAL_INTERRUPT_UNMASK(CYGNUM_HAL_INTERRUPT_SIU_LVL7);
310 externC hal_mpc5xx_arbitration_data *
311 hal_mpc5xx_remove_arbitration_isr(cyg_uint32 apriority)
313 hal_mpc5xx_arbitration_data * result = 0;
315 // Find the SIU vector from the priority
316 CYG_ADDRWORD vector = 2*(1 + apriority);
317 if(vector < CYGNUM_HAL_INTERRUPT_SIU_LVL7)
319 result = (hal_mpc5xx_arbitration_data *)(hal_interrupt_objects[vector]);
320 HAL_INTERRUPT_DETACH(vector, hal_interrupt_handlers[vector]);
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);
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);
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]);
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;
349 hal_variant_IRQ_init(void)
351 // Mask off everything. This guarantees that we can safely install a handler on the decrementer
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);
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);
375 #if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) \
376 || defined(CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT)
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;
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);
394 // -------------------------------------------------------------------------