[aecfa2b] | 1 | /* ictrl.c |
---|
| 2 | * |
---|
| 3 | * This routine installs and handles external interrupt vectors for |
---|
| 4 | * PowerPC 403 CPU built-in external interrupt controller |
---|
| 5 | * |
---|
| 6 | * Author: Thomas Doerfler <td@imd.m.isar.de> |
---|
| 7 | * |
---|
| 8 | * COPYRIGHT (c) 1998 by IMD, Puchheim, Germany |
---|
| 9 | * |
---|
| 10 | * To anyone who acknowledges that this file is provided "AS IS" |
---|
| 11 | * without any express or implied warranty: |
---|
| 12 | * permission to use, copy, modify, and distribute this file |
---|
| 13 | * for any purpose is hereby granted without fee, provided that |
---|
| 14 | * the above copyright notice and this notice appears in all |
---|
| 15 | * copies, and that the name of IMD not be used in |
---|
| 16 | * advertising or publicity pertaining to distribution of the |
---|
| 17 | * software without specific, written prior permission. |
---|
| 18 | * IMD makes no representations about the suitability |
---|
| 19 | * of this software for any purpose. |
---|
| 20 | * |
---|
[e9ae97fb] | 21 | * Modifications for PPC405GP by Dennis Ehlin |
---|
| 22 | * |
---|
[aecfa2b] | 23 | */ |
---|
| 24 | |
---|
| 25 | #include "ictrl.h" |
---|
[f817b02] | 26 | #include <rtems.h> |
---|
[aecfa2b] | 27 | #include <rtems/libio.h> |
---|
| 28 | |
---|
| 29 | #include <stdlib.h> /* for atexit() */ |
---|
| 30 | |
---|
| 31 | /* |
---|
| 32 | * ISR vector table to dispatch external interrupts |
---|
| 33 | */ |
---|
| 34 | |
---|
| 35 | rtems_isr_entry ictrl_vector_table[PPC_IRQ_EXT_MAX]; |
---|
| 36 | |
---|
| 37 | /* |
---|
| 38 | * |
---|
| 39 | * some utilities to access the EXI* registers |
---|
| 40 | * |
---|
| 41 | */ |
---|
| 42 | |
---|
| 43 | /* |
---|
| 44 | * clear bits in EXISR that have a bit set in mask |
---|
| 45 | */ |
---|
[e9ae97fb] | 46 | #if defined(ppc405) |
---|
| 47 | RTEMS_INLINE_ROUTINE void |
---|
[66c373bf] | 48 | clr_exisr(uint32_t mask) |
---|
[e9ae97fb] | 49 | { |
---|
| 50 | asm volatile ("mtdcr 0xC0,%0"::"r" (mask));/*EXISR*/ |
---|
| 51 | } |
---|
| 52 | |
---|
| 53 | /* |
---|
| 54 | * get value of EXISR |
---|
| 55 | */ |
---|
[66c373bf] | 56 | RTEMS_INLINE_ROUTINE uint32_t |
---|
[e9ae97fb] | 57 | get_exisr(void) |
---|
| 58 | { |
---|
[66c373bf] | 59 | uint32_t val; |
---|
[e9ae97fb] | 60 | |
---|
| 61 | asm volatile ("mfdcr %0,0xC0":"=r" (val));/*EXISR*/ |
---|
| 62 | return val; |
---|
| 63 | } |
---|
| 64 | |
---|
| 65 | /* |
---|
| 66 | * get value of EXIER |
---|
| 67 | */ |
---|
[66c373bf] | 68 | RTEMS_INLINE_ROUTINE uint32_t |
---|
[e9ae97fb] | 69 | get_exier(void) |
---|
| 70 | { |
---|
[66c373bf] | 71 | uint32_t val; |
---|
[e9ae97fb] | 72 | asm volatile ("mfdcr %0,0xC2":"=r" (val));/*EXIER*/ |
---|
| 73 | return val; |
---|
| 74 | } |
---|
| 75 | |
---|
| 76 | /* |
---|
| 77 | * set value of EXIER |
---|
| 78 | */ |
---|
| 79 | RTEMS_INLINE_ROUTINE void |
---|
[66c373bf] | 80 | set_exier(uint32_t val) |
---|
[e9ae97fb] | 81 | { |
---|
| 82 | asm volatile ("mtdcr 0xC2,%0"::"r" (val));/*EXIER*/ |
---|
| 83 | } |
---|
| 84 | |
---|
| 85 | #else /* not ppc405 */ |
---|
| 86 | |
---|
[aecfa2b] | 87 | RTEMS_INLINE_ROUTINE void |
---|
[66c373bf] | 88 | clr_exisr(uint32_t mask) |
---|
[aecfa2b] | 89 | { |
---|
| 90 | asm volatile ("mtdcr 0x40,%0"::"r" (mask));/*EXISR*/ |
---|
| 91 | } |
---|
| 92 | |
---|
| 93 | /* |
---|
| 94 | * get value of EXISR |
---|
| 95 | */ |
---|
[66c373bf] | 96 | RTEMS_INLINE_ROUTINE uint32_t |
---|
[aecfa2b] | 97 | get_exisr(void) |
---|
| 98 | { |
---|
[66c373bf] | 99 | uint32_t val; |
---|
[aecfa2b] | 100 | |
---|
| 101 | asm volatile ("mfdcr %0,0x40":"=r" (val));/*EXISR*/ |
---|
| 102 | return val; |
---|
| 103 | } |
---|
| 104 | |
---|
| 105 | /* |
---|
| 106 | * get value of EXIER |
---|
| 107 | */ |
---|
[66c373bf] | 108 | RTEMS_INLINE_ROUTINE uint32_t |
---|
[aecfa2b] | 109 | get_exier(void) |
---|
| 110 | { |
---|
[66c373bf] | 111 | uint32_t val; |
---|
[aecfa2b] | 112 | asm volatile ("mfdcr %0,0x42":"=r" (val));/*EXIER*/ |
---|
| 113 | return val; |
---|
| 114 | } |
---|
| 115 | |
---|
| 116 | /* |
---|
| 117 | * set value of EXIER |
---|
| 118 | */ |
---|
| 119 | RTEMS_INLINE_ROUTINE void |
---|
[66c373bf] | 120 | set_exier(uint32_t val) |
---|
[aecfa2b] | 121 | { |
---|
| 122 | asm volatile ("mtdcr 0x42,%0"::"r" (val));/*EXIER*/ |
---|
| 123 | } |
---|
[e9ae97fb] | 124 | #endif /* ppc405 */ |
---|
[aecfa2b] | 125 | /* |
---|
| 126 | * enable an external interrupt, make this interrupt consistent |
---|
| 127 | */ |
---|
| 128 | RTEMS_INLINE_ROUTINE void |
---|
[66c373bf] | 129 | enable_ext_irq( uint32_t mask) |
---|
[aecfa2b] | 130 | { |
---|
[f93630d] | 131 | rtems_interrupt_level level; |
---|
| 132 | |
---|
| 133 | rtems_interrupt_disable(level); |
---|
[aecfa2b] | 134 | set_exier(get_exier() | ((mask)&PPC_EXI_MASK)); |
---|
[f93630d] | 135 | rtems_interrupt_enable(level); |
---|
[aecfa2b] | 136 | } |
---|
| 137 | |
---|
| 138 | /* |
---|
| 139 | * disable an external interrupt, make this interrupt consistent |
---|
| 140 | */ |
---|
| 141 | RTEMS_INLINE_ROUTINE void |
---|
[66c373bf] | 142 | disable_ext_irq( uint32_t mask) |
---|
[aecfa2b] | 143 | { |
---|
[f93630d] | 144 | rtems_interrupt_level level; |
---|
| 145 | |
---|
| 146 | rtems_interrupt_disable(level); |
---|
[aecfa2b] | 147 | set_exier(get_exier() & ~(mask) & PPC_EXI_MASK); |
---|
[f93630d] | 148 | rtems_interrupt_enable(level); |
---|
[aecfa2b] | 149 | } |
---|
| 150 | |
---|
| 151 | /* |
---|
| 152 | * |
---|
| 153 | * this function is called, when a external interrupt is present and |
---|
| 154 | * enabled but there is no handler installed. It will clear |
---|
| 155 | * the corresponding enable bits and call the spurious handler |
---|
[458bd34] | 156 | * present in the CPU Configuration Table, if any. |
---|
[aecfa2b] | 157 | * |
---|
| 158 | */ |
---|
| 159 | void |
---|
[66c373bf] | 160 | ictrl_spurious_handler(uint32_t spurious_mask, |
---|
[aecfa2b] | 161 | CPU_Interrupt_frame *cpu_frame) |
---|
| 162 | { |
---|
| 163 | int v; |
---|
| 164 | |
---|
| 165 | for (v=0; v < PPC_IRQ_EXT_MAX; v++) { |
---|
| 166 | if (VEC_TO_EXMSK(v) & spurious_mask) { |
---|
| 167 | clr_exisr(VEC_TO_EXMSK(v)); |
---|
| 168 | disable_ext_irq(VEC_TO_EXMSK(v)); |
---|
| 169 | #if 0 |
---|
| 170 | printf("spurious external interrupt: %d at pc 0x%x; disabling\n", |
---|
| 171 | vector, cpu_frame->Interrupt.pcoqfront); |
---|
| 172 | #endif |
---|
[458bd34] | 173 | if (rtems_cpu_configuration_get_spurious_handler()) { |
---|
| 174 | rtems_cpu_configuration_get_spurious_handler()(v + PPC_IRQ_EXT_BASE,cpu_frame); |
---|
[aecfa2b] | 175 | } |
---|
| 176 | } |
---|
| 177 | } |
---|
| 178 | } |
---|
| 179 | |
---|
| 180 | |
---|
| 181 | /* |
---|
| 182 | * ISR Handler: this is called from the primary exception dispatcher |
---|
| 183 | */ |
---|
| 184 | |
---|
| 185 | void |
---|
| 186 | ictrl_isr(rtems_vector_number vector,CPU_Interrupt_frame *cpu_frame) |
---|
| 187 | { |
---|
[66c373bf] | 188 | uint32_t istat, |
---|
[aecfa2b] | 189 | mask, |
---|
| 190 | global_vec; |
---|
| 191 | int exvec; |
---|
| 192 | rtems_isr_entry handler; |
---|
| 193 | |
---|
| 194 | istat = get_exisr() & get_exier() & PPC_EXI_MASK; |
---|
| 195 | |
---|
| 196 | /* FIXME: this may be speeded up using cntlzw instruction */ |
---|
| 197 | for (exvec = 0;exvec < PPC_IRQ_EXT_MAX;exvec++) { |
---|
| 198 | mask = VEC_TO_EXMSK(exvec); |
---|
| 199 | if (0 != (istat & mask)) { |
---|
[307685b] | 200 | /*clr_exisr(mask); too early to ack*/ |
---|
[aecfa2b] | 201 | handler = ictrl_vector_table[exvec]; |
---|
| 202 | if (handler) { |
---|
| 203 | istat &= ~mask; |
---|
| 204 | global_vec = exvec + PPC_IRQ_EXT_BASE; |
---|
| 205 | (handler)(global_vec); |
---|
| 206 | } |
---|
[307685b] | 207 | clr_exisr(mask);/* now we can ack*/ |
---|
[aecfa2b] | 208 | } |
---|
| 209 | } |
---|
| 210 | if (istat != 0) { /* anything left? then we have a spurious interrupt */ |
---|
| 211 | ictrl_spurious_handler(istat,cpu_frame); |
---|
| 212 | } |
---|
| 213 | } |
---|
| 214 | |
---|
| 215 | /* |
---|
| 216 | * |
---|
| 217 | * the following functions form the user interface |
---|
| 218 | * |
---|
| 219 | */ |
---|
| 220 | |
---|
| 221 | /* |
---|
| 222 | * |
---|
| 223 | * install a user vector for one of the external interrupt sources |
---|
| 224 | * |
---|
| 225 | */ |
---|
| 226 | rtems_status_code |
---|
| 227 | ictrl_set_vector(rtems_isr_entry new_handler, |
---|
[66c373bf] | 228 | uint32_t vector, |
---|
[aecfa2b] | 229 | rtems_isr_entry *old_handler |
---|
| 230 | ) |
---|
| 231 | { |
---|
| 232 | /* |
---|
| 233 | * We put the actual user ISR address in 'ictrl_vector_table'. This will |
---|
| 234 | * be used by the _ictrl_isr so the user gets control. |
---|
| 235 | */ |
---|
| 236 | |
---|
| 237 | /* check for valid vector range */ |
---|
| 238 | if ((vector >= PPC_IRQ_EXT_BASE) && |
---|
| 239 | (vector < PPC_IRQ_EXT_BASE + PPC_IRQ_EXT_MAX)) { |
---|
| 240 | /* return old handler entry */ |
---|
| 241 | *old_handler = ictrl_vector_table[vector - PPC_IRQ_EXT_BASE]; |
---|
| 242 | |
---|
| 243 | if (new_handler != NULL) { |
---|
| 244 | /* store handler function... */ |
---|
| 245 | ictrl_vector_table[vector - PPC_IRQ_EXT_BASE] = new_handler; |
---|
| 246 | /* then enable it in EXIER register */ |
---|
| 247 | enable_ext_irq(VEC_TO_EXMSK(vector - PPC_IRQ_EXT_BASE)); |
---|
| 248 | } |
---|
| 249 | else { /* new_handler == NULL */ |
---|
| 250 | /* then disable it in EXIER register */ |
---|
| 251 | disable_ext_irq(VEC_TO_EXMSK(vector - PPC_IRQ_EXT_BASE)); |
---|
| 252 | ictrl_vector_table[vector - PPC_IRQ_EXT_BASE] = NULL; |
---|
| 253 | } |
---|
| 254 | return RTEMS_SUCCESSFUL; |
---|
| 255 | } |
---|
| 256 | else { |
---|
| 257 | return RTEMS_INVALID_NUMBER; |
---|
| 258 | } |
---|
| 259 | } |
---|
| 260 | |
---|
| 261 | /* |
---|
| 262 | * Called via atexit() |
---|
| 263 | * deactivate the interrupt controller |
---|
| 264 | */ |
---|
| 265 | |
---|
| 266 | void |
---|
| 267 | ictrl_exit(void) |
---|
| 268 | { |
---|
| 269 | /* mark them all unused */ |
---|
| 270 | disable_ext_irq(~0); |
---|
| 271 | clr_exisr(~0); |
---|
| 272 | |
---|
| 273 | } |
---|
| 274 | |
---|
| 275 | /* |
---|
| 276 | * activate the interrupt controller |
---|
| 277 | */ |
---|
| 278 | |
---|
| 279 | rtems_status_code |
---|
| 280 | ictrl_init(void) |
---|
| 281 | { |
---|
| 282 | proc_ptr dummy; |
---|
| 283 | |
---|
| 284 | /* mark them all unused */ |
---|
| 285 | disable_ext_irq(~0); |
---|
| 286 | clr_exisr(~0); |
---|
| 287 | |
---|
| 288 | /* install the external interrupt handler */ |
---|
| 289 | _CPU_ISR_install_vector(PPC_IRQ_EXTERNAL, |
---|
| 290 | ictrl_isr, |
---|
| 291 | &dummy); |
---|
| 292 | atexit(ictrl_exit); |
---|
| 293 | return RTEMS_SUCCESSFUL; |
---|
| 294 | } |
---|