uboot/arch/powerpc/cpu/mpc8xx/interrupts.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2000-2002
   4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   5 */
   6
   7#include <common.h>
   8#include <irq_func.h>
   9#include <mpc8xx.h>
  10#include <mpc8xx_irq.h>
  11#include <asm/cpm_8xx.h>
  12#include <asm/processor.h>
  13#include <asm/io.h>
  14
  15/************************************************************************/
  16
  17/*
  18 * CPM interrupt vector functions.
  19 */
  20struct interrupt_action {
  21        interrupt_handler_t *handler;
  22        void *arg;
  23};
  24
  25static struct interrupt_action cpm_vecs[CPMVEC_NR];
  26static struct interrupt_action irq_vecs[NR_IRQS];
  27
  28static void cpm_interrupt_init(void);
  29static void cpm_interrupt(void *regs);
  30
  31/************************************************************************/
  32
  33void interrupt_init_cpu(unsigned *decrementer_count)
  34{
  35        immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
  36
  37        *decrementer_count = get_tbclk() / CONFIG_SYS_HZ;
  38
  39        /* disable all interrupts */
  40        out_be32(&immr->im_siu_conf.sc_simask, 0);
  41
  42        /* Configure CPM interrupts */
  43        cpm_interrupt_init();
  44}
  45
  46/************************************************************************/
  47
  48/*
  49 * Handle external interrupts
  50 */
  51void external_interrupt(struct pt_regs *regs)
  52{
  53        immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
  54        int irq;
  55        ulong simask;
  56        ulong vec, v_bit;
  57
  58        /*
  59         * read the SIVEC register and shift the bits down
  60         * to get the irq number
  61         */
  62        vec = in_be32(&immr->im_siu_conf.sc_sivec);
  63        irq = vec >> 26;
  64        v_bit = 0x80000000UL >> irq;
  65
  66        /*
  67         * Read Interrupt Mask Register and Mask Interrupts
  68         */
  69        simask = in_be32(&immr->im_siu_conf.sc_simask);
  70        clrbits_be32(&immr->im_siu_conf.sc_simask, 0xFFFF0000 >> irq);
  71
  72        if (!(irq & 0x1)) {             /* External Interrupt ?     */
  73                ulong siel;
  74
  75                /*
  76                 * Read Interrupt Edge/Level Register
  77                 */
  78                siel = in_be32(&immr->im_siu_conf.sc_siel);
  79
  80                if (siel & v_bit) {     /* edge triggered interrupt ?   */
  81                        /*
  82                         * Rewrite SIPEND Register to clear interrupt
  83                         */
  84                        out_be32(&immr->im_siu_conf.sc_sipend, v_bit);
  85                }
  86        }
  87
  88        if (irq_vecs[irq].handler != NULL) {
  89                irq_vecs[irq].handler(irq_vecs[irq].arg);
  90        } else {
  91                printf("\nBogus External Interrupt IRQ %d Vector %ld\n",
  92                       irq, vec);
  93                /* turn off the bogus interrupt to avoid it from now */
  94                simask &= ~v_bit;
  95        }
  96        /*
  97         * Re-Enable old Interrupt Mask
  98         */
  99        out_be32(&immr->im_siu_conf.sc_simask, simask);
 100}
 101
 102/************************************************************************/
 103
 104/*
 105 * CPM interrupt handler
 106 */
 107static void cpm_interrupt(void *regs)
 108{
 109        immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
 110        uint vec;
 111
 112        /*
 113         * Get the vector by setting the ACK bit
 114         * and then reading the register.
 115         */
 116        out_be16(&immr->im_cpic.cpic_civr, 1);
 117        vec = in_be16(&immr->im_cpic.cpic_civr);
 118        vec >>= 11;
 119
 120        if (cpm_vecs[vec].handler != NULL) {
 121                (*cpm_vecs[vec].handler) (cpm_vecs[vec].arg);
 122        } else {
 123                clrbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec);
 124                printf("Masking bogus CPM interrupt vector 0x%x\n", vec);
 125        }
 126        /*
 127         * After servicing the interrupt,
 128         * we have to remove the status indicator.
 129         */
 130        setbits_be32(&immr->im_cpic.cpic_cisr, 1 << vec);
 131}
 132
 133/*
 134 * The CPM can generate the error interrupt when there is a race
 135 * condition between generating and masking interrupts. All we have
 136 * to do is ACK it and return. This is a no-op function so we don't
 137 * need any special tests in the interrupt handler.
 138 */
 139static void cpm_error_interrupt(void *dummy)
 140{
 141}
 142
 143/************************************************************************/
 144/*
 145 * Install and free an interrupt handler
 146 */
 147void irq_install_handler(int vec, interrupt_handler_t *handler, void *arg)
 148{
 149        immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
 150
 151        if ((vec & CPMVEC_OFFSET) != 0) {
 152                /* CPM interrupt */
 153                vec &= 0xffff;
 154                if (cpm_vecs[vec].handler != NULL)
 155                        printf("CPM interrupt 0x%x replacing 0x%x\n",
 156                               (uint)handler, (uint)cpm_vecs[vec].handler);
 157                cpm_vecs[vec].handler = handler;
 158                cpm_vecs[vec].arg = arg;
 159                setbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec);
 160        } else {
 161                /* SIU interrupt */
 162                if (irq_vecs[vec].handler != NULL)
 163                        printf("SIU interrupt %d 0x%x replacing 0x%x\n",
 164                               vec, (uint)handler, (uint)cpm_vecs[vec].handler);
 165                irq_vecs[vec].handler = handler;
 166                irq_vecs[vec].arg = arg;
 167                setbits_be32(&immr->im_siu_conf.sc_simask, 1 << (31 - vec));
 168        }
 169}
 170
 171void irq_free_handler(int vec)
 172{
 173        immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
 174
 175        if ((vec & CPMVEC_OFFSET) != 0) {
 176                /* CPM interrupt */
 177                vec &= 0xffff;
 178                clrbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec);
 179                cpm_vecs[vec].handler = NULL;
 180                cpm_vecs[vec].arg = NULL;
 181        } else {
 182                /* SIU interrupt */
 183                clrbits_be32(&immr->im_siu_conf.sc_simask, 1 << (31 - vec));
 184                irq_vecs[vec].handler = NULL;
 185                irq_vecs[vec].arg = NULL;
 186        }
 187}
 188
 189/************************************************************************/
 190
 191static void cpm_interrupt_init(void)
 192{
 193        immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
 194        uint cicr;
 195
 196        /*
 197         * Initialize the CPM interrupt controller.
 198         */
 199
 200        cicr = CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1 |
 201               ((CPM_INTERRUPT / 2) << 13) | CICR_HP_MASK;
 202
 203        out_be32(&immr->im_cpic.cpic_cicr, cicr);
 204        out_be32(&immr->im_cpic.cpic_cimr, 0);
 205
 206        /*
 207         * Install the error handler.
 208         */
 209        irq_install_handler(CPMVEC_ERROR, cpm_error_interrupt, NULL);
 210
 211        setbits_be32(&immr->im_cpic.cpic_cicr, CICR_IEN);
 212
 213        /*
 214         * Install the cpm interrupt handler
 215         */
 216        irq_install_handler(CPM_INTERRUPT, cpm_interrupt, NULL);
 217}
 218
 219/************************************************************************/
 220
 221/*
 222 * timer_interrupt - gets called when the decrementer overflows,
 223 * with interrupts disabled.
 224 * Trivial implementation - no need to be really accurate.
 225 */
 226void timer_interrupt_cpu(struct pt_regs *regs)
 227{
 228        immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
 229
 230        /* Reset Timer Expired and Timers Interrupt Status */
 231        out_be32(&immr->im_clkrstk.cark_plprcrk, KAPWR_KEY);
 232        __asm__ ("nop");
 233        /*
 234          Clear TEXPS (and TMIST on older chips). SPLSS (on older
 235          chips) is cleared too.
 236
 237          Bitwise OR is a read-modify-write operation so ALL bits
 238          which are cleared by writing `1' would be cleared by
 239          operations like
 240
 241          immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS;
 242
 243          The same can be achieved by simple writing of the PLPRCR
 244          to itself. If a bit value should be preserved, read the
 245          register, ZERO the bit and write, not OR, the result back.
 246        */
 247        setbits_be32(&immr->im_clkrst.car_plprcr, 0);
 248}
 249
 250/************************************************************************/
 251