linux/arch/mips/sgi-ip22/ip22-int.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * ip22-int.c: Routines for generic manipulation of the INT[23] ASIC
   4 *             found on INDY and Indigo2 workstations.
   5 *
   6 * Copyright (C) 1996 David S. Miller (davem@davemloft.net)
   7 * Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org)
   8 * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu)
   9 *                    - Indigo2 changes
  10 *                    - Interrupt handling fixes
  11 * Copyright (C) 2001, 2003 Ladislav Michl (ladis@linux-mips.org)
  12 */
  13#include <linux/types.h>
  14#include <linux/init.h>
  15#include <linux/kernel_stat.h>
  16#include <linux/interrupt.h>
  17#include <linux/ftrace.h>
  18
  19#include <asm/irq_cpu.h>
  20#include <asm/sgi/hpc3.h>
  21#include <asm/sgi/ip22.h>
  22
  23/* So far nothing hangs here */
  24#undef USE_LIO3_IRQ
  25
  26struct sgint_regs *sgint;
  27
  28static char lc0msk_to_irqnr[256];
  29static char lc1msk_to_irqnr[256];
  30static char lc2msk_to_irqnr[256];
  31static char lc3msk_to_irqnr[256];
  32
  33extern int ip22_eisa_init(void);
  34
  35static void enable_local0_irq(struct irq_data *d)
  36{
  37        /* don't allow mappable interrupt to be enabled from setup_irq,
  38         * we have our own way to do so */
  39        if (d->irq != SGI_MAP_0_IRQ)
  40                sgint->imask0 |= (1 << (d->irq - SGINT_LOCAL0));
  41}
  42
  43static void disable_local0_irq(struct irq_data *d)
  44{
  45        sgint->imask0 &= ~(1 << (d->irq - SGINT_LOCAL0));
  46}
  47
  48static struct irq_chip ip22_local0_irq_type = {
  49        .name           = "IP22 local 0",
  50        .irq_mask       = disable_local0_irq,
  51        .irq_unmask     = enable_local0_irq,
  52};
  53
  54static void enable_local1_irq(struct irq_data *d)
  55{
  56        /* don't allow mappable interrupt to be enabled from setup_irq,
  57         * we have our own way to do so */
  58        if (d->irq != SGI_MAP_1_IRQ)
  59                sgint->imask1 |= (1 << (d->irq - SGINT_LOCAL1));
  60}
  61
  62static void disable_local1_irq(struct irq_data *d)
  63{
  64        sgint->imask1 &= ~(1 << (d->irq - SGINT_LOCAL1));
  65}
  66
  67static struct irq_chip ip22_local1_irq_type = {
  68        .name           = "IP22 local 1",
  69        .irq_mask       = disable_local1_irq,
  70        .irq_unmask     = enable_local1_irq,
  71};
  72
  73static void enable_local2_irq(struct irq_data *d)
  74{
  75        sgint->imask0 |= (1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0));
  76        sgint->cmeimask0 |= (1 << (d->irq - SGINT_LOCAL2));
  77}
  78
  79static void disable_local2_irq(struct irq_data *d)
  80{
  81        sgint->cmeimask0 &= ~(1 << (d->irq - SGINT_LOCAL2));
  82        if (!sgint->cmeimask0)
  83                sgint->imask0 &= ~(1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0));
  84}
  85
  86static struct irq_chip ip22_local2_irq_type = {
  87        .name           = "IP22 local 2",
  88        .irq_mask       = disable_local2_irq,
  89        .irq_unmask     = enable_local2_irq,
  90};
  91
  92static void enable_local3_irq(struct irq_data *d)
  93{
  94        sgint->imask1 |= (1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1));
  95        sgint->cmeimask1 |= (1 << (d->irq - SGINT_LOCAL3));
  96}
  97
  98static void disable_local3_irq(struct irq_data *d)
  99{
 100        sgint->cmeimask1 &= ~(1 << (d->irq - SGINT_LOCAL3));
 101        if (!sgint->cmeimask1)
 102                sgint->imask1 &= ~(1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1));
 103}
 104
 105static struct irq_chip ip22_local3_irq_type = {
 106        .name           = "IP22 local 3",
 107        .irq_mask       = disable_local3_irq,
 108        .irq_unmask     = enable_local3_irq,
 109};
 110
 111static void indy_local0_irqdispatch(void)
 112{
 113        u8 mask = sgint->istat0 & sgint->imask0;
 114        u8 mask2;
 115        int irq;
 116
 117        if (mask & SGINT_ISTAT0_LIO2) {
 118                mask2 = sgint->vmeistat & sgint->cmeimask0;
 119                irq = lc2msk_to_irqnr[mask2];
 120        } else
 121                irq = lc0msk_to_irqnr[mask];
 122
 123        /*
 124         * workaround for INT2 bug; if irq == 0, INT2 has seen a fifo full
 125         * irq, but failed to latch it into status register
 126         */
 127        if (irq)
 128                do_IRQ(irq);
 129        else
 130                do_IRQ(SGINT_LOCAL0 + 0);
 131}
 132
 133static void indy_local1_irqdispatch(void)
 134{
 135        u8 mask = sgint->istat1 & sgint->imask1;
 136        u8 mask2;
 137        int irq;
 138
 139        if (mask & SGINT_ISTAT1_LIO3) {
 140                mask2 = sgint->vmeistat & sgint->cmeimask1;
 141                irq = lc3msk_to_irqnr[mask2];
 142        } else
 143                irq = lc1msk_to_irqnr[mask];
 144
 145        /* if irq == 0, then the interrupt has already been cleared */
 146        if (irq)
 147                do_IRQ(irq);
 148}
 149
 150extern void ip22_be_interrupt(int irq);
 151
 152static void __irq_entry indy_buserror_irq(void)
 153{
 154        int irq = SGI_BUSERR_IRQ;
 155
 156        irq_enter();
 157        kstat_incr_irq_this_cpu(irq);
 158        ip22_be_interrupt(irq);
 159        irq_exit();
 160}
 161
 162#ifdef USE_LIO3_IRQ
 163#define SGI_INTERRUPTS  SGINT_END
 164#else
 165#define SGI_INTERRUPTS  SGINT_LOCAL3
 166#endif
 167
 168extern void indy_8254timer_irq(void);
 169
 170/*
 171 * IRQs on the INDY look basically (barring software IRQs which we don't use
 172 * at all) like:
 173 *
 174 *      MIPS IRQ        Source
 175 *      --------        ------
 176 *             0        Software (ignored)
 177 *             1        Software (ignored)
 178 *             2        Local IRQ level zero
 179 *             3        Local IRQ level one
 180 *             4        8254 Timer zero
 181 *             5        8254 Timer one
 182 *             6        Bus Error
 183 *             7        R4k timer (what we use)
 184 *
 185 * We handle the IRQ according to _our_ priority which is:
 186 *
 187 * Highest ----     R4k Timer
 188 *                  Local IRQ zero
 189 *                  Local IRQ one
 190 *                  Bus Error
 191 *                  8254 Timer zero
 192 * Lowest  ----     8254 Timer one
 193 *
 194 * then we just return, if multiple IRQs are pending then we will just take
 195 * another exception, big deal.
 196 */
 197
 198asmlinkage void plat_irq_dispatch(void)
 199{
 200        unsigned int pending = read_c0_status() & read_c0_cause();
 201
 202        /*
 203         * First we check for r4k counter/timer IRQ.
 204         */
 205        if (pending & CAUSEF_IP7)
 206                do_IRQ(SGI_TIMER_IRQ);
 207        else if (pending & CAUSEF_IP2)
 208                indy_local0_irqdispatch();
 209        else if (pending & CAUSEF_IP3)
 210                indy_local1_irqdispatch();
 211        else if (pending & CAUSEF_IP6)
 212                indy_buserror_irq();
 213        else if (pending & (CAUSEF_IP4 | CAUSEF_IP5))
 214                indy_8254timer_irq();
 215}
 216
 217void __init arch_init_irq(void)
 218{
 219        int i;
 220
 221        /* Init local mask --> irq tables. */
 222        for (i = 0; i < 256; i++) {
 223                if (i & 0x80) {
 224                        lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 7;
 225                        lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 7;
 226                        lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 7;
 227                        lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 7;
 228                } else if (i & 0x40) {
 229                        lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 6;
 230                        lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 6;
 231                        lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 6;
 232                        lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 6;
 233                } else if (i & 0x20) {
 234                        lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 5;
 235                        lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 5;
 236                        lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 5;
 237                        lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 5;
 238                } else if (i & 0x10) {
 239                        lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 4;
 240                        lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 4;
 241                        lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 4;
 242                        lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 4;
 243                } else if (i & 0x08) {
 244                        lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 3;
 245                        lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 3;
 246                        lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 3;
 247                        lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 3;
 248                } else if (i & 0x04) {
 249                        lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 2;
 250                        lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 2;
 251                        lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 2;
 252                        lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 2;
 253                } else if (i & 0x02) {
 254                        lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 1;
 255                        lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 1;
 256                        lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 1;
 257                        lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 1;
 258                } else if (i & 0x01) {
 259                        lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 0;
 260                        lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 0;
 261                        lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 0;
 262                        lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 0;
 263                } else {
 264                        lc0msk_to_irqnr[i] = 0;
 265                        lc1msk_to_irqnr[i] = 0;
 266                        lc2msk_to_irqnr[i] = 0;
 267                        lc3msk_to_irqnr[i] = 0;
 268                }
 269        }
 270
 271        /* Mask out all interrupts. */
 272        sgint->imask0 = 0;
 273        sgint->imask1 = 0;
 274        sgint->cmeimask0 = 0;
 275        sgint->cmeimask1 = 0;
 276
 277        /* init CPU irqs */
 278        mips_cpu_irq_init();
 279
 280        for (i = SGINT_LOCAL0; i < SGI_INTERRUPTS; i++) {
 281                struct irq_chip *handler;
 282
 283                if (i < SGINT_LOCAL1)
 284                        handler         = &ip22_local0_irq_type;
 285                else if (i < SGINT_LOCAL2)
 286                        handler         = &ip22_local1_irq_type;
 287                else if (i < SGINT_LOCAL3)
 288                        handler         = &ip22_local2_irq_type;
 289                else
 290                        handler         = &ip22_local3_irq_type;
 291
 292                irq_set_chip_and_handler(i, handler, handle_level_irq);
 293        }
 294
 295        /* vector handler. this register the IRQ as non-sharable */
 296        if (request_irq(SGI_LOCAL_0_IRQ, no_action, IRQF_NO_THREAD,
 297                        "local0 cascade", NULL))
 298                pr_err("Failed to register local0 cascade interrupt\n");
 299        if (request_irq(SGI_LOCAL_1_IRQ, no_action, IRQF_NO_THREAD,
 300                        "local1 cascade", NULL))
 301                pr_err("Failed to register local1 cascade interrupt\n");
 302        if (request_irq(SGI_BUSERR_IRQ, no_action, IRQF_NO_THREAD,
 303                        "Bus Error", NULL))
 304                pr_err("Failed to register Bus Error interrupt\n");
 305
 306        /* cascade in cascade. i love Indy ;-) */
 307        if (request_irq(SGI_MAP_0_IRQ, no_action, IRQF_NO_THREAD,
 308                        "mapable0 cascade", NULL))
 309                pr_err("Failed to register mapable0 cascade interrupt\n");
 310#ifdef USE_LIO3_IRQ
 311        if (request_irq(SGI_MAP_1_IRQ, no_action, IRQF_NO_THREAD,
 312                        "mapable1 cascade", NULL))
 313                pr_err("Failed to register mapable1 cascade interrupt\n");
 314#endif
 315
 316#ifdef CONFIG_EISA
 317        if (ip22_is_fullhouse())        /* Only Indigo-2 has EISA stuff */
 318                ip22_eisa_init();
 319#endif
 320}
 321