linux/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
   9#undef DEBUG
  10
  11#include <linux/init.h>
  12#include <linux/irq.h>
  13#include <linux/errno.h>
  14#include <linux/signal.h>
  15#include <linux/sched.h>
  16#include <linux/types.h>
  17#include <linux/interrupt.h>
  18#include <linux/ioport.h>
  19#include <linux/timex.h>
  20#include <linux/slab.h>
  21#include <linux/smp.h>
  22#include <linux/random.h>
  23#include <linux/kernel.h>
  24#include <linux/kernel_stat.h>
  25#include <linux/delay.h>
  26#include <linux/bitops.h>
  27
  28#include <asm/bootinfo.h>
  29#include <asm/io.h>
  30#include <asm/mipsregs.h>
  31#include <asm/system.h>
  32
  33#include <asm/processor.h>
  34#include <asm/pci/bridge.h>
  35#include <asm/sn/addrs.h>
  36#include <asm/sn/agent.h>
  37#include <asm/sn/arch.h>
  38#include <asm/sn/hub.h>
  39#include <asm/sn/intr.h>
  40
  41/*
  42 * Linux has a controller-independent x86 interrupt architecture.
  43 * every controller has a 'controller-template', that is used
  44 * by the main code to do the right thing. Each driver-visible
  45 * interrupt source is transparently wired to the apropriate
  46 * controller. Thus drivers need not be aware of the
  47 * interrupt-controller.
  48 *
  49 * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC,
  50 * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC.
  51 * (IO-APICs assumed to be messaging to Pentium local-APICs)
  52 *
  53 * the code is designed to be easily extended with new/different
  54 * interrupt controllers, without having to do assembly magic.
  55 */
  56
  57extern asmlinkage void ip27_irq(void);
  58
  59extern struct bridge_controller *irq_to_bridge[];
  60extern int irq_to_slot[];
  61
  62/*
  63 * use these macros to get the encoded nasid and widget id
  64 * from the irq value
  65 */
  66#define IRQ_TO_BRIDGE(i)                irq_to_bridge[(i)]
  67#define SLOT_FROM_PCI_IRQ(i)            irq_to_slot[i]
  68
  69static inline int alloc_level(int cpu, int irq)
  70{
  71        struct hub_data *hub = hub_data(cpu_to_node(cpu));
  72        struct slice_data *si = cpu_data[cpu].data;
  73        int level;
  74
  75        level = find_first_zero_bit(hub->irq_alloc_mask, LEVELS_PER_SLICE);
  76        if (level >= LEVELS_PER_SLICE)
  77                panic("Cpu %d flooded with devices\n", cpu);
  78
  79        __set_bit(level, hub->irq_alloc_mask);
  80        si->level_to_irq[level] = irq;
  81
  82        return level;
  83}
  84
  85static inline int find_level(cpuid_t *cpunum, int irq)
  86{
  87        int cpu, i;
  88
  89        for_each_online_cpu(cpu) {
  90                struct slice_data *si = cpu_data[cpu].data;
  91
  92                for (i = BASE_PCI_IRQ; i < LEVELS_PER_SLICE; i++)
  93                        if (si->level_to_irq[i] == irq) {
  94                                *cpunum = cpu;
  95
  96                                return i;
  97                        }
  98        }
  99
 100        panic("Could not identify cpu/level for irq %d\n", irq);
 101}
 102
 103/*
 104 * Find first bit set
 105 */
 106static int ms1bit(unsigned long x)
 107{
 108        int b = 0, s;
 109
 110        s = 16; if (x >> 16 == 0) s = 0; b += s; x >>= s;
 111        s =  8; if (x >>  8 == 0) s = 0; b += s; x >>= s;
 112        s =  4; if (x >>  4 == 0) s = 0; b += s; x >>= s;
 113        s =  2; if (x >>  2 == 0) s = 0; b += s; x >>= s;
 114        s =  1; if (x >>  1 == 0) s = 0; b += s;
 115
 116        return b;
 117}
 118
 119/*
 120 * This code is unnecessarily complex, because we do IRQF_DISABLED
 121 * intr enabling. Basically, once we grab the set of intrs we need
 122 * to service, we must mask _all_ these interrupts; firstly, to make
 123 * sure the same intr does not intr again, causing recursion that
 124 * can lead to stack overflow. Secondly, we can not just mask the
 125 * one intr we are do_IRQing, because the non-masked intrs in the
 126 * first set might intr again, causing multiple servicings of the
 127 * same intr. This effect is mostly seen for intercpu intrs.
 128 * Kanoj 05.13.00
 129 */
 130
 131static void ip27_do_irq_mask0(void)
 132{
 133        int irq, swlevel;
 134        hubreg_t pend0, mask0;
 135        cpuid_t cpu = smp_processor_id();
 136        int pi_int_mask0 =
 137                (cputoslice(cpu) == 0) ?  PI_INT_MASK0_A : PI_INT_MASK0_B;
 138
 139        /* copied from Irix intpend0() */
 140        pend0 = LOCAL_HUB_L(PI_INT_PEND0);
 141        mask0 = LOCAL_HUB_L(pi_int_mask0);
 142
 143        pend0 &= mask0;         /* Pick intrs we should look at */
 144        if (!pend0)
 145                return;
 146
 147        swlevel = ms1bit(pend0);
 148#ifdef CONFIG_SMP
 149        if (pend0 & (1UL << CPU_RESCHED_A_IRQ)) {
 150                LOCAL_HUB_CLR_INTR(CPU_RESCHED_A_IRQ);
 151        } else if (pend0 & (1UL << CPU_RESCHED_B_IRQ)) {
 152                LOCAL_HUB_CLR_INTR(CPU_RESCHED_B_IRQ);
 153        } else if (pend0 & (1UL << CPU_CALL_A_IRQ)) {
 154                LOCAL_HUB_CLR_INTR(CPU_CALL_A_IRQ);
 155                smp_call_function_interrupt();
 156        } else if (pend0 & (1UL << CPU_CALL_B_IRQ)) {
 157                LOCAL_HUB_CLR_INTR(CPU_CALL_B_IRQ);
 158                smp_call_function_interrupt();
 159        } else
 160#endif
 161        {
 162                /* "map" swlevel to irq */
 163                struct slice_data *si = cpu_data[cpu].data;
 164
 165                irq = si->level_to_irq[swlevel];
 166                do_IRQ(irq);
 167        }
 168
 169        LOCAL_HUB_L(PI_INT_PEND0);
 170}
 171
 172static void ip27_do_irq_mask1(void)
 173{
 174        int irq, swlevel;
 175        hubreg_t pend1, mask1;
 176        cpuid_t cpu = smp_processor_id();
 177        int pi_int_mask1 = (cputoslice(cpu) == 0) ?  PI_INT_MASK1_A : PI_INT_MASK1_B;
 178        struct slice_data *si = cpu_data[cpu].data;
 179
 180        /* copied from Irix intpend0() */
 181        pend1 = LOCAL_HUB_L(PI_INT_PEND1);
 182        mask1 = LOCAL_HUB_L(pi_int_mask1);
 183
 184        pend1 &= mask1;         /* Pick intrs we should look at */
 185        if (!pend1)
 186                return;
 187
 188        swlevel = ms1bit(pend1);
 189        /* "map" swlevel to irq */
 190        irq = si->level_to_irq[swlevel];
 191        LOCAL_HUB_CLR_INTR(swlevel);
 192        do_IRQ(irq);
 193
 194        LOCAL_HUB_L(PI_INT_PEND1);
 195}
 196
 197static void ip27_prof_timer(void)
 198{
 199        panic("CPU %d got a profiling interrupt", smp_processor_id());
 200}
 201
 202static void ip27_hub_error(void)
 203{
 204        panic("CPU %d got a hub error interrupt", smp_processor_id());
 205}
 206
 207static int intr_connect_level(int cpu, int bit)
 208{
 209        nasid_t nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu));
 210        struct slice_data *si = cpu_data[cpu].data;
 211
 212        set_bit(bit, si->irq_enable_mask);
 213
 214        if (!cputoslice(cpu)) {
 215                REMOTE_HUB_S(nasid, PI_INT_MASK0_A, si->irq_enable_mask[0]);
 216                REMOTE_HUB_S(nasid, PI_INT_MASK1_A, si->irq_enable_mask[1]);
 217        } else {
 218                REMOTE_HUB_S(nasid, PI_INT_MASK0_B, si->irq_enable_mask[0]);
 219                REMOTE_HUB_S(nasid, PI_INT_MASK1_B, si->irq_enable_mask[1]);
 220        }
 221
 222        return 0;
 223}
 224
 225static int intr_disconnect_level(int cpu, int bit)
 226{
 227        nasid_t nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu));
 228        struct slice_data *si = cpu_data[cpu].data;
 229
 230        clear_bit(bit, si->irq_enable_mask);
 231
 232        if (!cputoslice(cpu)) {
 233                REMOTE_HUB_S(nasid, PI_INT_MASK0_A, si->irq_enable_mask[0]);
 234                REMOTE_HUB_S(nasid, PI_INT_MASK1_A, si->irq_enable_mask[1]);
 235        } else {
 236                REMOTE_HUB_S(nasid, PI_INT_MASK0_B, si->irq_enable_mask[0]);
 237                REMOTE_HUB_S(nasid, PI_INT_MASK1_B, si->irq_enable_mask[1]);
 238        }
 239
 240        return 0;
 241}
 242
 243/* Startup one of the (PCI ...) IRQs routes over a bridge.  */
 244static unsigned int startup_bridge_irq(unsigned int irq)
 245{
 246        struct bridge_controller *bc;
 247        bridgereg_t device;
 248        bridge_t *bridge;
 249        int pin, swlevel;
 250        cpuid_t cpu;
 251
 252        pin = SLOT_FROM_PCI_IRQ(irq);
 253        bc = IRQ_TO_BRIDGE(irq);
 254        bridge = bc->base;
 255
 256        pr_debug("bridge_startup(): irq= 0x%x  pin=%d\n", irq, pin);
 257        /*
 258         * "map" irq to a swlevel greater than 6 since the first 6 bits
 259         * of INT_PEND0 are taken
 260         */
 261        swlevel = find_level(&cpu, irq);
 262        bridge->b_int_addr[pin].addr = (0x20000 | swlevel | (bc->nasid << 8));
 263        bridge->b_int_enable |= (1 << pin);
 264        bridge->b_int_enable |= 0x7ffffe00;     /* more stuff in int_enable */
 265
 266        /*
 267         * Enable sending of an interrupt clear packt to the hub on a high to
 268         * low transition of the interrupt pin.
 269         *
 270         * IRIX sets additional bits in the address which are documented as
 271         * reserved in the bridge docs.
 272         */
 273        bridge->b_int_mode |= (1UL << pin);
 274
 275        /*
 276         * We assume the bridge to have a 1:1 mapping between devices
 277         * (slots) and intr pins.
 278         */
 279        device = bridge->b_int_device;
 280        device &= ~(7 << (pin*3));
 281        device |= (pin << (pin*3));
 282        bridge->b_int_device = device;
 283
 284        bridge->b_wid_tflush;
 285
 286        intr_connect_level(cpu, swlevel);
 287
 288        return 0;       /* Never anything pending.  */
 289}
 290
 291/* Shutdown one of the (PCI ...) IRQs routes over a bridge.  */
 292static void shutdown_bridge_irq(unsigned int irq)
 293{
 294        struct bridge_controller *bc = IRQ_TO_BRIDGE(irq);
 295        bridge_t *bridge = bc->base;
 296        int pin, swlevel;
 297        cpuid_t cpu;
 298
 299        pr_debug("bridge_shutdown: irq 0x%x\n", irq);
 300        pin = SLOT_FROM_PCI_IRQ(irq);
 301
 302        /*
 303         * map irq to a swlevel greater than 6 since the first 6 bits
 304         * of INT_PEND0 are taken
 305         */
 306        swlevel = find_level(&cpu, irq);
 307        intr_disconnect_level(cpu, swlevel);
 308
 309        bridge->b_int_enable &= ~(1 << pin);
 310        bridge->b_wid_tflush;
 311}
 312
 313static inline void enable_bridge_irq(unsigned int irq)
 314{
 315        cpuid_t cpu;
 316        int swlevel;
 317
 318        swlevel = find_level(&cpu, irq);        /* Criminal offence */
 319        intr_connect_level(cpu, swlevel);
 320}
 321
 322static inline void disable_bridge_irq(unsigned int irq)
 323{
 324        cpuid_t cpu;
 325        int swlevel;
 326
 327        swlevel = find_level(&cpu, irq);        /* Criminal offence */
 328        intr_disconnect_level(cpu, swlevel);
 329}
 330
 331static struct irq_chip bridge_irq_type = {
 332        .name           = "bridge",
 333        .startup        = startup_bridge_irq,
 334        .shutdown       = shutdown_bridge_irq,
 335        .ack            = disable_bridge_irq,
 336        .mask           = disable_bridge_irq,
 337        .mask_ack       = disable_bridge_irq,
 338        .unmask         = enable_bridge_irq,
 339};
 340
 341void __devinit register_bridge_irq(unsigned int irq)
 342{
 343        set_irq_chip_and_handler(irq, &bridge_irq_type, handle_level_irq);
 344}
 345
 346int __devinit request_bridge_irq(struct bridge_controller *bc)
 347{
 348        int irq = allocate_irqno();
 349        int swlevel, cpu;
 350        nasid_t nasid;
 351
 352        if (irq < 0)
 353                return irq;
 354
 355        /*
 356         * "map" irq to a swlevel greater than 6 since the first 6 bits
 357         * of INT_PEND0 are taken
 358         */
 359        cpu = bc->irq_cpu;
 360        swlevel = alloc_level(cpu, irq);
 361        if (unlikely(swlevel < 0)) {
 362                free_irqno(irq);
 363
 364                return -EAGAIN;
 365        }
 366
 367        /* Make sure it's not already pending when we connect it. */
 368        nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu));
 369        REMOTE_HUB_CLR_INTR(nasid, swlevel);
 370
 371        intr_connect_level(cpu, swlevel);
 372
 373        register_bridge_irq(irq);
 374
 375        return irq;
 376}
 377
 378asmlinkage void plat_irq_dispatch(void)
 379{
 380        unsigned long pending = read_c0_cause() & read_c0_status();
 381        extern unsigned int rt_timer_irq;
 382
 383        if (pending & CAUSEF_IP4)
 384                do_IRQ(rt_timer_irq);
 385        else if (pending & CAUSEF_IP2)  /* PI_INT_PEND_0 or CC_PEND_{A|B} */
 386                ip27_do_irq_mask0();
 387        else if (pending & CAUSEF_IP3)  /* PI_INT_PEND_1 */
 388                ip27_do_irq_mask1();
 389        else if (pending & CAUSEF_IP5)
 390                ip27_prof_timer();
 391        else if (pending & CAUSEF_IP6)
 392                ip27_hub_error();
 393}
 394
 395void __init arch_init_irq(void)
 396{
 397}
 398
 399void install_ipi(void)
 400{
 401        int slice = LOCAL_HUB_L(PI_CPU_NUM);
 402        int cpu = smp_processor_id();
 403        struct slice_data *si = cpu_data[cpu].data;
 404        struct hub_data *hub = hub_data(cpu_to_node(cpu));
 405        int resched, call;
 406
 407        resched = CPU_RESCHED_A_IRQ + slice;
 408        __set_bit(resched, hub->irq_alloc_mask);
 409        __set_bit(resched, si->irq_enable_mask);
 410        LOCAL_HUB_CLR_INTR(resched);
 411
 412        call = CPU_CALL_A_IRQ + slice;
 413        __set_bit(call, hub->irq_alloc_mask);
 414        __set_bit(call, si->irq_enable_mask);
 415        LOCAL_HUB_CLR_INTR(call);
 416
 417        if (slice == 0) {
 418                LOCAL_HUB_S(PI_INT_MASK0_A, si->irq_enable_mask[0]);
 419                LOCAL_HUB_S(PI_INT_MASK1_A, si->irq_enable_mask[1]);
 420        } else {
 421                LOCAL_HUB_S(PI_INT_MASK0_B, si->irq_enable_mask[0]);
 422                LOCAL_HUB_S(PI_INT_MASK1_B, si->irq_enable_mask[1]);
 423        }
 424}
 425