linux/arch/m68k/mac/oss.c
<<
>>
Prefs
   1/*
   2 *      Operating System Services (OSS) chip 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#include <linux/irq.h>
  23
  24#include <asm/bootinfo.h>
  25#include <asm/macintosh.h>
  26#include <asm/macints.h>
  27#include <asm/mac_via.h>
  28#include <asm/mac_oss.h>
  29
  30int oss_present;
  31volatile struct mac_oss *oss;
  32
  33/*
  34 * Initialize the OSS
  35 *
  36 * The OSS "detection" code is actually in via_init() which is always called
  37 * before us. Thus we can count on oss_present being valid on entry.
  38 */
  39
  40void __init oss_init(void)
  41{
  42        int i;
  43
  44        if (!oss_present) return;
  45
  46        oss = (struct mac_oss *) OSS_BASE;
  47
  48        /* Disable all interrupts. Unlike a VIA it looks like we    */
  49        /* do this by setting the source's interrupt level to zero. */
  50
  51        for (i = 0; i <= OSS_NUM_SOURCES; i++) {
  52                oss->irq_level[i] = 0;
  53        }
  54}
  55
  56/*
  57 * Initialize OSS for Nubus access
  58 */
  59
  60void __init oss_nubus_init(void)
  61{
  62}
  63
  64/*
  65 * Handle miscellaneous OSS interrupts.
  66 */
  67
  68static void oss_irq(unsigned int irq, struct irq_desc *desc)
  69{
  70        int events = oss->irq_pending &
  71                     (OSS_IP_IOPSCC | OSS_IP_SCSI | OSS_IP_IOPISM);
  72
  73#ifdef DEBUG_IRQS
  74        if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) {
  75                printk("oss_irq: irq %u events = 0x%04X\n", irq,
  76                        (int) oss->irq_pending);
  77        }
  78#endif
  79
  80        if (events & OSS_IP_IOPSCC) {
  81                oss->irq_pending &= ~OSS_IP_IOPSCC;
  82                generic_handle_irq(IRQ_MAC_SCC);
  83        }
  84
  85        if (events & OSS_IP_SCSI) {
  86                oss->irq_pending &= ~OSS_IP_SCSI;
  87                generic_handle_irq(IRQ_MAC_SCSI);
  88        }
  89
  90        if (events & OSS_IP_IOPISM) {
  91                oss->irq_pending &= ~OSS_IP_IOPISM;
  92                generic_handle_irq(IRQ_MAC_ADB);
  93        }
  94}
  95
  96/*
  97 * Nubus IRQ handler, OSS style
  98 *
  99 * Unlike the VIA/RBV this is on its own autovector interrupt level.
 100 */
 101
 102static void oss_nubus_irq(unsigned int irq, struct irq_desc *desc)
 103{
 104        int events, irq_bit, i;
 105
 106        events = oss->irq_pending & OSS_IP_NUBUS;
 107        if (!events)
 108                return;
 109
 110#ifdef DEBUG_NUBUS_INT
 111        if (console_loglevel > 7) {
 112                printk("oss_nubus_irq: events = 0x%04X\n", events);
 113        }
 114#endif
 115        /* There are only six slots on the OSS, not seven */
 116
 117        i = 6;
 118        irq_bit = 0x40;
 119        do {
 120                --i;
 121                irq_bit >>= 1;
 122                if (events & irq_bit) {
 123                        oss->irq_pending &= ~irq_bit;
 124                        generic_handle_irq(NUBUS_SOURCE_BASE + i);
 125                }
 126        } while(events & (irq_bit - 1));
 127}
 128
 129/*
 130 * Register the OSS and NuBus interrupt dispatchers.
 131 *
 132 * This IRQ mapping is laid out with two things in mind: first, we try to keep
 133 * things on their own levels to avoid having to do double-dispatches. Second,
 134 * the levels match as closely as possible the alternate IRQ mapping mode (aka
 135 * "A/UX mode") available on some VIA machines.
 136 */
 137
 138#define OSS_IRQLEV_IOPISM    IRQ_AUTO_1
 139#define OSS_IRQLEV_SCSI      IRQ_AUTO_2
 140#define OSS_IRQLEV_NUBUS     IRQ_AUTO_3
 141#define OSS_IRQLEV_IOPSCC    IRQ_AUTO_4
 142#define OSS_IRQLEV_VIA1      IRQ_AUTO_6
 143
 144void __init oss_register_interrupts(void)
 145{
 146        irq_set_chained_handler(OSS_IRQLEV_IOPISM, oss_irq);
 147        irq_set_chained_handler(OSS_IRQLEV_SCSI,   oss_irq);
 148        irq_set_chained_handler(OSS_IRQLEV_NUBUS,  oss_nubus_irq);
 149        irq_set_chained_handler(OSS_IRQLEV_IOPSCC, oss_irq);
 150        irq_set_chained_handler(OSS_IRQLEV_VIA1,   via1_irq);
 151
 152        /* OSS_VIA1 gets enabled here because it has no machspec interrupt. */
 153        oss->irq_level[OSS_VIA1] = IRQ_AUTO_6;
 154}
 155
 156/*
 157 * Enable an OSS interrupt
 158 *
 159 * It looks messy but it's rather straightforward. The switch() statement
 160 * just maps the machspec interrupt numbers to the right OSS interrupt
 161 * source (if the OSS handles that interrupt) and then sets the interrupt
 162 * level for that source to nonzero, thus enabling the interrupt.
 163 */
 164
 165void oss_irq_enable(int irq) {
 166#ifdef DEBUG_IRQUSE
 167        printk("oss_irq_enable(%d)\n", irq);
 168#endif
 169        switch(irq) {
 170                case IRQ_MAC_SCC:
 171                        oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
 172                        return;
 173                case IRQ_MAC_ADB:
 174                        oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM;
 175                        return;
 176                case IRQ_MAC_SCSI:
 177                        oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
 178                        return;
 179                case IRQ_NUBUS_9:
 180                case IRQ_NUBUS_A:
 181                case IRQ_NUBUS_B:
 182                case IRQ_NUBUS_C:
 183                case IRQ_NUBUS_D:
 184                case IRQ_NUBUS_E:
 185                        irq -= NUBUS_SOURCE_BASE;
 186                        oss->irq_level[irq] = OSS_IRQLEV_NUBUS;
 187                        return;
 188        }
 189
 190        if (IRQ_SRC(irq) == 1)
 191                via_irq_enable(irq);
 192}
 193
 194/*
 195 * Disable an OSS interrupt
 196 *
 197 * Same as above except we set the source's interrupt level to zero,
 198 * to disable the interrupt.
 199 */
 200
 201void oss_irq_disable(int irq) {
 202#ifdef DEBUG_IRQUSE
 203        printk("oss_irq_disable(%d)\n", irq);
 204#endif
 205        switch(irq) {
 206                case IRQ_MAC_SCC:
 207                        oss->irq_level[OSS_IOPSCC] = 0;
 208                        return;
 209                case IRQ_MAC_ADB:
 210                        oss->irq_level[OSS_IOPISM] = 0;
 211                        return;
 212                case IRQ_MAC_SCSI:
 213                        oss->irq_level[OSS_SCSI] = 0;
 214                        return;
 215                case IRQ_NUBUS_9:
 216                case IRQ_NUBUS_A:
 217                case IRQ_NUBUS_B:
 218                case IRQ_NUBUS_C:
 219                case IRQ_NUBUS_D:
 220                case IRQ_NUBUS_E:
 221                        irq -= NUBUS_SOURCE_BASE;
 222                        oss->irq_level[irq] = 0;
 223                        return;
 224        }
 225
 226        if (IRQ_SRC(irq) == 1)
 227                via_irq_disable(irq);
 228}
 229