linux/arch/mips/sgi-ip27/ip27-irq-pci.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/irq.h>
  12#include <linux/errno.h>
  13#include <linux/signal.h>
  14#include <linux/sched.h>
  15#include <linux/types.h>
  16#include <linux/interrupt.h>
  17#include <linux/ioport.h>
  18#include <linux/timex.h>
  19#include <linux/smp.h>
  20#include <linux/random.h>
  21#include <linux/kernel.h>
  22#include <linux/kernel_stat.h>
  23#include <linux/delay.h>
  24#include <linux/bitops.h>
  25
  26#include <asm/bootinfo.h>
  27#include <asm/io.h>
  28#include <asm/mipsregs.h>
  29
  30#include <asm/processor.h>
  31#include <asm/pci/bridge.h>
  32#include <asm/sn/addrs.h>
  33#include <asm/sn/agent.h>
  34#include <asm/sn/arch.h>
  35#include <asm/sn/hub.h>
  36#include <asm/sn/intr.h>
  37
  38/*
  39 * Linux has a controller-independent x86 interrupt architecture.
  40 * every controller has a 'controller-template', that is used
  41 * by the main code to do the right thing. Each driver-visible
  42 * interrupt source is transparently wired to the appropriate
  43 * controller. Thus drivers need not be aware of the
  44 * interrupt-controller.
  45 *
  46 * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC,
  47 * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC.
  48 * (IO-APICs assumed to be messaging to Pentium local-APICs)
  49 *
  50 * the code is designed to be easily extended with new/different
  51 * interrupt controllers, without having to do assembly magic.
  52 */
  53
  54extern struct bridge_controller *irq_to_bridge[];
  55extern int irq_to_slot[];
  56
  57/*
  58 * use these macros to get the encoded nasid and widget id
  59 * from the irq value
  60 */
  61#define IRQ_TO_BRIDGE(i)                irq_to_bridge[(i)]
  62#define SLOT_FROM_PCI_IRQ(i)            irq_to_slot[i]
  63
  64static inline int alloc_level(int cpu, int irq)
  65{
  66        struct hub_data *hub = hub_data(cpu_to_node(cpu));
  67        struct slice_data *si = cpu_data[cpu].data;
  68        int level;
  69
  70        level = find_first_zero_bit(hub->irq_alloc_mask, LEVELS_PER_SLICE);
  71        if (level >= LEVELS_PER_SLICE)
  72                panic("Cpu %d flooded with devices", cpu);
  73
  74        __set_bit(level, hub->irq_alloc_mask);
  75        si->level_to_irq[level] = irq;
  76
  77        return level;
  78}
  79
  80static inline int find_level(cpuid_t *cpunum, int irq)
  81{
  82        int cpu, i;
  83
  84        for_each_online_cpu(cpu) {
  85                struct slice_data *si = cpu_data[cpu].data;
  86
  87                for (i = BASE_PCI_IRQ; i < LEVELS_PER_SLICE; i++)
  88                        if (si->level_to_irq[i] == irq) {
  89                                *cpunum = cpu;
  90
  91                                return i;
  92                        }
  93        }
  94
  95        panic("Could not identify cpu/level for irq %d", irq);
  96}
  97
  98static int intr_connect_level(int cpu, int bit)
  99{
 100        nasid_t nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu));
 101        struct slice_data *si = cpu_data[cpu].data;
 102
 103        set_bit(bit, si->irq_enable_mask);
 104
 105        if (!cputoslice(cpu)) {
 106                REMOTE_HUB_S(nasid, PI_INT_MASK0_A, si->irq_enable_mask[0]);
 107                REMOTE_HUB_S(nasid, PI_INT_MASK1_A, si->irq_enable_mask[1]);
 108        } else {
 109                REMOTE_HUB_S(nasid, PI_INT_MASK0_B, si->irq_enable_mask[0]);
 110                REMOTE_HUB_S(nasid, PI_INT_MASK1_B, si->irq_enable_mask[1]);
 111        }
 112
 113        return 0;
 114}
 115
 116static int intr_disconnect_level(int cpu, int bit)
 117{
 118        nasid_t nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu));
 119        struct slice_data *si = cpu_data[cpu].data;
 120
 121        clear_bit(bit, si->irq_enable_mask);
 122
 123        if (!cputoslice(cpu)) {
 124                REMOTE_HUB_S(nasid, PI_INT_MASK0_A, si->irq_enable_mask[0]);
 125                REMOTE_HUB_S(nasid, PI_INT_MASK1_A, si->irq_enable_mask[1]);
 126        } else {
 127                REMOTE_HUB_S(nasid, PI_INT_MASK0_B, si->irq_enable_mask[0]);
 128                REMOTE_HUB_S(nasid, PI_INT_MASK1_B, si->irq_enable_mask[1]);
 129        }
 130
 131        return 0;
 132}
 133
 134/* Startup one of the (PCI ...) IRQs routes over a bridge.  */
 135static unsigned int startup_bridge_irq(struct irq_data *d)
 136{
 137        struct bridge_controller *bc;
 138        bridgereg_t device;
 139        bridge_t *bridge;
 140        int pin, swlevel;
 141        cpuid_t cpu;
 142
 143        pin = SLOT_FROM_PCI_IRQ(d->irq);
 144        bc = IRQ_TO_BRIDGE(d->irq);
 145        bridge = bc->base;
 146
 147        pr_debug("bridge_startup(): irq= 0x%x  pin=%d\n", d->irq, pin);
 148        /*
 149         * "map" irq to a swlevel greater than 6 since the first 6 bits
 150         * of INT_PEND0 are taken
 151         */
 152        swlevel = find_level(&cpu, d->irq);
 153        bridge->b_int_addr[pin].addr = (0x20000 | swlevel | (bc->nasid << 8));
 154        bridge->b_int_enable |= (1 << pin);
 155        bridge->b_int_enable |= 0x7ffffe00;     /* more stuff in int_enable */
 156
 157        /*
 158         * Enable sending of an interrupt clear packt to the hub on a high to
 159         * low transition of the interrupt pin.
 160         *
 161         * IRIX sets additional bits in the address which are documented as
 162         * reserved in the bridge docs.
 163         */
 164        bridge->b_int_mode |= (1UL << pin);
 165
 166        /*
 167         * We assume the bridge to have a 1:1 mapping between devices
 168         * (slots) and intr pins.
 169         */
 170        device = bridge->b_int_device;
 171        device &= ~(7 << (pin*3));
 172        device |= (pin << (pin*3));
 173        bridge->b_int_device = device;
 174
 175        bridge->b_wid_tflush;
 176
 177        intr_connect_level(cpu, swlevel);
 178
 179        return 0;       /* Never anything pending.  */
 180}
 181
 182/* Shutdown one of the (PCI ...) IRQs routes over a bridge.  */
 183static void shutdown_bridge_irq(struct irq_data *d)
 184{
 185        struct bridge_controller *bc = IRQ_TO_BRIDGE(d->irq);
 186        bridge_t *bridge = bc->base;
 187        int pin, swlevel;
 188        cpuid_t cpu;
 189
 190        pr_debug("bridge_shutdown: irq 0x%x\n", d->irq);
 191        pin = SLOT_FROM_PCI_IRQ(d->irq);
 192
 193        /*
 194         * map irq to a swlevel greater than 6 since the first 6 bits
 195         * of INT_PEND0 are taken
 196         */
 197        swlevel = find_level(&cpu, d->irq);
 198        intr_disconnect_level(cpu, swlevel);
 199
 200        bridge->b_int_enable &= ~(1 << pin);
 201        bridge->b_wid_tflush;
 202}
 203
 204static inline void enable_bridge_irq(struct irq_data *d)
 205{
 206        cpuid_t cpu;
 207        int swlevel;
 208
 209        swlevel = find_level(&cpu, d->irq);     /* Criminal offence */
 210        intr_connect_level(cpu, swlevel);
 211}
 212
 213static inline void disable_bridge_irq(struct irq_data *d)
 214{
 215        cpuid_t cpu;
 216        int swlevel;
 217
 218        swlevel = find_level(&cpu, d->irq);     /* Criminal offence */
 219        intr_disconnect_level(cpu, swlevel);
 220}
 221
 222static struct irq_chip bridge_irq_type = {
 223        .name           = "bridge",
 224        .irq_startup    = startup_bridge_irq,
 225        .irq_shutdown   = shutdown_bridge_irq,
 226        .irq_mask       = disable_bridge_irq,
 227        .irq_unmask     = enable_bridge_irq,
 228};
 229
 230void register_bridge_irq(unsigned int irq)
 231{
 232        irq_set_chip_and_handler(irq, &bridge_irq_type, handle_level_irq);
 233}
 234
 235int request_bridge_irq(struct bridge_controller *bc)
 236{
 237        int irq = allocate_irqno();
 238        int swlevel, cpu;
 239        nasid_t nasid;
 240
 241        if (irq < 0)
 242                return irq;
 243
 244        /*
 245         * "map" irq to a swlevel greater than 6 since the first 6 bits
 246         * of INT_PEND0 are taken
 247         */
 248        cpu = bc->irq_cpu;
 249        swlevel = alloc_level(cpu, irq);
 250        if (unlikely(swlevel < 0)) {
 251                free_irqno(irq);
 252
 253                return -EAGAIN;
 254        }
 255
 256        /* Make sure it's not already pending when we connect it. */
 257        nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu));
 258        REMOTE_HUB_CLR_INTR(nasid, swlevel);
 259
 260        intr_connect_level(cpu, swlevel);
 261
 262        register_bridge_irq(irq);
 263
 264        return irq;
 265}
 266