1 /* 2 * (C) Copyright 2000-2002 3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4 * 5 * See file CREDITS for list of people who contributed to this 6 * project. 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License as 10 * published by the Free Software Foundation; either version 2 of 11 * the License, or (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 21 * MA 02111-1307 USA 22 */ 23 24 #include <common.h> 25 #include <mpc8xx.h> 26 #include <mpc8xx_irq.h> 27 #include <asm/processor.h> 28 #include <commproc.h> 29 30 /************************************************************************/ 31 32 /* 33 * CPM interrupt vector functions. 34 */ 35 struct interrupt_action { 36 interrupt_handler_t *handler; 37 void *arg; 38 }; 39 40 static struct interrupt_action cpm_vecs[CPMVEC_NR]; 41 static struct interrupt_action irq_vecs[NR_IRQS]; 42 43 static void cpm_interrupt_init (void); 44 static void cpm_interrupt (void *regs); 45 46 /************************************************************************/ 47 48 int interrupt_init_cpu (unsigned *decrementer_count) 49 { 50 volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; 51 52 *decrementer_count = get_tbclk () / CONFIG_SYS_HZ; 53 54 /* disable all interrupts */ 55 immr->im_siu_conf.sc_simask = 0; 56 57 /* Configure CPM interrupts */ 58 cpm_interrupt_init (); 59 60 return (0); 61 } 62 63 /************************************************************************/ 64 65 /* 66 * Handle external interrupts 67 */ 68 void external_interrupt (struct pt_regs *regs) 69 { 70 volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; 71 int irq; 72 ulong simask, newmask; 73 ulong vec, v_bit; 74 75 /* 76 * read the SIVEC register and shift the bits down 77 * to get the irq number 78 */ 79 vec = immr->im_siu_conf.sc_sivec; 80 irq = vec >> 26; 81 v_bit = 0x80000000UL >> irq; 82 83 /* 84 * Read Interrupt Mask Register and Mask Interrupts 85 */ 86 simask = immr->im_siu_conf.sc_simask; 87 newmask = simask & (~(0xFFFF0000 >> irq)); 88 immr->im_siu_conf.sc_simask = newmask; 89 90 if (!(irq & 0x1)) { /* External Interrupt ? */ 91 ulong siel; 92 93 /* 94 * Read Interrupt Edge/Level Register 95 */ 96 siel = immr->im_siu_conf.sc_siel; 97 98 if (siel & v_bit) { /* edge triggered interrupt ? */ 99 /* 100 * Rewrite SIPEND Register to clear interrupt 101 */ 102 immr->im_siu_conf.sc_sipend = v_bit; 103 } 104 } 105 106 if (irq_vecs[irq].handler != NULL) { 107 irq_vecs[irq].handler (irq_vecs[irq].arg); 108 } else { 109 printf ("\nBogus External Interrupt IRQ %d Vector %ld\n", 110 irq, vec); 111 /* turn off the bogus interrupt to avoid it from now */ 112 simask &= ~v_bit; 113 } 114 /* 115 * Re-Enable old Interrupt Mask 116 */ 117 immr->im_siu_conf.sc_simask = simask; 118 } 119 120 /************************************************************************/ 121 122 /* 123 * CPM interrupt handler 124 */ 125 static void cpm_interrupt (void *regs) 126 { 127 volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; 128 uint vec; 129 130 /* 131 * Get the vector by setting the ACK bit 132 * and then reading the register. 133 */ 134 immr->im_cpic.cpic_civr = 1; 135 vec = immr->im_cpic.cpic_civr; 136 vec >>= 11; 137 138 if (cpm_vecs[vec].handler != NULL) { 139 (*cpm_vecs[vec].handler) (cpm_vecs[vec].arg); 140 } else { 141 immr->im_cpic.cpic_cimr &= ~(1 << vec); 142 printf ("Masking bogus CPM interrupt vector 0x%x\n", vec); 143 } 144 /* 145 * After servicing the interrupt, 146 * we have to remove the status indicator. 147 */ 148 immr->im_cpic.cpic_cisr |= (1 << vec); 149 } 150 151 /* 152 * The CPM can generate the error interrupt when there is a race 153 * condition between generating and masking interrupts. All we have 154 * to do is ACK it and return. This is a no-op function so we don't 155 * need any special tests in the interrupt handler. 156 */ 157 static void cpm_error_interrupt (void *dummy) 158 { 159 } 160 161 /************************************************************************/ 162 /* 163 * Install and free an interrupt handler 164 */ 165 void irq_install_handler (int vec, interrupt_handler_t * handler, 166 void *arg) 167 { 168 volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; 169 170 if ((vec & CPMVEC_OFFSET) != 0) { 171 /* CPM interrupt */ 172 vec &= 0xffff; 173 if (cpm_vecs[vec].handler != NULL) { 174 printf ("CPM interrupt 0x%x replacing 0x%x\n", 175 (uint) handler, 176 (uint) cpm_vecs[vec].handler); 177 } 178 cpm_vecs[vec].handler = handler; 179 cpm_vecs[vec].arg = arg; 180 immr->im_cpic.cpic_cimr |= (1 << vec); 181 #if 0 182 printf ("Install CPM interrupt for vector %d ==> %p\n", 183 vec, handler); 184 #endif 185 } else { 186 /* SIU interrupt */ 187 if (irq_vecs[vec].handler != NULL) { 188 printf ("SIU interrupt %d 0x%x replacing 0x%x\n", 189 vec, 190 (uint) handler, 191 (uint) cpm_vecs[vec].handler); 192 } 193 irq_vecs[vec].handler = handler; 194 irq_vecs[vec].arg = arg; 195 immr->im_siu_conf.sc_simask |= 1 << (31 - vec); 196 #if 0 197 printf ("Install SIU interrupt for vector %d ==> %p\n", 198 vec, handler); 199 #endif 200 } 201 } 202 203 void irq_free_handler (int vec) 204 { 205 volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; 206 207 if ((vec & CPMVEC_OFFSET) != 0) { 208 /* CPM interrupt */ 209 vec &= 0xffff; 210 #if 0 211 printf ("Free CPM interrupt for vector %d ==> %p\n", 212 vec, cpm_vecs[vec].handler); 213 #endif 214 immr->im_cpic.cpic_cimr &= ~(1 << vec); 215 cpm_vecs[vec].handler = NULL; 216 cpm_vecs[vec].arg = NULL; 217 } else { 218 /* SIU interrupt */ 219 #if 0 220 printf ("Free CPM interrupt for vector %d ==> %p\n", 221 vec, cpm_vecs[vec].handler); 222 #endif 223 immr->im_siu_conf.sc_simask &= ~(1 << (31 - vec)); 224 irq_vecs[vec].handler = NULL; 225 irq_vecs[vec].arg = NULL; 226 } 227 } 228 229 /************************************************************************/ 230 231 static void cpm_interrupt_init (void) 232 { 233 volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; 234 235 /* 236 * Initialize the CPM interrupt controller. 237 */ 238 239 immr->im_cpic.cpic_cicr = 240 (CICR_SCD_SCC4 | 241 CICR_SCC_SCC3 | 242 CICR_SCB_SCC2 | 243 CICR_SCA_SCC1) | ((CPM_INTERRUPT / 2) << 13) | CICR_HP_MASK; 244 245 immr->im_cpic.cpic_cimr = 0; 246 247 /* 248 * Install the error handler. 249 */ 250 irq_install_handler (CPMVEC_ERROR, cpm_error_interrupt, NULL); 251 252 immr->im_cpic.cpic_cicr |= CICR_IEN; 253 254 /* 255 * Install the cpm interrupt handler 256 */ 257 irq_install_handler (CPM_INTERRUPT, cpm_interrupt, NULL); 258 } 259 260 /************************************************************************/ 261 262 /* 263 * timer_interrupt - gets called when the decrementer overflows, 264 * with interrupts disabled. 265 * Trivial implementation - no need to be really accurate. 266 */ 267 void timer_interrupt_cpu (struct pt_regs *regs) 268 { 269 volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; 270 271 #if 0 272 printf ("*** Timer Interrupt *** "); 273 #endif 274 /* Reset Timer Expired and Timers Interrupt Status */ 275 immr->im_clkrstk.cark_plprcrk = KAPWR_KEY; 276 __asm__ ("nop"); 277 /* 278 Clear TEXPS (and TMIST on older chips). SPLSS (on older 279 chips) is cleared too. 280 281 Bitwise OR is a read-modify-write operation so ALL bits 282 which are cleared by writing `1' would be cleared by 283 operations like 284 285 immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS; 286 287 The same can be achieved by simple writing of the PLPRCR 288 to itself. If a bit value should be preserved, read the 289 register, ZERO the bit and write, not OR, the result back. 290 */ 291 immr->im_clkrst.car_plprcr = immr->im_clkrst.car_plprcr; 292 } 293 294 /************************************************************************/ 295