uboot/cpu/mpc8xx/interrupts.c
<<
>>
Prefs
   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 */
  35struct interrupt_action {
  36        interrupt_handler_t *handler;
  37        void *arg;
  38};
  39
  40static struct interrupt_action cpm_vecs[CPMVEC_NR];
  41static struct interrupt_action irq_vecs[NR_IRQS];
  42
  43static void cpm_interrupt_init (void);
  44static void cpm_interrupt (void *regs);
  45
  46/************************************************************************/
  47
  48int 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 */
  68void 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 */
 125static 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 */
 157static void cpm_error_interrupt (void *dummy)
 158{
 159}
 160
 161/************************************************************************/
 162/*
 163 * Install and free an interrupt handler
 164 */
 165void 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
 203void 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
 231static 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 */
 267void 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