1907208c4SChristophe Leroy /* 2907208c4SChristophe Leroy * (C) Copyright 2000-2002 3907208c4SChristophe Leroy * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4907208c4SChristophe Leroy * 5907208c4SChristophe Leroy * SPDX-License-Identifier: GPL-2.0+ 6907208c4SChristophe Leroy */ 7907208c4SChristophe Leroy 8907208c4SChristophe Leroy #include <common.h> 9907208c4SChristophe Leroy #include <mpc8xx.h> 10907208c4SChristophe Leroy #include <mpc8xx_irq.h> 11907208c4SChristophe Leroy #include <asm/processor.h> 12*ba3da734SChristophe Leroy #include <asm/io.h> 13907208c4SChristophe Leroy #include <commproc.h> 14907208c4SChristophe Leroy 15907208c4SChristophe Leroy /************************************************************************/ 16907208c4SChristophe Leroy 17907208c4SChristophe Leroy /* 18907208c4SChristophe Leroy * CPM interrupt vector functions. 19907208c4SChristophe Leroy */ 20907208c4SChristophe Leroy struct interrupt_action { 21907208c4SChristophe Leroy interrupt_handler_t *handler; 22907208c4SChristophe Leroy void *arg; 23907208c4SChristophe Leroy }; 24907208c4SChristophe Leroy 25907208c4SChristophe Leroy static struct interrupt_action cpm_vecs[CPMVEC_NR]; 26907208c4SChristophe Leroy static struct interrupt_action irq_vecs[NR_IRQS]; 27907208c4SChristophe Leroy 28907208c4SChristophe Leroy static void cpm_interrupt_init (void); 29907208c4SChristophe Leroy static void cpm_interrupt (void *regs); 30907208c4SChristophe Leroy 31907208c4SChristophe Leroy /************************************************************************/ 32907208c4SChristophe Leroy 33907208c4SChristophe Leroy int interrupt_init_cpu (unsigned *decrementer_count) 34907208c4SChristophe Leroy { 35*ba3da734SChristophe Leroy immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR; 36907208c4SChristophe Leroy 37907208c4SChristophe Leroy *decrementer_count = get_tbclk () / CONFIG_SYS_HZ; 38907208c4SChristophe Leroy 39907208c4SChristophe Leroy /* disable all interrupts */ 40*ba3da734SChristophe Leroy out_be32(&immr->im_siu_conf.sc_simask, 0); 41907208c4SChristophe Leroy 42907208c4SChristophe Leroy /* Configure CPM interrupts */ 43907208c4SChristophe Leroy cpm_interrupt_init (); 44907208c4SChristophe Leroy 45907208c4SChristophe Leroy return (0); 46907208c4SChristophe Leroy } 47907208c4SChristophe Leroy 48907208c4SChristophe Leroy /************************************************************************/ 49907208c4SChristophe Leroy 50907208c4SChristophe Leroy /* 51907208c4SChristophe Leroy * Handle external interrupts 52907208c4SChristophe Leroy */ 53907208c4SChristophe Leroy void external_interrupt (struct pt_regs *regs) 54907208c4SChristophe Leroy { 55*ba3da734SChristophe Leroy immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR; 56907208c4SChristophe Leroy int irq; 57*ba3da734SChristophe Leroy ulong simask; 58907208c4SChristophe Leroy ulong vec, v_bit; 59907208c4SChristophe Leroy 60907208c4SChristophe Leroy /* 61907208c4SChristophe Leroy * read the SIVEC register and shift the bits down 62907208c4SChristophe Leroy * to get the irq number 63907208c4SChristophe Leroy */ 64*ba3da734SChristophe Leroy vec = in_be32(&immr->im_siu_conf.sc_sivec); 65907208c4SChristophe Leroy irq = vec >> 26; 66907208c4SChristophe Leroy v_bit = 0x80000000UL >> irq; 67907208c4SChristophe Leroy 68907208c4SChristophe Leroy /* 69907208c4SChristophe Leroy * Read Interrupt Mask Register and Mask Interrupts 70907208c4SChristophe Leroy */ 71*ba3da734SChristophe Leroy simask = in_be32(&immr->im_siu_conf.sc_simask); 72*ba3da734SChristophe Leroy clrbits_be32(&immr->im_siu_conf.sc_simask, 0xFFFF0000 >> irq); 73907208c4SChristophe Leroy 74907208c4SChristophe Leroy if (!(irq & 0x1)) { /* External Interrupt ? */ 75907208c4SChristophe Leroy ulong siel; 76907208c4SChristophe Leroy 77907208c4SChristophe Leroy /* 78907208c4SChristophe Leroy * Read Interrupt Edge/Level Register 79907208c4SChristophe Leroy */ 80*ba3da734SChristophe Leroy siel = in_be32(&immr->im_siu_conf.sc_siel); 81907208c4SChristophe Leroy 82907208c4SChristophe Leroy if (siel & v_bit) { /* edge triggered interrupt ? */ 83907208c4SChristophe Leroy /* 84907208c4SChristophe Leroy * Rewrite SIPEND Register to clear interrupt 85907208c4SChristophe Leroy */ 86*ba3da734SChristophe Leroy out_be32(&immr->im_siu_conf.sc_sipend, v_bit); 87907208c4SChristophe Leroy } 88907208c4SChristophe Leroy } 89907208c4SChristophe Leroy 90907208c4SChristophe Leroy if (irq_vecs[irq].handler != NULL) { 91907208c4SChristophe Leroy irq_vecs[irq].handler (irq_vecs[irq].arg); 92907208c4SChristophe Leroy } else { 93907208c4SChristophe Leroy printf ("\nBogus External Interrupt IRQ %d Vector %ld\n", 94907208c4SChristophe Leroy irq, vec); 95907208c4SChristophe Leroy /* turn off the bogus interrupt to avoid it from now */ 96907208c4SChristophe Leroy simask &= ~v_bit; 97907208c4SChristophe Leroy } 98907208c4SChristophe Leroy /* 99907208c4SChristophe Leroy * Re-Enable old Interrupt Mask 100907208c4SChristophe Leroy */ 101*ba3da734SChristophe Leroy out_be32(&immr->im_siu_conf.sc_simask, simask); 102907208c4SChristophe Leroy } 103907208c4SChristophe Leroy 104907208c4SChristophe Leroy /************************************************************************/ 105907208c4SChristophe Leroy 106907208c4SChristophe Leroy /* 107907208c4SChristophe Leroy * CPM interrupt handler 108907208c4SChristophe Leroy */ 109907208c4SChristophe Leroy static void cpm_interrupt (void *regs) 110907208c4SChristophe Leroy { 111*ba3da734SChristophe Leroy immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR; 112907208c4SChristophe Leroy uint vec; 113907208c4SChristophe Leroy 114907208c4SChristophe Leroy /* 115907208c4SChristophe Leroy * Get the vector by setting the ACK bit 116907208c4SChristophe Leroy * and then reading the register. 117907208c4SChristophe Leroy */ 118*ba3da734SChristophe Leroy out_be16(&immr->im_cpic.cpic_civr, 1); 119*ba3da734SChristophe Leroy vec = in_be16(&immr->im_cpic.cpic_civr); 120907208c4SChristophe Leroy vec >>= 11; 121907208c4SChristophe Leroy 122907208c4SChristophe Leroy if (cpm_vecs[vec].handler != NULL) { 123907208c4SChristophe Leroy (*cpm_vecs[vec].handler) (cpm_vecs[vec].arg); 124907208c4SChristophe Leroy } else { 125*ba3da734SChristophe Leroy clrbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec); 126907208c4SChristophe Leroy printf ("Masking bogus CPM interrupt vector 0x%x\n", vec); 127907208c4SChristophe Leroy } 128907208c4SChristophe Leroy /* 129907208c4SChristophe Leroy * After servicing the interrupt, 130907208c4SChristophe Leroy * we have to remove the status indicator. 131907208c4SChristophe Leroy */ 132*ba3da734SChristophe Leroy setbits_be32(&immr->im_cpic.cpic_cisr, 1 << vec); 133907208c4SChristophe Leroy } 134907208c4SChristophe Leroy 135907208c4SChristophe Leroy /* 136907208c4SChristophe Leroy * The CPM can generate the error interrupt when there is a race 137907208c4SChristophe Leroy * condition between generating and masking interrupts. All we have 138907208c4SChristophe Leroy * to do is ACK it and return. This is a no-op function so we don't 139907208c4SChristophe Leroy * need any special tests in the interrupt handler. 140907208c4SChristophe Leroy */ 141907208c4SChristophe Leroy static void cpm_error_interrupt (void *dummy) 142907208c4SChristophe Leroy { 143907208c4SChristophe Leroy } 144907208c4SChristophe Leroy 145907208c4SChristophe Leroy /************************************************************************/ 146907208c4SChristophe Leroy /* 147907208c4SChristophe Leroy * Install and free an interrupt handler 148907208c4SChristophe Leroy */ 149907208c4SChristophe Leroy void irq_install_handler (int vec, interrupt_handler_t * handler, 150907208c4SChristophe Leroy void *arg) 151907208c4SChristophe Leroy { 152*ba3da734SChristophe Leroy immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR; 153907208c4SChristophe Leroy 154907208c4SChristophe Leroy if ((vec & CPMVEC_OFFSET) != 0) { 155907208c4SChristophe Leroy /* CPM interrupt */ 156907208c4SChristophe Leroy vec &= 0xffff; 157907208c4SChristophe Leroy if (cpm_vecs[vec].handler != NULL) { 158907208c4SChristophe Leroy printf ("CPM interrupt 0x%x replacing 0x%x\n", 159907208c4SChristophe Leroy (uint) handler, 160907208c4SChristophe Leroy (uint) cpm_vecs[vec].handler); 161907208c4SChristophe Leroy } 162907208c4SChristophe Leroy cpm_vecs[vec].handler = handler; 163907208c4SChristophe Leroy cpm_vecs[vec].arg = arg; 164*ba3da734SChristophe Leroy setbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec); 165907208c4SChristophe Leroy } else { 166907208c4SChristophe Leroy /* SIU interrupt */ 167907208c4SChristophe Leroy if (irq_vecs[vec].handler != NULL) { 168907208c4SChristophe Leroy printf ("SIU interrupt %d 0x%x replacing 0x%x\n", 169907208c4SChristophe Leroy vec, 170907208c4SChristophe Leroy (uint) handler, 171907208c4SChristophe Leroy (uint) cpm_vecs[vec].handler); 172907208c4SChristophe Leroy } 173907208c4SChristophe Leroy irq_vecs[vec].handler = handler; 174907208c4SChristophe Leroy irq_vecs[vec].arg = arg; 175*ba3da734SChristophe Leroy setbits_be32(&immr->im_siu_conf.sc_simask, 1 << (31 - vec)); 176907208c4SChristophe Leroy } 177907208c4SChristophe Leroy } 178907208c4SChristophe Leroy 179907208c4SChristophe Leroy void irq_free_handler (int vec) 180907208c4SChristophe Leroy { 181*ba3da734SChristophe Leroy immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR; 182907208c4SChristophe Leroy 183907208c4SChristophe Leroy if ((vec & CPMVEC_OFFSET) != 0) { 184907208c4SChristophe Leroy /* CPM interrupt */ 185907208c4SChristophe Leroy vec &= 0xffff; 186*ba3da734SChristophe Leroy clrbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec); 187907208c4SChristophe Leroy cpm_vecs[vec].handler = NULL; 188907208c4SChristophe Leroy cpm_vecs[vec].arg = NULL; 189907208c4SChristophe Leroy } else { 190907208c4SChristophe Leroy /* SIU interrupt */ 191*ba3da734SChristophe Leroy clrbits_be32(&immr->im_siu_conf.sc_simask, 1 << (31 - vec)); 192907208c4SChristophe Leroy irq_vecs[vec].handler = NULL; 193907208c4SChristophe Leroy irq_vecs[vec].arg = NULL; 194907208c4SChristophe Leroy } 195907208c4SChristophe Leroy } 196907208c4SChristophe Leroy 197907208c4SChristophe Leroy /************************************************************************/ 198907208c4SChristophe Leroy 199907208c4SChristophe Leroy static void cpm_interrupt_init (void) 200907208c4SChristophe Leroy { 201*ba3da734SChristophe Leroy immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR; 202*ba3da734SChristophe Leroy uint cicr; 203907208c4SChristophe Leroy 204907208c4SChristophe Leroy /* 205907208c4SChristophe Leroy * Initialize the CPM interrupt controller. 206907208c4SChristophe Leroy */ 207907208c4SChristophe Leroy 208*ba3da734SChristophe Leroy cicr = CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1 | 209*ba3da734SChristophe Leroy ((CPM_INTERRUPT / 2) << 13) | CICR_HP_MASK; 210907208c4SChristophe Leroy 211*ba3da734SChristophe Leroy out_be32(&immr->im_cpic.cpic_cicr, cicr); 212*ba3da734SChristophe Leroy out_be32(&immr->im_cpic.cpic_cimr, 0); 213907208c4SChristophe Leroy 214907208c4SChristophe Leroy /* 215907208c4SChristophe Leroy * Install the error handler. 216907208c4SChristophe Leroy */ 217907208c4SChristophe Leroy irq_install_handler (CPMVEC_ERROR, cpm_error_interrupt, NULL); 218907208c4SChristophe Leroy 219*ba3da734SChristophe Leroy setbits_be32(&immr->im_cpic.cpic_cicr, CICR_IEN); 220907208c4SChristophe Leroy 221907208c4SChristophe Leroy /* 222907208c4SChristophe Leroy * Install the cpm interrupt handler 223907208c4SChristophe Leroy */ 224907208c4SChristophe Leroy irq_install_handler (CPM_INTERRUPT, cpm_interrupt, NULL); 225907208c4SChristophe Leroy } 226907208c4SChristophe Leroy 227907208c4SChristophe Leroy /************************************************************************/ 228907208c4SChristophe Leroy 229907208c4SChristophe Leroy /* 230907208c4SChristophe Leroy * timer_interrupt - gets called when the decrementer overflows, 231907208c4SChristophe Leroy * with interrupts disabled. 232907208c4SChristophe Leroy * Trivial implementation - no need to be really accurate. 233907208c4SChristophe Leroy */ 234907208c4SChristophe Leroy void timer_interrupt_cpu (struct pt_regs *regs) 235907208c4SChristophe Leroy { 236*ba3da734SChristophe Leroy immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR; 237907208c4SChristophe Leroy 238907208c4SChristophe Leroy /* Reset Timer Expired and Timers Interrupt Status */ 239*ba3da734SChristophe Leroy out_be32(&immr->im_clkrstk.cark_plprcrk, KAPWR_KEY); 240907208c4SChristophe Leroy __asm__ ("nop"); 241907208c4SChristophe Leroy /* 242907208c4SChristophe Leroy Clear TEXPS (and TMIST on older chips). SPLSS (on older 243907208c4SChristophe Leroy chips) is cleared too. 244907208c4SChristophe Leroy 245907208c4SChristophe Leroy Bitwise OR is a read-modify-write operation so ALL bits 246907208c4SChristophe Leroy which are cleared by writing `1' would be cleared by 247907208c4SChristophe Leroy operations like 248907208c4SChristophe Leroy 249907208c4SChristophe Leroy immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS; 250907208c4SChristophe Leroy 251907208c4SChristophe Leroy The same can be achieved by simple writing of the PLPRCR 252907208c4SChristophe Leroy to itself. If a bit value should be preserved, read the 253907208c4SChristophe Leroy register, ZERO the bit and write, not OR, the result back. 254907208c4SChristophe Leroy */ 255*ba3da734SChristophe Leroy setbits_be32(&immr->im_clkrst.car_plprcr, 0); 256907208c4SChristophe Leroy } 257907208c4SChristophe Leroy 258907208c4SChristophe Leroy /************************************************************************/ 259