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> 12ba3da734SChristophe 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 33*deff9b1dSTom Rini void interrupt_init_cpu(unsigned *decrementer_count) 34907208c4SChristophe Leroy { 35ba3da734SChristophe 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 */ 40ba3da734SChristophe 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 46907208c4SChristophe Leroy /************************************************************************/ 47907208c4SChristophe Leroy 48907208c4SChristophe Leroy /* 49907208c4SChristophe Leroy * Handle external interrupts 50907208c4SChristophe Leroy */ 51907208c4SChristophe Leroy void external_interrupt(struct pt_regs *regs) 52907208c4SChristophe Leroy { 53ba3da734SChristophe Leroy immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR; 54907208c4SChristophe Leroy int irq; 55ba3da734SChristophe Leroy ulong simask; 56907208c4SChristophe Leroy ulong vec, v_bit; 57907208c4SChristophe Leroy 58907208c4SChristophe Leroy /* 59907208c4SChristophe Leroy * read the SIVEC register and shift the bits down 60907208c4SChristophe Leroy * to get the irq number 61907208c4SChristophe Leroy */ 62ba3da734SChristophe Leroy vec = in_be32(&immr->im_siu_conf.sc_sivec); 63907208c4SChristophe Leroy irq = vec >> 26; 64907208c4SChristophe Leroy v_bit = 0x80000000UL >> irq; 65907208c4SChristophe Leroy 66907208c4SChristophe Leroy /* 67907208c4SChristophe Leroy * Read Interrupt Mask Register and Mask Interrupts 68907208c4SChristophe Leroy */ 69ba3da734SChristophe Leroy simask = in_be32(&immr->im_siu_conf.sc_simask); 70ba3da734SChristophe Leroy clrbits_be32(&immr->im_siu_conf.sc_simask, 0xFFFF0000 >> irq); 71907208c4SChristophe Leroy 72907208c4SChristophe Leroy if (!(irq & 0x1)) { /* External Interrupt ? */ 73907208c4SChristophe Leroy ulong siel; 74907208c4SChristophe Leroy 75907208c4SChristophe Leroy /* 76907208c4SChristophe Leroy * Read Interrupt Edge/Level Register 77907208c4SChristophe Leroy */ 78ba3da734SChristophe Leroy siel = in_be32(&immr->im_siu_conf.sc_siel); 79907208c4SChristophe Leroy 80907208c4SChristophe Leroy if (siel & v_bit) { /* edge triggered interrupt ? */ 81907208c4SChristophe Leroy /* 82907208c4SChristophe Leroy * Rewrite SIPEND Register to clear interrupt 83907208c4SChristophe Leroy */ 84ba3da734SChristophe Leroy out_be32(&immr->im_siu_conf.sc_sipend, v_bit); 85907208c4SChristophe Leroy } 86907208c4SChristophe Leroy } 87907208c4SChristophe Leroy 88907208c4SChristophe Leroy if (irq_vecs[irq].handler != NULL) { 89907208c4SChristophe Leroy irq_vecs[irq].handler(irq_vecs[irq].arg); 90907208c4SChristophe Leroy } else { 91907208c4SChristophe Leroy printf("\nBogus External Interrupt IRQ %d Vector %ld\n", 92907208c4SChristophe Leroy irq, vec); 93907208c4SChristophe Leroy /* turn off the bogus interrupt to avoid it from now */ 94907208c4SChristophe Leroy simask &= ~v_bit; 95907208c4SChristophe Leroy } 96907208c4SChristophe Leroy /* 97907208c4SChristophe Leroy * Re-Enable old Interrupt Mask 98907208c4SChristophe Leroy */ 99ba3da734SChristophe Leroy out_be32(&immr->im_siu_conf.sc_simask, simask); 100907208c4SChristophe Leroy } 101907208c4SChristophe Leroy 102907208c4SChristophe Leroy /************************************************************************/ 103907208c4SChristophe Leroy 104907208c4SChristophe Leroy /* 105907208c4SChristophe Leroy * CPM interrupt handler 106907208c4SChristophe Leroy */ 107907208c4SChristophe Leroy static void cpm_interrupt(void *regs) 108907208c4SChristophe Leroy { 109ba3da734SChristophe Leroy immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR; 110907208c4SChristophe Leroy uint vec; 111907208c4SChristophe Leroy 112907208c4SChristophe Leroy /* 113907208c4SChristophe Leroy * Get the vector by setting the ACK bit 114907208c4SChristophe Leroy * and then reading the register. 115907208c4SChristophe Leroy */ 116ba3da734SChristophe Leroy out_be16(&immr->im_cpic.cpic_civr, 1); 117ba3da734SChristophe Leroy vec = in_be16(&immr->im_cpic.cpic_civr); 118907208c4SChristophe Leroy vec >>= 11; 119907208c4SChristophe Leroy 120907208c4SChristophe Leroy if (cpm_vecs[vec].handler != NULL) { 121907208c4SChristophe Leroy (*cpm_vecs[vec].handler) (cpm_vecs[vec].arg); 122907208c4SChristophe Leroy } else { 123ba3da734SChristophe Leroy clrbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec); 124907208c4SChristophe Leroy printf("Masking bogus CPM interrupt vector 0x%x\n", vec); 125907208c4SChristophe Leroy } 126907208c4SChristophe Leroy /* 127907208c4SChristophe Leroy * After servicing the interrupt, 128907208c4SChristophe Leroy * we have to remove the status indicator. 129907208c4SChristophe Leroy */ 130ba3da734SChristophe Leroy setbits_be32(&immr->im_cpic.cpic_cisr, 1 << vec); 131907208c4SChristophe Leroy } 132907208c4SChristophe Leroy 133907208c4SChristophe Leroy /* 134907208c4SChristophe Leroy * The CPM can generate the error interrupt when there is a race 135907208c4SChristophe Leroy * condition between generating and masking interrupts. All we have 136907208c4SChristophe Leroy * to do is ACK it and return. This is a no-op function so we don't 137907208c4SChristophe Leroy * need any special tests in the interrupt handler. 138907208c4SChristophe Leroy */ 139907208c4SChristophe Leroy static void cpm_error_interrupt(void *dummy) 140907208c4SChristophe Leroy { 141907208c4SChristophe Leroy } 142907208c4SChristophe Leroy 143907208c4SChristophe Leroy /************************************************************************/ 144907208c4SChristophe Leroy /* 145907208c4SChristophe Leroy * Install and free an interrupt handler 146907208c4SChristophe Leroy */ 14770fd0710SChristophe Leroy void irq_install_handler(int vec, interrupt_handler_t *handler, void *arg) 148907208c4SChristophe Leroy { 149ba3da734SChristophe Leroy immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR; 150907208c4SChristophe Leroy 151907208c4SChristophe Leroy if ((vec & CPMVEC_OFFSET) != 0) { 152907208c4SChristophe Leroy /* CPM interrupt */ 153907208c4SChristophe Leroy vec &= 0xffff; 15470fd0710SChristophe Leroy if (cpm_vecs[vec].handler != NULL) 155907208c4SChristophe Leroy printf("CPM interrupt 0x%x replacing 0x%x\n", 15670fd0710SChristophe Leroy (uint)handler, (uint)cpm_vecs[vec].handler); 157907208c4SChristophe Leroy cpm_vecs[vec].handler = handler; 158907208c4SChristophe Leroy cpm_vecs[vec].arg = arg; 159ba3da734SChristophe Leroy setbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec); 160907208c4SChristophe Leroy } else { 161907208c4SChristophe Leroy /* SIU interrupt */ 16270fd0710SChristophe Leroy if (irq_vecs[vec].handler != NULL) 163907208c4SChristophe Leroy printf("SIU interrupt %d 0x%x replacing 0x%x\n", 16470fd0710SChristophe Leroy vec, (uint)handler, (uint)cpm_vecs[vec].handler); 165907208c4SChristophe Leroy irq_vecs[vec].handler = handler; 166907208c4SChristophe Leroy irq_vecs[vec].arg = arg; 167ba3da734SChristophe Leroy setbits_be32(&immr->im_siu_conf.sc_simask, 1 << (31 - vec)); 168907208c4SChristophe Leroy } 169907208c4SChristophe Leroy } 170907208c4SChristophe Leroy 171907208c4SChristophe Leroy void irq_free_handler(int vec) 172907208c4SChristophe Leroy { 173ba3da734SChristophe Leroy immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR; 174907208c4SChristophe Leroy 175907208c4SChristophe Leroy if ((vec & CPMVEC_OFFSET) != 0) { 176907208c4SChristophe Leroy /* CPM interrupt */ 177907208c4SChristophe Leroy vec &= 0xffff; 178ba3da734SChristophe Leroy clrbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec); 179907208c4SChristophe Leroy cpm_vecs[vec].handler = NULL; 180907208c4SChristophe Leroy cpm_vecs[vec].arg = NULL; 181907208c4SChristophe Leroy } else { 182907208c4SChristophe Leroy /* SIU interrupt */ 183ba3da734SChristophe Leroy clrbits_be32(&immr->im_siu_conf.sc_simask, 1 << (31 - vec)); 184907208c4SChristophe Leroy irq_vecs[vec].handler = NULL; 185907208c4SChristophe Leroy irq_vecs[vec].arg = NULL; 186907208c4SChristophe Leroy } 187907208c4SChristophe Leroy } 188907208c4SChristophe Leroy 189907208c4SChristophe Leroy /************************************************************************/ 190907208c4SChristophe Leroy 191907208c4SChristophe Leroy static void cpm_interrupt_init(void) 192907208c4SChristophe Leroy { 193ba3da734SChristophe Leroy immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR; 194ba3da734SChristophe Leroy uint cicr; 195907208c4SChristophe Leroy 196907208c4SChristophe Leroy /* 197907208c4SChristophe Leroy * Initialize the CPM interrupt controller. 198907208c4SChristophe Leroy */ 199907208c4SChristophe Leroy 200ba3da734SChristophe Leroy cicr = CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1 | 201ba3da734SChristophe Leroy ((CPM_INTERRUPT / 2) << 13) | CICR_HP_MASK; 202907208c4SChristophe Leroy 203ba3da734SChristophe Leroy out_be32(&immr->im_cpic.cpic_cicr, cicr); 204ba3da734SChristophe Leroy out_be32(&immr->im_cpic.cpic_cimr, 0); 205907208c4SChristophe Leroy 206907208c4SChristophe Leroy /* 207907208c4SChristophe Leroy * Install the error handler. 208907208c4SChristophe Leroy */ 209907208c4SChristophe Leroy irq_install_handler(CPMVEC_ERROR, cpm_error_interrupt, NULL); 210907208c4SChristophe Leroy 211ba3da734SChristophe Leroy setbits_be32(&immr->im_cpic.cpic_cicr, CICR_IEN); 212907208c4SChristophe Leroy 213907208c4SChristophe Leroy /* 214907208c4SChristophe Leroy * Install the cpm interrupt handler 215907208c4SChristophe Leroy */ 216907208c4SChristophe Leroy irq_install_handler(CPM_INTERRUPT, cpm_interrupt, NULL); 217907208c4SChristophe Leroy } 218907208c4SChristophe Leroy 219907208c4SChristophe Leroy /************************************************************************/ 220907208c4SChristophe Leroy 221907208c4SChristophe Leroy /* 222907208c4SChristophe Leroy * timer_interrupt - gets called when the decrementer overflows, 223907208c4SChristophe Leroy * with interrupts disabled. 224907208c4SChristophe Leroy * Trivial implementation - no need to be really accurate. 225907208c4SChristophe Leroy */ 226907208c4SChristophe Leroy void timer_interrupt_cpu(struct pt_regs *regs) 227907208c4SChristophe Leroy { 228ba3da734SChristophe Leroy immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR; 229907208c4SChristophe Leroy 230907208c4SChristophe Leroy /* Reset Timer Expired and Timers Interrupt Status */ 231ba3da734SChristophe Leroy out_be32(&immr->im_clkrstk.cark_plprcrk, KAPWR_KEY); 232907208c4SChristophe Leroy __asm__ ("nop"); 233907208c4SChristophe Leroy /* 234907208c4SChristophe Leroy Clear TEXPS (and TMIST on older chips). SPLSS (on older 235907208c4SChristophe Leroy chips) is cleared too. 236907208c4SChristophe Leroy 237907208c4SChristophe Leroy Bitwise OR is a read-modify-write operation so ALL bits 238907208c4SChristophe Leroy which are cleared by writing `1' would be cleared by 239907208c4SChristophe Leroy operations like 240907208c4SChristophe Leroy 241907208c4SChristophe Leroy immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS; 242907208c4SChristophe Leroy 243907208c4SChristophe Leroy The same can be achieved by simple writing of the PLPRCR 244907208c4SChristophe Leroy to itself. If a bit value should be preserved, read the 245907208c4SChristophe Leroy register, ZERO the bit and write, not OR, the result back. 246907208c4SChristophe Leroy */ 247ba3da734SChristophe Leroy setbits_be32(&immr->im_clkrst.car_plprcr, 0); 248907208c4SChristophe Leroy } 249907208c4SChristophe Leroy 250907208c4SChristophe Leroy /************************************************************************/ 251