linux/arch/m68k/mac/oss.c
<<
>>
Prefs
   1/*
   2 *      OSS handling
   3 *      Written by Joshua M. Thompson (funaho@jurai.org)
   4 *
   5 *
   6 *      This chip is used in the IIfx in place of VIA #2. It acts like a fancy
   7 *      VIA chip with prorammable interrupt levels.
   8 *
   9 * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some
  10 *                recent insights into OSS operational details.
  11 * 990610 (jmt) - Now taking full advantage of the OSS. Interrupts are mapped
  12 *                to mostly match the A/UX interrupt scheme supported on the
  13 *                VIA side. Also added support for enabling the ISM irq again
  14 *                since we now have a functional IOP manager.
  15 */
  16
  17#include <linux/types.h>
  18#include <linux/kernel.h>
  19#include <linux/mm.h>
  20#include <linux/delay.h>
  21#include <linux/init.h>
  22
  23#include <asm/bootinfo.h>
  24#include <asm/macintosh.h>
  25#include <asm/macints.h>
  26#include <asm/mac_via.h>
  27#include <asm/mac_oss.h>
  28
  29int oss_present;
  30volatile struct mac_oss *oss;
  31
  32static irqreturn_t oss_irq(int, void *);
  33static irqreturn_t oss_nubus_irq(int, void *);
  34
  35extern irqreturn_t via1_irq(int, void *);
  36
  37/*
  38 * Initialize the OSS
  39 *
  40 * The OSS "detection" code is actually in via_init() which is always called
  41 * before us. Thus we can count on oss_present being valid on entry.
  42 */
  43
  44void __init oss_init(void)
  45{
  46        int i;
  47
  48        if (!oss_present) return;
  49
  50        oss = (struct mac_oss *) OSS_BASE;
  51
  52        /* Disable all interrupts. Unlike a VIA it looks like we    */
  53        /* do this by setting the source's interrupt level to zero. */
  54
  55        for (i = 0; i <= OSS_NUM_SOURCES; i++) {
  56                oss->irq_level[i] = OSS_IRQLEV_DISABLED;
  57        }
  58        /* If we disable VIA1 here, we never really handle it... */
  59        oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1;
  60}
  61
  62/*
  63 * Register the OSS and NuBus interrupt dispatchers.
  64 */
  65
  66void __init oss_register_interrupts(void)
  67{
  68        if (request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK,
  69                        "scsi", (void *) oss))
  70                pr_err("Couldn't register %s interrupt\n", "scsi");
  71        if (request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK,
  72                        "nubus", (void *) oss))
  73                pr_err("Couldn't register %s interrupt\n", "nubus");
  74        if (request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK,
  75                        "sound", (void *) oss))
  76                pr_err("Couldn't register %s interrupt\n", "sound");
  77        if (request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK,
  78                        "via1", (void *) via1))
  79                pr_err("Couldn't register %s interrupt\n", "via1");
  80}
  81
  82/*
  83 * Initialize OSS for Nubus access
  84 */
  85
  86void __init oss_nubus_init(void)
  87{
  88}
  89
  90/*
  91 * Handle miscellaneous OSS interrupts. Right now that's just sound
  92 * and SCSI; everything else is routed to its own autovector IRQ.
  93 */
  94
  95static irqreturn_t oss_irq(int irq, void *dev_id)
  96{
  97        int events;
  98
  99        events = oss->irq_pending & (OSS_IP_SOUND|OSS_IP_SCSI);
 100        if (!events)
 101                return IRQ_NONE;
 102
 103#ifdef DEBUG_IRQS
 104        if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) {
 105                printk("oss_irq: irq %d events = 0x%04X\n", irq,
 106                        (int) oss->irq_pending);
 107        }
 108#endif
 109        /* FIXME: how do you clear a pending IRQ?    */
 110
 111        if (events & OSS_IP_SOUND) {
 112                oss->irq_pending &= ~OSS_IP_SOUND;
 113                /* FIXME: call sound handler */
 114        } else if (events & OSS_IP_SCSI) {
 115                oss->irq_pending &= ~OSS_IP_SCSI;
 116                m68k_handle_int(IRQ_MAC_SCSI);
 117        } else {
 118                /* FIXME: error check here? */
 119        }
 120        return IRQ_HANDLED;
 121}
 122
 123/*
 124 * Nubus IRQ handler, OSS style
 125 *
 126 * Unlike the VIA/RBV this is on its own autovector interrupt level.
 127 */
 128
 129static irqreturn_t oss_nubus_irq(int irq, void *dev_id)
 130{
 131        int events, irq_bit, i;
 132
 133        events = oss->irq_pending & OSS_IP_NUBUS;
 134        if (!events)
 135                return IRQ_NONE;
 136
 137#ifdef DEBUG_NUBUS_INT
 138        if (console_loglevel > 7) {
 139                printk("oss_nubus_irq: events = 0x%04X\n", events);
 140        }
 141#endif
 142        /* There are only six slots on the OSS, not seven */
 143
 144        i = 6;
 145        irq_bit = 0x40;
 146        do {
 147                --i;
 148                irq_bit >>= 1;
 149                if (events & irq_bit) {
 150                        oss->irq_pending &= ~irq_bit;
 151                        m68k_handle_int(NUBUS_SOURCE_BASE + i);
 152                }
 153        } while(events & (irq_bit - 1));
 154        return IRQ_HANDLED;
 155}
 156
 157/*
 158 * Enable an OSS interrupt
 159 *
 160 * It looks messy but it's rather straightforward. The switch() statement
 161 * just maps the machspec interrupt numbers to the right OSS interrupt
 162 * source (if the OSS handles that interrupt) and then sets the interrupt
 163 * level for that source to nonzero, thus enabling the interrupt.
 164 */
 165
 166void oss_irq_enable(int irq) {
 167#ifdef DEBUG_IRQUSE
 168        printk("oss_irq_enable(%d)\n", irq);
 169#endif
 170        switch(irq) {
 171                case IRQ_MAC_SCC:
 172                        oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
 173                        break;
 174                case IRQ_MAC_ADB:
 175                        oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM;
 176                        break;
 177                case IRQ_MAC_SCSI:
 178                        oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
 179                        break;
 180                case IRQ_NUBUS_9:
 181                case IRQ_NUBUS_A:
 182                case IRQ_NUBUS_B:
 183                case IRQ_NUBUS_C:
 184                case IRQ_NUBUS_D:
 185                case IRQ_NUBUS_E:
 186                        irq -= NUBUS_SOURCE_BASE;
 187                        oss->irq_level[irq] = OSS_IRQLEV_NUBUS;
 188                        break;
 189#ifdef DEBUG_IRQUSE
 190                default:
 191                        printk("%s unknown irq %d\n", __func__, irq);
 192                        break;
 193#endif
 194        }
 195}
 196
 197/*
 198 * Disable an OSS interrupt
 199 *
 200 * Same as above except we set the source's interrupt level to zero,
 201 * to disable the interrupt.
 202 */
 203
 204void oss_irq_disable(int irq) {
 205#ifdef DEBUG_IRQUSE
 206        printk("oss_irq_disable(%d)\n", irq);
 207#endif
 208        switch(irq) {
 209                case IRQ_MAC_SCC:
 210                        oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_DISABLED;
 211                        break;
 212                case IRQ_MAC_ADB:
 213                        oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_DISABLED;
 214                        break;
 215                case IRQ_MAC_SCSI:
 216                        oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED;
 217                        break;
 218                case IRQ_NUBUS_9:
 219                case IRQ_NUBUS_A:
 220                case IRQ_NUBUS_B:
 221                case IRQ_NUBUS_C:
 222                case IRQ_NUBUS_D:
 223                case IRQ_NUBUS_E:
 224                        irq -= NUBUS_SOURCE_BASE;
 225                        oss->irq_level[irq] = OSS_IRQLEV_DISABLED;
 226                        break;
 227#ifdef DEBUG_IRQUSE
 228                default:
 229                        printk("%s unknown irq %d\n", __func__, irq);
 230                        break;
 231#endif
 232        }
 233}
 234
 235/*
 236 * Clear an OSS interrupt
 237 *
 238 * Not sure if this works or not but it's the only method I could
 239 * think of based on the contents of the mac_oss structure.
 240 */
 241
 242void oss_irq_clear(int irq) {
 243        /* FIXME: how to do this on OSS? */
 244        switch(irq) {
 245                case IRQ_MAC_SCC:
 246                        oss->irq_pending &= ~OSS_IP_IOPSCC;
 247                        break;
 248                case IRQ_MAC_ADB:
 249                        oss->irq_pending &= ~OSS_IP_IOPISM;
 250                        break;
 251                case IRQ_MAC_SCSI:
 252                        oss->irq_pending &= ~OSS_IP_SCSI;
 253                        break;
 254                case IRQ_NUBUS_9:
 255                case IRQ_NUBUS_A:
 256                case IRQ_NUBUS_B:
 257                case IRQ_NUBUS_C:
 258                case IRQ_NUBUS_D:
 259                case IRQ_NUBUS_E:
 260                        irq -= NUBUS_SOURCE_BASE;
 261                        oss->irq_pending &= ~(1 << irq);
 262                        break;
 263        }
 264}
 265
 266/*
 267 * Check to see if a specific OSS interrupt is pending
 268 */
 269
 270int oss_irq_pending(int irq)
 271{
 272        switch(irq) {
 273                case IRQ_MAC_SCC:
 274                        return oss->irq_pending & OSS_IP_IOPSCC;
 275                        break;
 276                case IRQ_MAC_ADB:
 277                        return oss->irq_pending & OSS_IP_IOPISM;
 278                        break;
 279                case IRQ_MAC_SCSI:
 280                        return oss->irq_pending & OSS_IP_SCSI;
 281                        break;
 282                case IRQ_NUBUS_9:
 283                case IRQ_NUBUS_A:
 284                case IRQ_NUBUS_B:
 285                case IRQ_NUBUS_C:
 286                case IRQ_NUBUS_D:
 287                case IRQ_NUBUS_E:
 288                        irq -= NUBUS_SOURCE_BASE;
 289                        return oss->irq_pending & (1 << irq);
 290                        break;
 291        }
 292        return 0;
 293}
 294
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.