/*

    Copyright (C) Corollary, Inc., 1993, 1994.
    All Rights Reserved.
    This Module contains Proprietary Information of
    Corollary, Inc., and should be treated as Confidential.

Module Name:

    cbus.c

Abstract:

    This module implements the initialization of the system dependent
    functions that define the Platform Specific Driver (PSD) for the
    MP Corollary machines under OS/2 2.x.

    This includes the Corollary C-bus II machines which use Corollary's
    CBC chips as well as Corollary C-bus I machines which use the Intel APIC.
    Hardware dependencies of each C-bus backend are isolated in their
    independent hardware modules.  This module is completely hardware
    independent.

Author:

    Landy Wang (landy@corollary.com) 11-Jan-1994

 */

#include <psd.h>
#include "cbustype.h"
#include "cbusrrd.h"            /* PSD <-> RRD interface definitions */
#include "cbus.h"               /* Cbus1 & Cbus2 max # of elements is here */
#include "cbus_sw.h"            /* Cbus OS/2-specific switch table */

#ifndef MIN
#define MIN(a,b)                (((a)>(b))?(b):(a))
#endif

EXT_CFG_OVERRIDE_T              CbusGlobal;

ULONG                           CbusProcessors;
ULONG                           CbusProcessorMask;
ULONG                           CbusActiveProcessors;
ULONG                           CbusActiveProcessorsMask;

/*
 * For Cbus1, the CbusCSR[] & CbusBroadcastCSR really point at
 * Cbus1 I/O space which can vary from platform to platform.
 *
 * For Cbus2, the CbusCSR[] & CbusBroadcastCSR really do point at
 * the Cbus2 CSR for the particular element.
 */
PVOID                           CbusBroadcastCSR;

/*
 * Cbus information table for all elements (an element may not
 * necessarily contain an x86 processor; ie: it may be a pure
 * I/O element).
 */
ELEMENT_T                       CbusCSR[MAX_CBUS_ELEMENTS];

MEMORY_CARD_T                   CbusMemoryBoards[MAX_ELEMENT_CSRS];
ULONG                           CbusMemoryBoardIndex;

RRD_CONFIGURATION_T             CbusJumpers;

EXT_ID_INFO_T                   CbusExtIDTable[MAX_CBUS_ELEMENTS];
ULONG                           CbusValidIDs;

extern ULONG                    CbusIrqPolarity;

ULONG				CbusEisaVectors[] = {

				        0xB8,	/* IRQ 0 - 8254 Timer   */
				        0xA0,	/* IRQ 1 - Keyboard     */
				        0x00,	/* IRQ 2 - not used     */
				        0xD1,	/* IRQ 3 - COM2         */
				
				        0xD0,	/* IRQ 4 - COM1         */
				        0xB0,	/* IRQ 5 -              */
				        0x78,	/* IRQ 6 - Floppy       */
				        0x40,	/* IRQ 7 -              */
				
				        0xC0,	/* IRQ 8 - RTC          */
				        0x88,	/* IRQ 9 - Free         */
				        0x60,	/* IRQ 10 - Free        */
				        0x98,	/* IRQ 11 - SCSI (98)   */
				
				        0x58,	/* IRQ 12 - PS2 mouse   */
				        0x4F,	/* IRQ 13 - DMA chain   */
				        0x9F,	/* IRQ 14 - Hard disk   */
				        0x38	/* IRQ 15               */
				};

/*
 * and for the reverse of the map above...
 */
UCHAR   			CbusVectorToIrq[MAX_VECTORS];

/*--------------- function prototypes start here  ----------------------*/

BOOL
CbusMPMachine(VOID);

ULONG
CbusQueryInterruptPolarity();

VOID
CbusMemoryFree(ULONG, ULONG);

VOID
CbusIOPresent(ULONG, ULONG, ULONG, ULONG, ULONG, PVOID);

VOID
i486cacheon(VOID);

/*--------------- function prototypes end here  ----------------------*/

LONG
CbusStrLen(
IN PUCHAR       Str
)
{
	LONG	length = 0;

	while (*Str++)
		length++;

	return length;
}

PUCHAR
CbusFindString (
IN PUCHAR       Str,
IN PUCHAR       StartAddr,
IN LONG         Len
)

/*

Routine Description:

    Searches a given virtual address for the specified string
    up to the specified length.

Arguments:

    Str - Supplies a pointer to the string

    StartAddr - Supplies a pointer to memory to be searched

    Len - Maximum length for the search

Return Value:

    Pointer to the string if found, 0 if not.

 */

{
        LONG    Index, n;

        for (n = 0; Str[n]; ++n)
                ;

        if (--n < 0) {
                return StartAddr;
        }

        for (Len -= n; Len > 0; --Len, ++StartAddr) {
                if ((StartAddr[0] == Str[0]) && (StartAddr[n] == Str[n])) {
                        for (Index = 1; Index < n; ++Index)
                                if (StartAddr[Index] != Str[Index])
                                        break;
                        if (Index >= n) {
                                return StartAddr;
                        }
                }
        }

        return (PUCHAR)0;
}

ULONG
CbusReadExtIDs(
IN PEXT_ID_INFO From,
IN PEXT_ID_INFO To
)

/*

Routine Description:

    Read in the C-bus II extended id information table.

Arguments:

    From - Supplies a pointer to the RRD source table

    To - Supplies a pointer to the destination storage for the table

Return Value:

    Number of valid table entries.

 */

{
        ULONG Index = 0;
        ULONG ValidEntries = 0;

        for ( ; Index < MAX_CBUS_ELEMENTS && From->id != LAST_EXT_ID; Index++) {

                /*
                 * we cannot skip blank RRD entries, ie: can't do this:
                 *
                 * if (From->pm == 0 && From->io_function == IOF_INVALID_ENTRY)
                 *                      continue;
                 */

                bcopy((PVOID)From, (PVOID)To, sizeof(EXT_ID_INFO_T));

                From++;
                To++;
                ValidEntries++;
        }

        /*
         *  WARNING: this is not necessarily the number of valid CPUs !!!
         */
        return ValidEntries;
}

/*

Routine Description:

    Map a given processor's CSR space and save an idp pointer as well.

Arguments:

    Processor - Supplies a logical processor number

    Idp - Supplies an RRD extended ID pointer for this processor element

Return Value:

    Opaque pointer to this processor's CSR space.c

 */
PVOID
CbusMappings(
IN ULONG                Processor,
IN PEXT_ID_INFO         Idp
)
{
        /*
         * RRD specifies how much to map in per processor - this
         * will usually be just 8K of the 64K CSR space for Cbus2.
         * For Cbus1, RRD must give us a size which includes any
         * processor-specific registers the PSD may access,
         * generally indicated via the CbusGlobal structure.
         */
        CbusCSR[Processor].csr = (PVOID)psd_map (
                (ULONG)Idp->pel_start, Idp->pel_size);

        if (!CbusCSR[Processor].csr)
                printf("could not map processor %d csr\n", Processor);

        CbusCSR[Processor].idp = (PVOID)Idp;

        return CbusCSR[Processor].csr;
}

/*

Routine Description:

    Maps a given RRD entry into the PSD's memory board structures

Arguments:

    Idp - Supplies a pointer to the RRD extended information structure entry

 */
VOID
CbusMapMemoryRegisters(
IN PEXT_ID_INFO Idp
)
{
        PMEMORY_CARD    pm;

        pm = &CbusMemoryBoards[CbusMemoryBoardIndex];

        pm->io_attr = (ULONG)Idp->io_attr;
        pm->regmap = (PULONG)psd_map ((ULONG)Idp->io_start, Idp->io_size);

        if (pm->regmap)
                CbusMemoryBoardIndex++;
        else
                printf("PSD: could not map memory board registers\n");
}

/*

Routine Description:

    Parse the given RRD extended ID configuration table, and construct
    various PSD data structures accordingly.

        note that psd_map() address & length parameters are in bytes.
        Mappings obtained with it at CbusProbe() time are valid to
        use throughout the life of the kernel on any CPU.  Mappings obtained
        with it at other times are not necessarily visible to all CPUs.
        Note that each CPU has his own PDE at all times.

Arguments:

    Table - Supplies a pointer to the RRD extended information table

    Count - Supplies a count of the maximum number of entries.
 */

VOID
CbusEstablishMaps(
IN PEXT_ID_INFO Table,
IN ULONG Count
)
{
        ULONG                   Index, processor = 0;
        PEXT_ID_INFO            Idp = Table;
        PUCHAR                  csr;

        for (Index = 0; Index < Count; Index++, Idp++) {

                /*
                 *  Map in the broadcast CSR.  Note this is not a processor.
                 */

                if (Idp->id == CbusGlobal.broadcast_id) {
                        CbusBroadcastCSR = (PVOID)psd_map(
                                (ULONG)Idp->pel_start,
                                        Idp->pel_size);

                        if (!CbusBroadcastCSR)
                                printf("PSD: cannot map the broadcast CSR\n");

                        continue;
                }


                /*
                 *  Establish virtual maps for each processor
                 */
                if (Idp->pm) {

                        if ((UCHAR)Idp->id == CbusGlobal.bootid) {
                                CbusMappings(0, Idp);
                        }
                        else {
                                /*
                                 * We have an additional processor - set up
                                 * his maps and put him in reset.  We will
                                 * boot him shortly.
                                 */

                                processor++;

                                csr = (PUCHAR)CbusMappings(processor, Idp);

                                COUTB(csr, CbusGlobal.smp_sreset,
                                        CbusGlobal.smp_sreset_val);

                                /*
                                 * turn off his LED too -
                                 * this way he can turn it on when he boots.
                                 */
                                COUTB(csr, CbusGlobal.smp_cled,
                                        CbusGlobal.smp_cled_val);
                        }
                }

                /*
                 *  Establish virtual maps for each I/O and/or
                 *  memory board.  Note that I/O devices may or may
                 *  not have an attached processor - a CPU is NOT required!
                 *  memory, on the other hand, may NOT have a processor
                 *  on the same board.
                 */

                switch (Idp->io_function) {
                        case IOF_INVALID_ENTRY:
                        case IOF_NO_IO:
                                break;

                        case IOF_MEMORY:
                                /*
                                 * If not a processor, must be a memory card.
                                 * Add this memory card to our list
                                 * of memory cards in the machine.
                                 */

                                if (Idp->pm)
                                        break;

                                CbusMapMemoryRegisters(Idp);

                                /*
                                 * Add this memory range to our list
                                 * of additional memory to free later.
                                 */

                                CbusMemoryFree(Idp->pel_start, Idp->pel_size);
                                break;

                        default:
                                /*
                                 * Add this I/O functionality to our table
                                 * to make available to Cbus hardware drivers.
                                 * Since I/O card interpretation of the RRD
                                 * table is strictly up to the driver, do not
                                 * try to register any of this element's space
                                 * in the PSD's public consumption list, as we
                                 * do for memory and processor cards.
                                 */

                                if (Idp->pel_features & ELEMENT_HAS_IO) {

                                        /*
                                         * If there was an attached processor,
                                         * we already mapped in the CSR.  If
                                         * no attached processor, map in the
                                         * CSR now.  We'll need it later for
                                         * interrupt vector enabling.
                                         */

                                        if (Idp->pm == 0) {
                                                csr = (PUCHAR)psd_map(
                                                        (ULONG)Idp->pel_start,
                                                        Idp->pel_size);

                                                if (!csr) {
                                                        printf("PSD: cannot map I/O element\n");
                                                }
                                        }

                                        CbusIOPresent(
                                                (ULONG)Idp->id,
                                                (ULONG)Idp->io_function,
                                                (ULONG)Idp->io_attr,
                                                Idp->pel_start,
                                                Idp->pel_size,
                                                (PVOID)csr );
                                }
                                break;
                }
        }

        /*
         * Set total number of processors and their global mask
         */

        CbusProcessors = processor + 1;
        CbusProcessorMask = (1 << CbusProcessors) - 1;

        (*CbusBackend->InitializePlatform)();
}

static ULONG RRDextsignature[] = { 0xfeedbeef, 0 };

static ULONG RRDsignature[] = { 0xdeadbeef, 0 };

static UCHAR CorollaryOwns[] = "Copyright(C) Corollary, Inc. 1991. All Rights Reserved";

/*

    Routine Description:

    For robustness, we check for the following before concluding that
    we are indeed a Corollary C-bus I or C-bus II licensee supported
    in multiprocessor mode under this PSD:

    a) Corollary string in the BIOS ROM 64K area                (0x000F0000)
    b) Corollary string in the RRD RAM/ROM 64K area             (0xFFFE0000)
    c) 2 Corollary extended configuration tables
                 in the RRD RAM/ROM 64K area                    (0xFFFE0000)

    If any of the above checks fail, it is assumed that this machine
    is either a non-Corollary machine or an early Corollary machine
    not supported by this PSD.  Both of these types of machines are,
    however, supported (in uniprocessor mode) by the standard
    uniprocessor PSD.

    If the above checks succeed, then we proceed to fill in various
    configuration structures for later use.
 */
BOOL
CbusReadRRD()
{
        PEXT_CFG_HEADER         p;
        PVOID                   s;
        ULONG                   OverrideLength = 0, EntryLength;
        PUCHAR                  Bios;

        /*
         * Map in the 64K of BIOS ROM @ 0xF0000 and scan it for our signature...
         */
        Bios = (PUCHAR)psd_map((ULONG)0xF0000, 0x10000);

        if (!Bios)
                return FALSE;

        if (!CbusFindString((PUCHAR)"Corollary", Bios, (LONG)0x10000)) {
                /*
                 * not really a Corollary machine at all
                 */
                psd_unmap((caddr_t)Bios, 0x10000);
                return FALSE;
        }
        psd_unmap(Bios, 0x10000);

        /*
         * Map in the 64K of RRD @ 0xFFFE0000 and scan it for our signature.
         */
        Bios = (PUCHAR)psd_map((ULONG)0xFFFE0000, 0x10000);

        if (!Bios)
                return FALSE;

        if (!CbusFindString((PUCHAR)"Corollary", Bios, (LONG)0x10000)) {
                /*
                 * not really a Corollary machine at all
                 */
                psd_unmap(Bios, 0x10000);
                return FALSE;
        }
        psd_unmap(Bios, 0x10000);

        /*
         * Map in the 32K of RRD RAM information @ 0xE0000.
         */
        Bios = (PUCHAR)psd_map((ULONG)RRD_RAM, 0x8000);

        if (!Bios)
                return FALSE;

        if (!CbusFindString(CorollaryOwns, Bios, (LONG)0x8000)) {
                /*
                 * an obsolete Corollary machine (of the assymetric flavor)
                 */
                psd_unmap((caddr_t)Bios, 0x8000);
                return FALSE;
        }

        /*
         * At this point, we are assured that it is indeed a
         * Corollary architecture machine.  Search for our
         * extended configuration tables, note we still search for
         * the existence of our earliest 'configuration' structure,
         * ie: the 0xdeadbeef version.  This is not to find out where
         * the memory is, but to find out which Cbus1 megabytes have been
         * 'jumpered' so that I/O cards can use the RAM address(es) for
         * their own dual-ported RAM buffers.  This early configuration
         * structure will not be present in Cbus2.
         *
         * If there is no extended configuration structure,
         * this must be an old rom.  NO SUPPORT FOR THESE.
         */

        s = (PVOID)CbusFindString((PUCHAR)RRDsignature, Bios, (LONG)0x8000);

        if (s) {
                bcopy((PVOID)s, (PVOID)&CbusJumpers, JUMPER_SIZE);
        }
#ifdef DEBUG
        else {
                /*
                 * RRD configuration is not expected on Cbus2, but is for Cbus1
                 */
                printf("PSD: No RRD ROM configuration table\n");
        }
#endif

        /*
         * Now go for the extended configuration structure which will tell
         * us about memory, processors and I/O devices.
         */

        p = (PEXT_CFG_HEADER)CbusFindString((PUCHAR)RRDextsignature,
                                         Bios, (LONG)0x8000);

        if (!p) {
                printf("PSD: Obsolete machine (No RRD extended configuration tables\n");
                goto bailwitherror;
        }

        /*
         * Read in the 'extended ID information' table which,
         * among other things, will give us the processor
         * configuration.
         *
         * Multiple structures are strung together with a "checkword",
         * "length", and "data" structure.  The first null "checkword"
         * entry marks the end of the extended configuration
         * structure.
         *
         * We are only actively reading two types of structures, and
         * they MUST be in the following order, although not necessarily
         * consecutive:
         *
         *      - ext_id_info
         *
         *      - ext_cfg_override
         *
         * We ignore all other extended configuration entries built
         * by RRD - they are mainly for early UNIX kernels.
         */

        do {
                EntryLength = p->ext_cfg_length;

                switch (p->ext_cfg_checkword) {

                case EXT_ID_INFO:

                        CbusValidIDs = CbusReadExtIDs((PEXT_ID_INFO)(p+1),
                                                (PEXT_ID_INFO)CbusExtIDTable);

                        break;

                case EXT_CFG_OVERRIDE:
                        /*
                         * We just copy the size of the structures
                         * we know about.  If an rrd tries to pass us
                         * more than we know about, we ignore the
                         * overflow.  Underflow is interpreted as
                         * "this must be a pre-XM machine", and such
                         * machines must default to the standard OS/2
                         * uniprocessor PSD.
                         */

                        if (EntryLength < sizeof(EXT_CFG_OVERRIDE_T)) {
                                printf("PSD: Obsolete machine (Ancient RRD tables)\n");
                                goto bailwitherror;
                        }

                        OverrideLength = MIN(sizeof(EXT_CFG_OVERRIDE_T),
                                         EntryLength);

                        bcopy((PVOID)(p + 1), (PVOID)&CbusGlobal,
                                OverrideLength);

                        break;

                case EXT_CFG_END:

                        /*
                         * If ancient C-bus box, it's not supported in MP mode
                         */
                        if (CbusValidIDs == 0 || OverrideLength == 0) {
                                printf("PSD: Obsolete machine (Missing RRD tables)\n");
                                goto bailwitherror;
                        }

                        if (CbusMPMachine() == FALSE) {
                                printf("PSD: This Corollary machine is not supported under this PSD\n");
                                goto bailwitherror;
                        }

                        (*CbusBackend->ParseRRD)(CbusExtIDTable,
                                        &CbusValidIDs);


                        CbusEstablishMaps(CbusExtIDTable, CbusValidIDs);

                        psd_unmap((caddr_t)Bios, 0x8000);
                        return TRUE;

                default:
                        /*
                         * Skip unused or unrecognized configuration entries
                         */

                        break;
                }

                /*
                 * Get past the header, add in the length and then
                 * we're at the next entry.
                 */
                p = (PEXT_CFG_HEADER) ((PUCHAR)(p + 1) + EntryLength);

        } while (1);

        /*
         * only errors fall through to here...
         */

bailwitherror:
        psd_unmap((caddr_t)Bios, 0x8000);
        return FALSE;
}

VOID
CbusBuildVectorToIrqs()
{
        ULONG  irqline;

        /*
         * initialize all entries to invalid irqlines
         */
        for (irqline = 0; irqline < MAX_VECTORS; irqline++) {
                CbusVectorToIrq[irqline] = NO_IRQLINE;
        }

        /*
         * validate each EISA irqline in the system - don't
         * include the spurious or IPI vectors in our map.
         */
        for (irqline = 0; irqline < EISA_IRQLINES; irqline++) {
                CbusVectorToIrq[CbusEisaVectors[irqline]] = (UCHAR)irqline;
        }
}

/*
 *    - this routine reads in the global RRD information which pertains
 *      to the entire system, including all the processors.  if the
 *      machine is not really a Corollary/Corollary-clone machine, then
 *      this routine returns FALSE.
 */
BOOL
CbusInitializePlatform()
{
        /*
         * Find and store our signature and configuration information
         */
        if (CbusReadRRD() == FALSE)
                return FALSE;

        /*
         * get the EISA interrupt polarities (ie: edge vs level triggering).
         */
        CbusIrqPolarity = CbusQueryInterruptPolarity();

        /*
         * from our irqline-to-vector table, create a vector-to-irqline
         * reverse mapping table so we can easily identify which IRRs/ISRs
         * to read for GetMask calls later.
         */
        CbusBuildVectorToIrqs();

        return TRUE;
}

/*
 * Called by each processor in turn, to clear any software
 * structures prior to disabling himself, typically for shutdown.
 * It is presumed that a processor killing himself will not
 * be resurrected without a reboot.
 */
VOID
CbusKillProcessor(
IN ULONG Processor
)
{
        ULONG   ProcessorBit;

        ProcessorBit = (1 << Processor);

        CbusActiveProcessors -= 1;
        CbusActiveProcessorsMask &= ~ProcessorBit;
}

/*
 * Called by each processor in turn, to initialize himself.
 * CbusInitializePlatform() has already set up all of the element
 * space and set up _global_ maps for each processor's CSR.
 * each CPU will enable his incoming IPI here, and disable all other interrupts,
 * including all of this CPU's half-card CBC I/O interrupts.
 */
VOID
CbusInitializeProcessor(
IN ULONG Processor
)
{
        ELEMENT_T               ProcessorData;
        PEXT_ID_INFO            Idp;

        /*
         * turn on this processor's LED
         */
        COUTB(CbusCSR[Processor].csr, CbusGlobal.smp_sled,
                        CbusGlobal.smp_sled_val);

        ProcessorData = CbusCSR[Processor];

        Idp = (PEXT_ID_INFO)ProcessorData.idp;

        /*
         * Only enable 486 internal caches here because the PSD gets the
         * processor straight from reset.  For Cbus2, the PSD will get
         * the processor via a ROM, and the ROM will be responsible for
         * turning on the cache (and flushing it).
         */
        if (Idp->proc_attr == PA_CACHE_ON) {
                /*
                 * Enable this processor's internal cache - do this
                 * before clksetup() so that the delay-execution calibration
                 * will be accurate.
                 */
                i486cacheon();
        }

        /*
         * Initialize this processor's data - done ONCE by each processor.
         *
         * Typically, this processor's interrupt controller is initialized
         * here.  Also, if it's the first processor, any I/O interrupt
         * controllers will also be initialized here, ie: EISA bridges or
         * I/O APICs.
         */

        (*CbusBackend->InitializeCPU)(Processor);

        CbusActiveProcessors += 1;
        CbusActiveProcessorsMask = (1 << CbusActiveProcessors) - 1;
}

/*
 * Mask for valid bits of edge/level control register (ELCR) in 82357 ISP:
 * ie: ensure irqlines 0, 1, 2, 8 and 13 are always marked edge, as the
 * I/O register will not have them set correctly.  All other bits in the
 * I/O register will be valid without us having to poke them.
 */
#define ELCR_MASK               0xDEF8

#define PIC1_ELCR_PORT          (PUCHAR)0x4D0   /* ISP edge/level control regs */
#define PIC2_ELCR_PORT          (PUCHAR)0x4D1

/*

    Called once to read the EISA interrupt configuration registers.
    This will tell us which interrupt lines are level-triggered and
    which are edge-triggered.  Note that irqlines 0, 1, 2, 8 and 13
    are not valid in the 4D0/4D1 registers and are defaulted to edge.

Return Value:

    The interrupt line polarity of all the EISA irqlines in the system.

 */
ULONG
CbusQueryInterruptPolarity()
{
        ULONG   InterruptLines = 0;

        /*
         * Read the edge-level control register (ELCR) so we'll know how
         * to mark each driver's interrupt line (ie: edge or level triggered).
         * in the APIC I/O unit redirection table entry.
         */

        InterruptLines = ( ((ULONG)inb(PIC2_ELCR_PORT) << 8) |
                           ((ULONG)inb(PIC1_ELCR_PORT)) );

        /*
         * Explicitly mark irqlines 0, 1, 2, 8 and 13 as edge.  Leave all
         * other irqlines at their current register values.
         */

        InterruptLines &= ELCR_MASK;

        return InterruptLines;
}

#define CBUS1_NMI_MASK          (PUCHAR)0x70
#define CBUS1_IO_CHANNEL_CHECK  (PUCHAR)0x61

/*

Routine Description:

    This function clears the NMI on the EISA bus.  Typically this
    was generated by one of Corollary's "NMI cards", used for
    debugging purposes.
 */
VOID
CbusClearEISANMI(VOID)
{
        UCHAR   IoChannelCheck;

        outb(CBUS1_NMI_MASK, (UCHAR)0);

        IoChannelCheck = inb(CBUS1_IO_CHANNEL_CHECK);
        outb(CBUS1_IO_CHANNEL_CHECK, (UCHAR)((IoChannelCheck & 0xF) | 0x08));

        IoChannelCheck = inb(CBUS1_IO_CHANNEL_CHECK);
        outb(CBUS1_IO_CHANNEL_CHECK, (UCHAR)(IoChannelCheck & 0x7));

        /*
         * Since the NMI we are clearing was caused by pressing the button,
         * which generated an EISA NMI (not a Cbus NMI), don't clear the
         * NMI in Cbus space.
         *
         * COUTB(CbusCSR[Processor].csr, CbusGlobal.smp_cnmi,
         *      CbusGlobal.smp_cnmi_val);
         */
}

/*
 *      Called to disable 8259 input lines as we switch into full
 *      distributed interrupt chip mode.  our distributed interrupt
 *      chip logic in the CBC will handle all interrupts from this
 *      point on.  the only reason we have to leave irq0 enabled is
 *      because Intel's EISA chipset doesn't leave an external irq0
 *      clock line for us to wire into the CBC.  hence, we have wired
 *      it from the 8259 into the CBC, and must leave it enabled in
 *      the 8259 IMR.  note that we will never allow the now passive
 *      8259 to respond to a CPU INTA cycle, but we do need to see the
 *      interrupt ourselves in the CBC so we can drive an appropriate
 *      vector during the INTA.
 *
 *      For the Cbus1 architecture, we mask off ALL the interrupts coming
 *      from the EISA chipset, since timers are generated by each local APIC.
 *
 *      This is the ONLY place in the Corollary PSD (and all of OS/2!)
 *      where the 8259s are accessed.
 *
 * Arguments:
 *
 *    Mask to put on the master and slave 8259.
 */
VOID
CbusDisable8259s(
USHORT  mask
)
{
        outb(MIMR_PORT, (CHAR)mask);
        outb(SIMR_PORT, (CHAR)(mask >> 8));
}

/*
 * called during an NMI, let the backend hardware handler resolve it.
 * no stis, irets or lock acquisitions are allowed.  this function is
 * called by OS/2 with cli protection, and no kernel-mode preemptions
 * can therefore happen underneath us while we are executing here.
 */
ULONG
CbusResolveNMI()
{
        return (*CbusBackend->ResolveNMI)();
}
