linux-old/arch/i386/kernel/visws_apic.c
<<
>>
Prefs
   1/*
   2 *      linux/arch/i386/kernel/visws_apic.c
   3 *
   4 *      Copyright (C) 1999 Bent Hagemark, Ingo Molnar
   5 *
   6 *  SGI Visual Workstation interrupt controller
   7 *
   8 *  The Cobalt system ASIC in the Visual Workstation contains a "Cobalt" APIC
   9 *  which serves as the main interrupt controller in the system.  Non-legacy
  10 *  hardware in the system uses this controller directly.  Legacy devices
  11 *  are connected to the PIIX4 which in turn has its 8259(s) connected to
  12 *  a of the Cobalt APIC entry.
  13 */
  14
  15#include <linux/ptrace.h>
  16#include <linux/errno.h>
  17#include <linux/kernel_stat.h>
  18#include <linux/signal.h>
  19#include <linux/sched.h>
  20#include <linux/ioport.h>
  21#include <linux/interrupt.h>
  22#include <linux/timex.h>
  23#include <linux/slab.h>
  24#include <linux/random.h>
  25#include <linux/smp.h>
  26#include <linux/smp_lock.h>
  27#include <linux/init.h>
  28
  29#include <asm/system.h>
  30#include <asm/io.h>
  31#include <asm/irq.h>
  32#include <asm/bitops.h>
  33#include <asm/smp.h>
  34#include <asm/pgtable.h>
  35#include <asm/delay.h>
  36#include <asm/desc.h>
  37
  38#include <asm/cobalt.h>
  39
  40#include <linux/irq.h>
  41
  42/*
  43 * This is the PIIX4-based 8259 that is wired up indirectly to Cobalt
  44 * -- not the manner expected by the normal 8259 code in irq.c.
  45 *
  46 * there is a 'master' physical interrupt source that gets sent to
  47 * the CPU. But in the chipset there are various 'virtual' interrupts
  48 * waiting to be handled. We represent this to Linux through a 'master'
  49 * interrupt controller type, and through a special virtual interrupt-
  50 * controller. Device drivers only see the virtual interrupt sources.
  51 */
  52
  53#define CO_IRQ_BASE     0x20    /* This is the 0x20 in init_IRQ()! */
  54
  55static void startup_piix4_master_irq(unsigned int irq);
  56static void shutdown_piix4_master_irq(unsigned int irq);
  57static void do_piix4_master_IRQ(unsigned int irq, struct pt_regs * regs);
  58#define enable_piix4_master_irq startup_piix4_master_irq
  59#define disable_piix4_master_irq shutdown_piix4_master_irq
  60
  61static struct hw_interrupt_type piix4_master_irq_type = {
  62        "PIIX4-master",
  63        startup_piix4_master_irq,
  64        shutdown_piix4_master_irq,
  65        do_piix4_master_IRQ,
  66        enable_piix4_master_irq,
  67        disable_piix4_master_irq
  68};
  69
  70static void enable_piix4_virtual_irq(unsigned int irq);
  71static void disable_piix4_virtual_irq(unsigned int irq);
  72#define startup_piix4_virtual_irq enable_piix4_virtual_irq
  73#define shutdown_piix4_virtual_irq disable_piix4_virtual_irq
  74
  75static struct hw_interrupt_type piix4_virtual_irq_type = {
  76        "PIIX4-virtual",
  77        startup_piix4_virtual_irq,
  78        shutdown_piix4_virtual_irq,
  79        0, /* no handler, it's never called physically */
  80        enable_piix4_virtual_irq,
  81        disable_piix4_virtual_irq
  82};
  83
  84/*
  85 * This is the SGI Cobalt (IO-)APIC:
  86 */
  87
  88static void do_cobalt_IRQ(unsigned int irq, struct pt_regs * regs);
  89static void enable_cobalt_irq(unsigned int irq);
  90static void disable_cobalt_irq(unsigned int irq);
  91static void startup_cobalt_irq(unsigned int irq);
  92#define shutdown_cobalt_irq disable_cobalt_irq
  93
  94static spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED;
  95
  96static struct hw_interrupt_type cobalt_irq_type = {
  97        "Cobalt-APIC",
  98        startup_cobalt_irq,
  99        shutdown_cobalt_irq,
 100        do_cobalt_IRQ,
 101        enable_cobalt_irq,
 102        disable_cobalt_irq
 103};
 104
 105
 106/*
 107 * Not an __init, needed by the reboot code
 108 */
 109void disable_IO_APIC(void)
 110{
 111        /* Nop on Cobalt */
 112} 
 113
 114/*
 115 * Cobalt (IO)-APIC functions to handle PCI devices.
 116 */
 117
 118static void disable_cobalt_irq(unsigned int irq)
 119{
 120        /* XXX undo the APIC entry here? */
 121
 122        /*
 123         * definitely, we do not want to have IRQ storms from
 124         * unused devices --mingo
 125         */
 126}
 127
 128static void enable_cobalt_irq(unsigned int irq)
 129{
 130}
 131
 132/*
 133 * Set the given Cobalt APIC Redirection Table entry to point
 134 * to the given IDT vector/index.
 135 */
 136static void co_apic_set(int entry, int idtvec)
 137{
 138        co_apic_write(CO_APIC_LO(entry), CO_APIC_LEVEL | (CO_IRQ_BASE+idtvec));
 139        co_apic_write(CO_APIC_HI(entry), 0);
 140
 141        printk("Cobalt APIC Entry %d IDT Vector %d\n", entry, idtvec);
 142}
 143
 144/*
 145 * "irq" really just serves to identify the device.  Here is where we
 146 * map this to the Cobalt APIC entry where it's physically wired.
 147 * This is called via request_irq -> setup_x86_irq -> irq_desc->startup()
 148 */
 149static void startup_cobalt_irq(unsigned int irq)
 150{
 151        /*
 152         * These "irq"'s are wired to the same Cobalt APIC entries
 153         * for all (known) motherboard types/revs
 154         */
 155        switch (irq) {
 156        case CO_IRQ_TIMER:      co_apic_set(CO_APIC_CPU, CO_IRQ_TIMER);
 157                                return;
 158
 159        case CO_IRQ_ENET:       co_apic_set(CO_APIC_ENET, CO_IRQ_ENET);
 160                                return;
 161
 162        case CO_IRQ_SERIAL:     return; /* XXX move to piix4-8259 "virtual" */
 163
 164        case CO_IRQ_8259:       co_apic_set(CO_APIC_8259, CO_IRQ_8259);
 165                                return;
 166
 167        case CO_IRQ_IDE:
 168                switch (visws_board_type) {
 169                case VISWS_320:
 170                        switch (visws_board_rev) {
 171                        case 5:
 172                                co_apic_set(CO_APIC_0_5_IDE0, CO_IRQ_IDE);
 173                                co_apic_set(CO_APIC_0_5_IDE1, CO_IRQ_IDE);
 174                                        return;
 175                        case 6:
 176                                co_apic_set(CO_APIC_0_6_IDE0, CO_IRQ_IDE);
 177                                co_apic_set(CO_APIC_0_6_IDE1, CO_IRQ_IDE);
 178                                        return;
 179                        }
 180                case VISWS_540:
 181                        switch (visws_board_rev) {
 182                        case 2:
 183                                co_apic_set(CO_APIC_1_2_IDE0, CO_IRQ_IDE);
 184                                        return;
 185                        }
 186                }
 187                break;
 188        default:
 189                panic("huh?");
 190        }
 191}
 192
 193/*
 194 * This is the handle() op in do_IRQ()
 195 */
 196static void do_cobalt_IRQ(unsigned int irq, struct pt_regs * regs)
 197{
 198        struct irqaction * action;
 199        irq_desc_t *desc = irq_desc + irq;
 200
 201        spin_lock(&irq_controller_lock);
 202        {
 203                unsigned int status;
 204                /* XXX APIC EOI? */
 205                status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
 206                action = NULL;
 207                if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
 208                        action = desc->action;
 209                        status |= IRQ_INPROGRESS;
 210                }
 211                desc->status = status;
 212        }
 213        spin_unlock(&irq_controller_lock);
 214
 215        /* Exit early if we had no action or it was disabled */
 216        if (!action)
 217                return;
 218
 219        handle_IRQ_event(irq, regs, action);
 220
 221        (void)co_cpu_read(CO_CPU_REV); /* Sync driver ack to its h/w */
 222        apic_write(APIC_EOI, APIC_EIO_ACK); /* Send EOI to Cobalt APIC */
 223
 224        spin_lock(&irq_controller_lock);
 225        {
 226                unsigned int status = desc->status & ~IRQ_INPROGRESS;
 227                desc->status = status;
 228                if (!(status & IRQ_DISABLED))
 229                        enable_cobalt_irq(irq);
 230        }
 231        spin_unlock(&irq_controller_lock);
 232}
 233
 234/*
 235 * PIIX4-8259 master/virtual functions to handle:
 236 *
 237 *      floppy
 238 *      parallel
 239 *      serial
 240 *      audio (?)
 241 *
 242 * None of these get Cobalt APIC entries, neither do they have IDT
 243 * entries. These interrupts are purely virtual and distributed from
 244 * the 'master' interrupt source: CO_IRQ_8259.
 245 *
 246 * When the 8259 interrupts its handler figures out which of these
 247 * devices is interrupting and dispatches to it's handler.
 248 *
 249 * CAREFUL: devices see the 'virtual' interrupt only. Thus disable/
 250 * enable_irq gets the right irq. This 'master' irq is never directly
 251 * manipulated by any driver.
 252 */
 253
 254static void startup_piix4_master_irq(unsigned int irq)
 255{
 256        /* ICW1 */
 257        outb(0x11, 0x20);
 258        outb(0x11, 0xa0);
 259
 260        /* ICW2 */
 261        outb(0x08, 0x21);
 262        outb(0x70, 0xa1);
 263
 264        /* ICW3 */
 265        outb(0x04, 0x21);
 266        outb(0x02, 0xa1);
 267
 268        /* ICW4 */
 269        outb(0x01, 0x21);
 270        outb(0x01, 0xa1);
 271
 272        /* OCW1 - disable all interrupts in both 8259's */
 273        outb(0xff, 0x21);
 274        outb(0xff, 0xa1);
 275
 276        startup_cobalt_irq(irq);
 277}
 278
 279static void shutdown_piix4_master_irq(unsigned int irq)
 280{
 281        /*
 282         * [we skip the 8259 magic here, not strictly necessary]
 283         */
 284
 285        shutdown_cobalt_irq(irq);
 286}
 287
 288static void do_piix4_master_IRQ(unsigned int irq, struct pt_regs * regs)
 289{
 290        int realirq, mask;
 291
 292        /* Find out what's interrupting in the PIIX4 8259 */
 293
 294        spin_lock(&irq_controller_lock);
 295        outb(0x0c, 0x20);               /* OCW3 Poll command */
 296        realirq = inb(0x20);
 297
 298        if (!(realirq & 0x80)) {
 299                /*
 300                 * Bit 7 == 0 means invalid/spurious
 301                 */
 302                goto out_unlock;
 303        }
 304        realirq &= 0x7f;
 305
 306        /*
 307         * mask and ack the 8259
 308         */
 309        mask = inb(0x21);
 310        if ((mask >> realirq) & 0x01)
 311                /*
 312                 * This IRQ is masked... ignore
 313                 */
 314                goto out_unlock;
 315
 316        outb(mask | (1<<realirq), 0x21);
 317        /*
 318         * OCW2 - non-specific EOI
 319         */
 320        outb(0x20, 0x20);
 321
 322        spin_unlock(&irq_controller_lock);
 323
 324        /*
 325         * handle this 'virtual interrupt' as a Cobalt one now.
 326         */
 327        kstat.irqs[smp_processor_id()][irq]++;
 328        do_cobalt_IRQ(realirq, regs);
 329
 330        spin_lock(&irq_controller_lock);
 331        {
 332                irq_desc_t *desc = irq_desc + realirq;
 333
 334                if (!(desc->status & IRQ_DISABLED))
 335                        enable_piix4_virtual_irq(realirq);
 336        }
 337        spin_unlock(&irq_controller_lock);
 338        return;
 339
 340out_unlock:
 341        spin_unlock(&irq_controller_lock);
 342        return;
 343}
 344
 345static void enable_piix4_virtual_irq(unsigned int irq)
 346{
 347        /*
 348         * assumes this irq is one of the legacy devices
 349         */
 350
 351        unsigned int mask = inb(0x21);
 352        mask &= ~(1 << irq);
 353        outb(mask, 0x21);
 354        enable_cobalt_irq(irq);
 355}
 356
 357/*
 358 * assumes this irq is one of the legacy devices
 359 */
 360static void disable_piix4_virtual_irq(unsigned int irq)
 361{
 362        unsigned int mask;
 363
 364        disable_cobalt_irq(irq);
 365
 366        mask = inb(0x21);
 367        mask &= ~(1 << irq);
 368        outb(mask, 0x21);
 369}
 370
 371static struct irqaction master_action =
 372                { no_action, 0, 0, "PIIX4-8259", NULL, NULL };
 373
 374void init_VISWS_APIC_irqs(void)
 375{
 376        int i;
 377
 378        for (i = 0; i < 16; i++) {
 379                irq_desc[i].status = IRQ_DISABLED;
 380                irq_desc[i].action = 0;
 381                irq_desc[i].depth = 1;
 382
 383                /*
 384                 * Cobalt IRQs are mapped to standard ISA
 385                 * interrupt vectors:
 386                 */
 387                switch (i) {
 388                        /*
 389                         * Only CO_IRQ_8259 will be raised
 390                         * externally.
 391                         */
 392                case CO_IRQ_8259:
 393                        irq_desc[i].handler = &piix4_master_irq_type;
 394                        break;
 395                case CO_IRQ_FLOPPY:
 396                case CO_IRQ_PARLL:
 397                        irq_desc[i].handler = &piix4_virtual_irq_type;
 398                        break;
 399                default:
 400                        irq_desc[i].handler = &cobalt_irq_type;
 401                        break;
 402                }
 403        }
 404
 405        /*
 406         * The master interrupt is always present:
 407         */
 408        setup_x86_irq(CO_IRQ_8259, &master_action);
 409}
 410
 411
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.