linux-old/arch/mips/sgi-ip27/ip27-irq.c
<<
>>
Prefs
   1/*
   2 * ip27-irq.c: Highlevel interrupt handling for IP27 architecture.
   3 *
   4 * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org)
   5 * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
   6 * Copyright (C) 1999 - 2001 Kanoj Sarcar
   7 */
   8#include <linux/config.h>
   9#include <linux/init.h>
  10#include <linux/irq.h>
  11#include <linux/errno.h>
  12#include <linux/signal.h>
  13#include <linux/sched.h>
  14#include <linux/types.h>
  15#include <linux/interrupt.h>
  16#include <linux/ioport.h>
  17#include <linux/timex.h>
  18#include <linux/slab.h>
  19#include <linux/random.h>
  20#include <linux/smp_lock.h>
  21#include <linux/kernel_stat.h>
  22#include <linux/delay.h>
  23
  24#include <asm/bitops.h>
  25#include <asm/bootinfo.h>
  26#include <asm/io.h>
  27#include <asm/mipsregs.h>
  28#include <asm/system.h>
  29
  30#include <asm/ptrace.h>
  31#include <asm/processor.h>
  32#include <asm/pci/bridge.h>
  33#include <asm/sn/sn0/hub.h>
  34#include <asm/sn/sn0/ip27.h>
  35#include <asm/sn/addrs.h>
  36#include <asm/sn/agent.h>
  37#include <asm/sn/arch.h>
  38#include <asm/sn/intr.h>
  39#include <asm/sn/intr_public.h>
  40
  41
  42#undef DEBUG_IRQ
  43#ifdef DEBUG_IRQ
  44#define DBG(x...) printk(x)
  45#else
  46#define DBG(x...)
  47#endif
  48
  49/* These should die */
  50unsigned char bus_to_wid[256];  /* widget id for linux pci bus */
  51unsigned char bus_to_nid[256];  /* nasid for linux pci bus */
  52unsigned char num_bridges;      /* number of bridges in the system */
  53
  54/*
  55 * Linux has a controller-independent x86 interrupt architecture.
  56 * every controller has a 'controller-template', that is used
  57 * by the main code to do the right thing. Each driver-visible
  58 * interrupt source is transparently wired to the apropriate
  59 * controller. Thus drivers need not be aware of the
  60 * interrupt-controller.
  61 *
  62 * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC,
  63 * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC.
  64 * (IO-APICs assumed to be messaging to Pentium local-APICs)
  65 *
  66 * the code is designed to be easily extended with new/different
  67 * interrupt controllers, without having to do assembly magic.
  68 */
  69
  70extern asmlinkage void ip27_irq(void);
  71
  72extern int irq_to_bus[], irq_to_slot[], bus_to_cpu[];
  73int intr_connect_level(int cpu, int bit);
  74int intr_disconnect_level(int cpu, int bit);
  75
  76/*
  77 * There is a single intpend register per node, and we want to have
  78 * distinct levels for intercpu intrs for both cpus A and B on a node.
  79 */
  80int node_level_to_irq[MAX_COMPACT_NODES][PERNODE_LEVELS];
  81
  82/*
  83 * use these macros to get the encoded nasid and widget id
  84 * from the irq value
  85 */
  86#define IRQ_TO_BUS(i)                   irq_to_bus[(i)]
  87#define IRQ_TO_CPU(i)                   bus_to_cpu[IRQ_TO_BUS(i)]
  88#define NASID_FROM_PCI_IRQ(i)           bus_to_nid[IRQ_TO_BUS(i)]
  89#define WID_FROM_PCI_IRQ(i)             bus_to_wid[IRQ_TO_BUS(i)]
  90#define SLOT_FROM_PCI_IRQ(i)            irq_to_slot[i]
  91
  92static inline int alloc_level(cpuid_t cpunum, int irq)
  93{
  94        cnodeid_t nodenum = CPUID_TO_COMPACT_NODEID(cpunum);
  95        int j = LEAST_LEVEL + 3;        /* resched & crosscall entries taken */
  96
  97        while (++j < PERNODE_LEVELS) {
  98                if (node_level_to_irq[nodenum][j] == -1) {
  99                        node_level_to_irq[nodenum][j] = irq;
 100                        return j;
 101                }
 102        }
 103        printk("Cpu %ld flooded with devices\n", cpunum);
 104        while(1);
 105        return -1;
 106}
 107
 108static inline int find_level(cpuid_t *cpunum, int irq)
 109{
 110        int j;
 111        cnodeid_t nodenum = INVALID_CNODEID;
 112
 113        while (++nodenum < MAX_COMPACT_NODES) {
 114                j = LEAST_LEVEL + 3;    /* resched & crosscall entries taken */
 115                while (++j < PERNODE_LEVELS)
 116                        if (node_level_to_irq[nodenum][j] == irq) {
 117                                *cpunum = 0;    /* XXX Fixme */
 118                                return(j);
 119                        }
 120        }
 121        printk("Could not identify cpu/level for irq %d\n", irq);
 122        while(1);
 123        return(-1);
 124}
 125
 126/*
 127 * Find first bit set
 128 */
 129static int ms1bit(unsigned long x)
 130{
 131        int b = 0, s;
 132
 133        s = 16; if (x >> 16 == 0) s = 0; b += s; x >>= s;
 134        s =  8; if (x >>  8 == 0) s = 0; b += s; x >>= s;
 135        s =  4; if (x >>  4 == 0) s = 0; b += s; x >>= s;
 136        s =  2; if (x >>  2 == 0) s = 0; b += s; x >>= s;
 137        s =  1; if (x >>  1 == 0) s = 0; b += s;
 138
 139        return b;
 140}
 141
 142/*
 143 * This code is unnecessarily complex, because we do SA_INTERRUPT
 144 * intr enabling. Basically, once we grab the set of intrs we need
 145 * to service, we must mask _all_ these interrupts; firstly, to make
 146 * sure the same intr does not intr again, causing recursion that
 147 * can lead to stack overflow. Secondly, we can not just mask the
 148 * one intr we are do_IRQing, because the non-masked intrs in the
 149 * first set might intr again, causing multiple servicings of the
 150 * same intr. This effect is mostly seen for intercpu intrs.
 151 * Kanoj 05.13.00
 152 */
 153void ip27_do_irq(struct pt_regs *regs)
 154{
 155        int irq, swlevel;
 156        hubreg_t pend0, mask0;
 157        cpuid_t thiscpu = smp_processor_id();
 158        int pi_int_mask0 = ((cputoslice(thiscpu) == 0) ?
 159                                        PI_INT_MASK0_A : PI_INT_MASK0_B);
 160
 161        /* copied from Irix intpend0() */
 162        while (((pend0 = LOCAL_HUB_L(PI_INT_PEND0)) &
 163                                (mask0 = LOCAL_HUB_L(pi_int_mask0))) != 0) {
 164                pend0 &= mask0;         /* Pick intrs we should look at */
 165                if (pend0) {
 166                        /* Prevent any of the picked intrs from recursing */
 167                        LOCAL_HUB_S(pi_int_mask0, mask0 & ~(pend0));
 168                        do {
 169                                swlevel = ms1bit(pend0);
 170                                LOCAL_HUB_CLR_INTR(swlevel);
 171                                /* "map" swlevel to irq */
 172                                irq = LEVEL_TO_IRQ(thiscpu, swlevel);
 173                                do_IRQ(irq, regs);
 174                                /* clear bit in pend0 */
 175                                pend0 ^= 1ULL << swlevel;
 176                        } while(pend0);
 177                        /* Now allow the set of serviced intrs again */
 178                        LOCAL_HUB_S(pi_int_mask0, mask0);
 179                        LOCAL_HUB_L(PI_INT_PEND0);
 180                }
 181        }
 182}
 183
 184
 185/* Startup one of the (PCI ...) IRQs routes over a bridge.  */
 186static unsigned int startup_bridge_irq(unsigned int irq)
 187{
 188        bridgereg_t device;
 189        bridge_t *bridge;
 190        int pin, swlevel;
 191        cpuid_t cpu;
 192        nasid_t master = NASID_FROM_PCI_IRQ(irq);
 193
 194        if (irq < BASE_PCI_IRQ)
 195                return 0;
 196
 197        bridge = (bridge_t *) NODE_SWIN_BASE(master, WID_FROM_PCI_IRQ(irq));
 198        pin = SLOT_FROM_PCI_IRQ(irq);
 199        cpu = IRQ_TO_CPU(irq);
 200
 201        DBG("bridge_startup(): irq= 0x%x  pin=%d\n", irq, pin);
 202        /*
 203         * "map" irq to a swlevel greater than 6 since the first 6 bits
 204         * of INT_PEND0 are taken
 205         */
 206        swlevel = alloc_level(cpu, irq);
 207        intr_connect_level(cpu, swlevel);
 208
 209        bridge->b_int_addr[pin].addr = (0x20000 | swlevel | (master << 8));
 210        bridge->b_int_enable |= (1 << pin);
 211        /* more stuff in int_enable reg */
 212        bridge->b_int_enable |= 0x7ffffe00;
 213
 214        /*
 215         * XXX This only works if b_int_device is initialized to 0!
 216         * We program the bridge to have a 1:1 mapping between devices
 217         * (slots) and intr pins.
 218         */
 219        device = bridge->b_int_device;
 220        device |= (pin << (pin*3));
 221        bridge->b_int_device = device;
 222
 223        bridge->b_widget.w_tflush;                      /* Flush */
 224
 225        return 0;       /* Never anything pending.  */
 226}
 227
 228/* Shutdown one of the (PCI ...) IRQs routes over a bridge.  */
 229static unsigned int shutdown_bridge_irq(unsigned int irq)
 230{
 231        bridge_t *bridge;
 232        int pin, swlevel;
 233        cpuid_t cpu;
 234
 235        if (irq < BASE_PCI_IRQ)
 236                return 0;
 237
 238        bridge = (bridge_t *) NODE_SWIN_BASE(NASID_FROM_PCI_IRQ(irq),
 239                                             WID_FROM_PCI_IRQ(irq));
 240        DBG("bridge_shutdown: irq 0x%x\n", irq);
 241        pin = SLOT_FROM_PCI_IRQ(irq);
 242
 243        /*
 244         * map irq to a swlevel greater than 6 since the first 6 bits
 245         * of INT_PEND0 are taken
 246         */
 247        swlevel = find_level(&cpu, irq);
 248        intr_disconnect_level(cpu, swlevel);
 249        LEVEL_TO_IRQ(cpu, swlevel) = -1;
 250
 251        bridge->b_int_enable &= ~(1 << pin);
 252        bridge->b_widget.w_tflush;                      /* Flush */
 253
 254        return 0;       /* Never anything pending.  */
 255}
 256
 257static inline void enable_bridge_irq(unsigned int irq)
 258{
 259        /* All the braindamage happens magically for us in ip27_do_irq */
 260}
 261
 262static void disable_bridge_irq(unsigned int irq)
 263{
 264        /* All the braindamage happens magically for us in ip27_do_irq */
 265}
 266
 267static void mask_and_ack_bridge_irq(unsigned int irq)
 268{
 269        /* All the braindamage happens magically for us in ip27_do_irq */
 270}
 271
 272static void end_bridge_irq (unsigned int irq)
 273{
 274        if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
 275                enable_bridge_irq(irq);
 276}
 277
 278static struct hw_interrupt_type bridge_irq_type = {
 279        "bridge",
 280        startup_bridge_irq,
 281        shutdown_bridge_irq,
 282        enable_bridge_irq,
 283        disable_bridge_irq,
 284        mask_and_ack_bridge_irq,
 285        end_bridge_irq
 286};
 287
 288void irq_debug(void)
 289{
 290        bridge_t *bridge = (bridge_t *) 0x9200000008000000;
 291
 292        printk("bridge->b_int_status = 0x%x\n", bridge->b_int_status);
 293        printk("bridge->b_int_enable = 0x%x\n", bridge->b_int_enable);
 294        printk("PI_INT_PEND0   = 0x%lx\n", LOCAL_HUB_L(PI_INT_PEND0));
 295        printk("PI_INT_MASK0_A = 0x%lx\n", LOCAL_HUB_L(PI_INT_MASK0_A));
 296}
 297
 298void __init init_IRQ(void)
 299{
 300        int i;
 301
 302        set_except_vector(0, ip27_irq);
 303
 304        /*
 305         * Right now the bridge irq is our kitchen sink interrupt type
 306         */
 307        for (i = 0; i <= NR_IRQS; i++) {
 308                irq_desc[i].status      = IRQ_DISABLED;
 309                irq_desc[i].action      = 0;
 310                irq_desc[i].depth       = 1;
 311                irq_desc[i].handler     = &bridge_irq_type;
 312        }
 313}
 314
 315/*
 316 * Get values that vary depending on which CPU and bit we're operating on.
 317 */
 318static hub_intmasks_t *intr_get_ptrs(cpuid_t cpu, int bit, int *new_bit,
 319                                hubreg_t **intpend_masks, int *ip)
 320{
 321        hub_intmasks_t *hub_intmasks;
 322
 323        hub_intmasks = &cpu_data[cpu].p_intmasks;
 324        if (bit < N_INTPEND_BITS) {
 325                *intpend_masks = hub_intmasks->intpend0_masks;
 326                *ip = 0;
 327                *new_bit = bit;
 328        } else {
 329                *intpend_masks = hub_intmasks->intpend1_masks;
 330                *ip = 1;
 331                *new_bit = bit - N_INTPEND_BITS;
 332        }
 333        return hub_intmasks;
 334}
 335
 336int intr_connect_level(int cpu, int bit)
 337{
 338        int ip;
 339        int slice = cputoslice(cpu);
 340        volatile hubreg_t *mask_reg;
 341        hubreg_t *intpend_masks;
 342        nasid_t nasid = COMPACT_TO_NASID_NODEID(cputocnode(cpu));
 343
 344        (void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks, &ip);
 345
 346        /* Make sure it's not already pending when we connect it. */
 347        REMOTE_HUB_CLR_INTR(nasid, bit + ip * N_INTPEND_BITS);
 348
 349        intpend_masks[0] |= (1ULL << (u64)bit);
 350
 351        if (ip == 0) {
 352                mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK0_A +
 353                                PI_INT_MASK_OFFSET * slice);
 354        } else {
 355                mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK1_A +
 356                                PI_INT_MASK_OFFSET * slice);
 357        }
 358        HUB_S(mask_reg, intpend_masks[0]);
 359        return(0);
 360}
 361
 362int intr_disconnect_level(int cpu, int bit)
 363{
 364        int ip;
 365        int slice = cputoslice(cpu);
 366        volatile hubreg_t *mask_reg;
 367        hubreg_t *intpend_masks;
 368        nasid_t nasid = COMPACT_TO_NASID_NODEID(cputocnode(cpu));
 369
 370        (void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks, &ip);
 371        intpend_masks[0] &= ~(1ULL << (u64)bit);
 372        if (ip == 0) {
 373                mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK0_A +
 374                                PI_INT_MASK_OFFSET * slice);
 375        } else {
 376                mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK1_A +
 377                                PI_INT_MASK_OFFSET * slice);
 378        }
 379        HUB_S(mask_reg, intpend_masks[0]);
 380        return(0);
 381}
 382
 383
 384void handle_resched_intr(int irq, void *dev_id, struct pt_regs *regs)
 385{
 386        /* Nothing, the return from intr will work for us */
 387}
 388
 389#ifdef CONFIG_SMP
 390
 391void core_send_ipi(int destid, unsigned int action)
 392{
 393        int irq;
 394
 395#if (CPUS_PER_NODE == 2)
 396        switch (action) {
 397                case SMP_RESCHEDULE_YOURSELF:
 398                        irq = CPU_RESCHED_A_IRQ;
 399                        break;
 400                case SMP_CALL_FUNCTION:
 401                        irq = CPU_CALL_A_IRQ;
 402                        break;
 403                default:
 404                        panic("sendintr");
 405        }
 406        irq += cputoslice(destid);
 407
 408        /*
 409         * Convert the compact hub number to the NASID to get the correct
 410         * part of the address space.  Then set the interrupt bit associated
 411         * with the CPU we want to send the interrupt to.
 412         */
 413        REMOTE_HUB_SEND_INTR(COMPACT_TO_NASID_NODEID(cputocnode(destid)),
 414                        FAST_IRQ_TO_LEVEL(irq));
 415#else
 416        << Bomb!  Must redefine this for more than 2 CPUS. >>
 417#endif
 418}
 419
 420#endif
 421
 422extern void smp_call_function_interrupt(void);
 423
 424void install_cpuintr(int cpu)
 425{
 426#ifdef CONFIG_SMP
 427#if (CPUS_PER_NODE == 2)
 428        static int done = 0;
 429
 430        /*
 431         * This is a hack till we have a pernode irqlist. Currently,
 432         * just have the master cpu set up the handlers for the per
 433         * cpu irqs.
 434         */
 435        if (done == 0) {
 436                int j;
 437
 438                if (request_irq(CPU_RESCHED_A_IRQ, handle_resched_intr,
 439                                                        0, "resched", 0))
 440                        panic("intercpu intr unconnectible");
 441                if (request_irq(CPU_RESCHED_B_IRQ, handle_resched_intr,
 442                                                        0, "resched", 0))
 443                        panic("intercpu intr unconnectible");
 444                if (request_irq(CPU_CALL_A_IRQ, smp_call_function_interrupt,
 445                                                        0, "callfunc", 0))
 446                        panic("intercpu intr unconnectible");
 447                if (request_irq(CPU_CALL_B_IRQ, smp_call_function_interrupt,
 448                                                        0, "callfunc", 0))
 449                        panic("intercpu intr unconnectible");
 450
 451                for (j = 0; j < PERNODE_LEVELS; j++)
 452                        LEVEL_TO_IRQ(0, j) = -1;
 453                LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_RESCHED_A_IRQ)) =
 454                                                        CPU_RESCHED_A_IRQ;
 455                LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_RESCHED_B_IRQ)) =
 456                                                        CPU_RESCHED_B_IRQ;
 457                LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_CALL_A_IRQ)) =
 458                                                        CPU_CALL_A_IRQ;
 459                LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_CALL_B_IRQ)) =
 460                                                        CPU_CALL_B_IRQ;
 461                for (j = 1; j < MAX_COMPACT_NODES; j++)
 462                        memcpy(&node_level_to_irq[j][0],
 463                        &node_level_to_irq[0][0],
 464                        sizeof(node_level_to_irq[0][0])*PERNODE_LEVELS);
 465
 466                done = 1;
 467        }
 468
 469        intr_connect_level(cpu, FAST_IRQ_TO_LEVEL(CPU_RESCHED_A_IRQ +
 470                                                        cputoslice(cpu)));
 471        intr_connect_level(cpu, FAST_IRQ_TO_LEVEL(CPU_CALL_A_IRQ +
 472                                                        cputoslice(cpu)));
 473#else /* CPUS_PER_NODE */
 474#error Must redefine this for more than 2 CPUS.
 475#endif /* CPUS_PER_NODE */
 476#endif /* CONFIG_SMP */
 477}
 478
 479void install_tlbintr(int cpu)
 480{
 481#if 0
 482        int intr_bit = N_INTPEND_BITS + TLB_INTR_A + cputoslice(cpu);
 483
 484        intr_connect_level(cpu, intr_bit);
 485#endif
 486}
 487
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.