linux/arch/m68k/mac/oss.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *      Operating System Services (OSS) chip handling
   4 *      Written by Joshua M. Thompson (funaho@jurai.org)
   5 *
   6 *
   7 *      This chip is used in the IIfx in place of VIA #2. It acts like a fancy
   8 *      VIA chip with prorammable interrupt levels.
   9 *
  10 * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some
  11 *                recent insights into OSS operational details.
  12 * 990610 (jmt) - Now taking full advantage of the OSS. Interrupts are mapped
  13 *                to mostly match the A/UX interrupt scheme supported on the
  14 *                VIA side. Also added support for enabling the ISM irq again
  15 *                since we now have a functional IOP manager.
  16 */
  17
  18#include <linux/types.h>
  19#include <linux/kernel.h>
  20#include <linux/mm.h>
  21#include <linux/delay.h>
  22#include <linux/init.h>
  23#include <linux/irq.h>
  24
  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
  37void __init oss_init(void)
  38{
  39        int i;
  40
  41        if (macintosh_config->ident != MAC_MODEL_IIFX)
  42                return;
  43
  44        oss = (struct mac_oss *) OSS_BASE;
  45        pr_debug("OSS detected at %p", oss);
  46        oss_present = 1;
  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 * Handle OSS interrupts.
  57 * XXX how do you clear a pending IRQ? is it even necessary?
  58 */
  59
  60static void oss_iopism_irq(struct irq_desc *desc)
  61{
  62        generic_handle_irq(IRQ_MAC_ADB);
  63}
  64
  65static void oss_scsi_irq(struct irq_desc *desc)
  66{
  67        generic_handle_irq(IRQ_MAC_SCSI);
  68}
  69
  70static void oss_nubus_irq(struct irq_desc *desc)
  71{
  72        u16 events, irq_bit;
  73        int irq_num;
  74
  75        events = oss->irq_pending & OSS_IP_NUBUS;
  76        irq_num = NUBUS_SOURCE_BASE + 5;
  77        irq_bit = OSS_IP_NUBUS5;
  78        do {
  79                if (events & irq_bit) {
  80                        events &= ~irq_bit;
  81                        generic_handle_irq(irq_num);
  82                }
  83                --irq_num;
  84                irq_bit >>= 1;
  85        } while (events);
  86}
  87
  88static void oss_iopscc_irq(struct irq_desc *desc)
  89{
  90        generic_handle_irq(IRQ_MAC_SCC);
  91}
  92
  93/*
  94 * Register the OSS and NuBus interrupt dispatchers.
  95 *
  96 * This IRQ mapping is laid out with two things in mind: first, we try to keep
  97 * things on their own levels to avoid having to do double-dispatches. Second,
  98 * the levels match as closely as possible the alternate IRQ mapping mode (aka
  99 * "A/UX mode") available on some VIA machines.
 100 */
 101
 102#define OSS_IRQLEV_IOPISM    IRQ_AUTO_1
 103#define OSS_IRQLEV_SCSI      IRQ_AUTO_2
 104#define OSS_IRQLEV_NUBUS     IRQ_AUTO_3
 105#define OSS_IRQLEV_IOPSCC    IRQ_AUTO_4
 106#define OSS_IRQLEV_VIA1      IRQ_AUTO_6
 107
 108void __init oss_register_interrupts(void)
 109{
 110        irq_set_chained_handler(OSS_IRQLEV_IOPISM, oss_iopism_irq);
 111        irq_set_chained_handler(OSS_IRQLEV_SCSI,   oss_scsi_irq);
 112        irq_set_chained_handler(OSS_IRQLEV_NUBUS,  oss_nubus_irq);
 113        irq_set_chained_handler(OSS_IRQLEV_IOPSCC, oss_iopscc_irq);
 114        irq_set_chained_handler(OSS_IRQLEV_VIA1,   via1_irq);
 115
 116        /* OSS_VIA1 gets enabled here because it has no machspec interrupt. */
 117        oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1;
 118}
 119
 120/*
 121 * Enable an OSS interrupt
 122 *
 123 * It looks messy but it's rather straightforward. The switch() statement
 124 * just maps the machspec interrupt numbers to the right OSS interrupt
 125 * source (if the OSS handles that interrupt) and then sets the interrupt
 126 * level for that source to nonzero, thus enabling the interrupt.
 127 */
 128
 129void oss_irq_enable(int irq) {
 130        switch(irq) {
 131                case IRQ_MAC_SCC:
 132                        oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
 133                        return;
 134                case IRQ_MAC_ADB:
 135                        oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM;
 136                        return;
 137                case IRQ_MAC_SCSI:
 138                        oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
 139                        return;
 140                case IRQ_NUBUS_9:
 141                case IRQ_NUBUS_A:
 142                case IRQ_NUBUS_B:
 143                case IRQ_NUBUS_C:
 144                case IRQ_NUBUS_D:
 145                case IRQ_NUBUS_E:
 146                        irq -= NUBUS_SOURCE_BASE;
 147                        oss->irq_level[irq] = OSS_IRQLEV_NUBUS;
 148                        return;
 149        }
 150
 151        if (IRQ_SRC(irq) == 1)
 152                via_irq_enable(irq);
 153}
 154
 155/*
 156 * Disable an OSS interrupt
 157 *
 158 * Same as above except we set the source's interrupt level to zero,
 159 * to disable the interrupt.
 160 */
 161
 162void oss_irq_disable(int irq) {
 163        switch(irq) {
 164                case IRQ_MAC_SCC:
 165                        oss->irq_level[OSS_IOPSCC] = 0;
 166                        return;
 167                case IRQ_MAC_ADB:
 168                        oss->irq_level[OSS_IOPISM] = 0;
 169                        return;
 170                case IRQ_MAC_SCSI:
 171                        oss->irq_level[OSS_SCSI] = 0;
 172                        return;
 173                case IRQ_NUBUS_9:
 174                case IRQ_NUBUS_A:
 175                case IRQ_NUBUS_B:
 176                case IRQ_NUBUS_C:
 177                case IRQ_NUBUS_D:
 178                case IRQ_NUBUS_E:
 179                        irq -= NUBUS_SOURCE_BASE;
 180                        oss->irq_level[irq] = 0;
 181                        return;
 182        }
 183
 184        if (IRQ_SRC(irq) == 1)
 185                via_irq_disable(irq);
 186}
 187