
/*
 * THIS PSD WILL ONLY WORK WITH IBMINT13.I13.
 * The Tricord device drivers reprogram the PIC and screw things up.
 *
 * Defect 80130 - added support so that the psd is compatible with
 *                Tricord's IOOP device driver.
 */

/*
** PSD : TRICRD.C (TRICRD.PSD)
**
**       This PSD provides the platform specific code for interfacing
**       to the Tricord Model 30.
**
**/

// BUGBUG - added IPEs
// BUGBUG IO delay - need to import
// TESTCASE - switch master processor to different slots.
// TESTCASE - try only one CPU
// BUGBUG - SETTLE SOME CONFIG ISSUES W/ TRICORD CONCERNING GLOBAL IRQ ACCESS
//

#define INCL_ERROR_H

#include <os2.h>
#include <psd.h>
#include <int.h>
#include <tricrd.h>

/*
 *  Global Variables
 */
ulong_t  IPISent = 0;
ulong_t  IRQTable[] = {23,22,21,20,19,18,17};
ulong_t  NumberOfCPUs = 0;    // Set by Init()
ulong_t  sizePLMA = 0;        // Set by Install()
ulong_t *CPUNumber;           // Set by ProcInit()
ulong_t  CPUsInited = 0;      // Incremented by ProcInit()
ulong_t  NOPNotReceived = 1;  // Cleared by NOPHandler() & set by GenIPI()
int      IODelayCount = 30;   // BUGBUG - needs to be imported
char    *pParmString = 0;     // Set by Install()

S_CPUINFO CPUINFO[MAX_CPUS];  // Filled in by Init()
P_F_2    router = 0;          // Set by Install()

ulong_t NOPHandler (void);
ulong_t PIC3_Slave_IRQ_Handler (void);
void    InitPIC (void);
ulong_t GenIPI (ulong_t procnum);
ulong_t EndIPI (ulong_t procnum);
ulong_t IPIPresent (void);
ulong_t InByte (ulong_t port);
ulong_t InWord (ulong_t port);
void    OutByte (ulong_t port, ulong_t data);
void    OutWord (ulong_t port, ulong_t data);
ulong_t WhatIOBaseAmI ();
ulong_t GetCPUInfo (ulong_t Base,
                    ulong_t *pProcessorType,
                    ulong_t *pFlags,
                    ulong_t *Irq,
                    ulong_t *Features);
void GetCPUTypeAndFlags (ulong_t Base,
                         ulong_t *ProcessorType,
                         ulong_t *Flags);

/***  Install - Install PSD
 *
 *    This function checks to see if this PSD is installable on the
 *    current platform.
 *
 *    1) The BIOS segment is searched for the "Tricord' string.
 *    2) EISA port is cehcked for correct product ID.
 *    3) Install data is saved.
 *
 *    ENTRY   pinstall - pointer to an INSTALL structure
 *
 *    EXIT    NO_ERROR - PSD Installed
 *            -1       - PSD not valid for this platform
 */

#define TRICORD_STRING_LENGTH   7      // "Tricord"

ulong_t Install(INSTALL register *pinstall) {

   ulong_t Id;
   ulong_t i;
   ulong_t rc;
   char register *p;
   VMALLOC  vmac;

   /*
    * Set up global variables
    */

   router      = pinstall->pPSDHlpRouter;
   pParmString = pinstall->pParmString;
   CPUNumber   = (void *)pinstall->pPSDPLMA;
   sizePLMA    = pinstall->sizePLMA;

   /*
    * Search BIOS data segment for the string "Tricord".
    * First map the BIOS segment.
    */

   vmac.addr   = BIOS_SEG << 4;
   vmac.cbsize = BIOS_SEG_SIZE;
   vmac.flags  = VMALLOC_PHYS;

   if (PSDHelp(router, PSDHLP_VMALLOC, &vmac) == NO_ERROR)
   {
      p = (char *)vmac.addr;

      rc = -1;        // assume string not found

      for (i = 0 ; i <= BIOS_SEG_SIZE-TRICORD_STRING_LENGTH ; i++)
      {
        if (p[i] == 'T' && p[i+1] == 'r' && p[i+2] == 'i' && p[i+3] == 'c'
                        && p[i+4] == 'o' && p[i+5] == 'r' && p[i+6] == 'd')
        {
          /*
           * Check the EISA port ID to see if we have a Tricord Model 30.
           */

          Id = InByte(EISA_PRODUCT_ID_PORT3);
          if (Id == 2 || Id == 3)
          {
            rc = NO_ERROR;
          }
          break;
        }
      }

      if (PSDHelp(router, PSDHLP_VMFREE, vmac.addr) != NO_ERROR)
      {
        _asm int 3;
        //BUGBUG IPE here
      }
   }
   else
   {
     _asm int 3;
     //BUGBUG - IPE here
   }

   return (rc);
}

/***  DeInstall - DeInstall PSD
 *
 *    This function deinstalls the PSD.
 *
 *    ENTRY   None
 *
 *    EXIT    NO_ERROR
 *
 */

ulong_t DeInstall(void) {

   return (NO_ERROR);
}

/***  Init - Initialize the PSD
 *
 *    This function initalizes the PSD.
 *
 *    - Inits the master CPU
 *        - Gets its base IO addr
 *        - Gets the CPU type
 *
 *    - Register an IPI interrupt handler
 *    - Set init flags
 *
 *    ENTRY   pinit - pointer to INIT structure
 *
 *    EXIT    NO_ERROR - PSD initialized
 *            rc from    PSDHelp (PSDHLP_VMALLOC)
 *            rc from    PSDHelp (PSDHLP_SET_IRQ)
 */

ulong_t Init(INIT *pinit) {

   ulong_t rc = NO_ERROR;
   ulong_t i,j;
   ulong_t Base;
   VMALLOC vmac;
   SET_IRQ irq;
   char Pic1savedMask;
   char Pic2savedMask;

   /*
    * If this is a Tricord Model 30, then we are on the master CPU.
    *
    * (1) Determine what slot we (master) are on (base IO address).
    *     i.e 0x9000, 0xA000, ... 0xF000
    * (2) Get master CPU info.
    * (3) Disable Motherboard external floating point interrupts.
    * (4) Get info for the slave CPUs.
    */

   CPUINFO[0].SlotBaseIOAddr = WhatIOBaseAmI();                     // (1)
   NumberOfCPUs++;       // Increment from 0 to 1

   if (! GetCPUInfo (CPUINFO[0].SlotBaseIOAddr,                     // (2)
                     &CPUINFO[0].ProcessorType,
                     &CPUINFO[0].Flags,
                     &CPUINFO[0].Irq,
                     &CPUINFO[0].Features))
   {
     OutByte (MOTHERBOARD_CTRL_REG,                                 // (3)
              InByte(MOTHERBOARD_CTRL_REG) & ~MBCR_NPX_INT);

     /*
      * If the master is a MP CPU, then check that all slave CPUs are MP.
      * If they are MP, then do some setup.
      */

     for (Base =  CPU_BASE_SLOT_BASE_IO_ADDR ;
          Base <= CPU_MAX_SLOT_BASE_IO_ADDR ;
          Base += 0x1000)
     {
       if (CPUINFO[0].SlotBaseIOAddr != Base)      // if ! MASTER CPU
       {
         if (! GetCPUInfo (Base,
                           &CPUINFO[NumberOfCPUs].ProcessorType,
                           &CPUINFO[NumberOfCPUs].Flags,
                           &CPUINFO[NumberOfCPUs].Irq,
                           &CPUINFO[NumberOfCPUs].Features))
         {
           if (CPUINFO[NumberOfCPUs].Flags & MP_CPU)   // if MP CPU
           {
             CPUINFO[NumberOfCPUs].SlotBaseIOAddr = Base;

             NumberOfCPUs++;
           }
           else
           {
             _asm int 3;  // BUGBUG not an MP CPU.
           }
         }
       }
     }

     for (i = 0 ; i < 256 ; i++)
     {
       OutWord (CPUINFO[0].SlotBaseIOAddr+NOP_VECTOR_REGISTER,
                (NOP_VECTOR << 8) | i);
       for (j = 1 ; j < NumberOfCPUs ; j++)
       {
          OutWord (CPUINFO[j].SlotBaseIOAddr+NOP_VECTOR_REGISTER,
                   (NOP_VECTOR << 8) | i);
       }
       IODelay;

       for (j = 1 ; j < NumberOfCPUs ; j++)
       {
          if ( ((CPUINFO[j].Irq & 0x0f) +  PIC3_BASE ) == i)
          {
            OutByte ( CPUINFO[0].SlotBaseIOAddr+INTERRUPT_MASK_REGISTER ,0x00);
            OutByte ( CPUINFO[j].SlotBaseIOAddr+INTERRUPT_MASK_REGISTER ,0x80);
          }
          else
          {
            OutByte ( CPUINFO[j].SlotBaseIOAddr+INTERRUPT_MASK_REGISTER ,0x00);
            OutByte ( CPUINFO[0].SlotBaseIOAddr+INTERRUPT_MASK_REGISTER ,0x80);
          }
       }
       IODelay;
     }

     /*
      * Save init data
      */

//     pinit->flags = 0;
// 76618    pinit->flags = INIT_GLOBAL_IRQ_ACCESS | INIT_USE_FPERR_TRAP;
     pinit->flags = INIT_USE_FPERR_TRAP;
     pinit->version = VERSION;

     /*
      * We will init the PICS, save the current masks so we can restore them
      */

     Pic1savedMask=InByte(PIC1_PORT1);
     Pic2savedMask=InByte(PIC2_PORT1);

     InitPIC();

     /*
      * Restore the masks, enable the slave IRQ for cascading to the 3rd PIC.
      */

     OutByte(PIC1_PORT1, Pic1savedMask & ~(1 << PIC3_SLAVE_IRQ));
     OutByte(PIC2_PORT1, Pic2savedMask);
     OutByte(PIC1_PORT0, OCW2_SET_PRIORITY + 4);// Make IRQ 4 the lowest
                                                // priority IRQ so that
                                                // IPIs coming thru IRQ 5
                                                // don't get locked out when
                                                // we try to IPI after handling
                                                // an IRQ 8. (i.e. reschedule)

     /*
      * Setup Interrupt Handlers for the IPIs and NOP vector.
      */

     for (i = 0 ; i < (sizeof(IRQTable) / sizeof(ulong_t)) ; i++)
     {
        int Match;

        Match = 0;
        for ( j = 0; j < NumberOfCPUs; j++)
        {
            if ( CPUINFO[j].Irq == IRQTable[i] )
            {
               Match++;
               break;
            }
        }

       if ( Match != 0 )
       {
          irq.irq     = CPUINFO[j].Irq;
          irq.flags   = IRQf_IPI;
          irq.vector  = PIC3_BASE + (CPUINFO[j].Irq & 0x0F);
          irq.handler = (P_F_2)IPIPresent;
       }
       else
       {
          irq.irq     = IRQTable[i];
          irq.flags   = 0;
          irq.vector  = PIC3_BASE + 7 - i;
          irq.handler = (P_F_2)0;
       }

       if (PSDHelp (router,PSDHLP_SET_IRQ, &irq))
       {
         _asm int 3; // IPE here
       }
     }

     irq.irq     = NOP_IRQ;  // noop vector
     irq.flags   = IRQf_LSI;
     irq.vector  = NOP_VECTOR;
     irq.handler = (P_F_2)NOPHandler;

     if (PSDHelp(router,PSDHLP_SET_IRQ, &irq))
     {
        _asm int 3; // IPE here
     }

     /*
      * Setup dummy handler for IRQ 5 (cascade to 3rd PIC).
      * This is done so a if a device driver unset's the IRQ,
      * it doesn't mask it off.
      */

     irq.irq     = PIC3_SLAVE_IRQ;
     irq.flags   = 0;
     irq.vector  = 0;
     irq.handler = (P_F_2)PIC3_Slave_IRQ_Handler;

     if (PSDHelp(router,PSDHLP_SET_IRQ, &irq))
     {
        _asm int 3; // IPE here
     }
   }
   else
   {
     _asm int 3;   // BUGBUG IPE - master not a cpu ??
   }

   return (rc);
}

/***  GetNumOfProcs - Get number of processors
 *
 *    This function gets the number of processors which exist on this
 *    platform.
 *
 *    ENTRY   None
 *
 *    EXIT    Number of processors
 *
 */

ulong_t GetNumOfProcs(void) {

   ulong_t AllowableCPUs;

   AllowableCPUs = ( NumberOfCPUs > MAXCPUS ) ? MAXCPUS : NumberOfCPUs;

   return (AllowableCPUs);
}

/***  StartProcessor - Start a processor
 *
 *    This function starts a processor.
 *
 *    ENTRY   procnum - processor number to start (0-based)
 *
 *    EXIT    rc from GenIPI
 *
 */

ulong_t StartProcessor(ulong_t procnum) {

   ulong_t rc = NO_ERROR;

   /*
    * Start a processor by taking it out of reset.
    * First setup warmboot in CMOS.
    */

   if (procnum <= NumberOfCPUs)
   {
     OutByte (CMOS_ADDR, CMOS_CTRL);
     OutByte (CMOS_DATA, CMOS_WARM);
     OutWord (CPUINFO[procnum].SlotBaseIOAddr+FEATURE_CTRL_REG,
              CPUINFO[procnum].Features);
   }
   else
   {
     _asm int 3;  // BUGBUG - IPE here
   }
   return (rc);
}

/***  ProcInit - Processor initalization
 *
 *    This function initalizes per processor items.
 *
 *    NOTE: This function is called once on each processor
 *          in the system.
 *
 *    ENTRY   None
 *
 *    EXIT    NO_ERROR - Processor initialized
 *            rc from EndIPI
 */

ulong_t ProcInit(void) {

   /*
    * Set the logical processor number (zero based).
    */

   *CPUNumber = CPUsInited++;

   /*
    * Restore Feature Control Register.
    */

   OutWord (CPUINFO[*CPUNumber].SlotBaseIOAddr+FEATURE_CTRL_REG,
            CPUINFO[*CPUNumber].Features );

   if (*CPUNumber)
   {
      /*
       * Enable L1 cache
       */
      _asm mov eax, cr0;
      _asm and eax, 9fffffffh;  // clear bits in cr0 //BUGBUG
      _asm mov cr0, eax;
   }

   return (NO_ERROR);
}


/***  IPIPresent - Detects the presence of an IPI
 *
 *    This function detects the presence of an IPI on the current
 *    processor.
 *
 *    ENTRY   function - ignored
 *            arg      - ignored
 *
 *    EXIT    NO_ERROR - IPI present
 *            -1       - IPI not present
 *
 */

ulong_t IPIPresent (void) {

   ulong_t rc;

   rc = NO_ERROR;

   return (rc);
}

/***  PIC3_Slave_IRQ_Handler - Dummy handler for IRQ 5
 *
 *    This function does nothing. It is used to prevent a device driver
 *    from masking off the IRQ by unsetting it.
 *
 *    ENTRY   None
 *
 *    EXIT    -1 - Interrupt has not been handled
 *
 */

ulong_t PIC3_Slave_IRQ_Handler (void) {

   return (-1);
}

/***  NOPHandler - Handles NOP interrupt.
 *
 *    This function detects the presence of an IPI on the current
 *    processor.
 *
 *    ENTRY   function - ignored
 *            arg      - ignored
 *
 *    EXIT    NO_ERROR - IPI present
 *            -1       - IPI not present
 *
 */

ulong_t NOPHandler (void) {

   /*
    * Clear the NOP Not Recieved flag
    */

   NOPNotReceived = 0;

   _asm clc;

   return (NO_ERROR);
}

/***  GenIPI - Generate an inter-processor interrupt
 *
 *    This function generates an IPI.
 *
 *    ENTRY   procnum - processor number to interrupt (0-based)
 *
 *    EXIT    NO_ERROR
 *
 */

ulong_t GenIPI(ulong_t procnum) {

   ulong_t Fcr;
   ulong_t Isr; //#80130
   char Pic1savedMask;
   char Pic3savedMask;
   char Lowest; //#80130


//   OutByte(PIC3_PORT0, OCW3_READ_ISR);
//   IODelay;
//   Isr = InByte(PIC3_PORT0);
//   if (Isr)
//   {
//     _asm int 3;
//   }


   /*
    * IPIStart() disables interrupts. When sending IPI from master CPU
    * to a slave CPU, the master CPU must first receive the NOP_VECTOR.
    * Since IPIStart disables interrupts and spins, the master will
    * never get the NOP_VECTOR and the IPI will timeout.  To get around
    * this, we will mask interrupts at the PIC, except the IRQ used to
    * IPI the ACPU.  We will then enable interrupts.  After the NOP_VECTOR
    * is received, the NOP handler will fix....
    *
    */

   if (!*CPUNumber)       // if master IPIing a slave
   {
     IPISent = 0x00000001;     // IPI sent from processor 0
     Pic3savedMask=InByte(PIC3_PORT1);
     Pic1savedMask=InByte(PIC1_PORT1);
     OutByte (PIC3_PORT1, ~(1 << (CPUINFO[procnum].Irq & 0x0f)));
     OutByte (PIC1_PORT1, ~(1 << PIC3_SLAVE_IRQ));                //11011111
   }
   else
   {
     IPISent = 0x00000002;     // IPI sent from processor 1
   }

   Lowest = (CPUINFO[procnum].Irq & 0x0f) - 1;         // #80130
   OutByte(PIC3_PORT0, OCW2_SET_PRIORITY + Lowest);    //
                                                       // Make this
                                                       // IPI the
                                                       // highest
                                                       // priority

   /*
    * Toggling the interrupt bit of the Feature Control Register
    * will cause an interrupt to be sent to the processor.
    */

   Fcr = InWord (CPUINFO[procnum].SlotBaseIOAddr+FEATURE_CTRL_REG);
   Fcr &= ~FCR_INTR;
   OutWord (CPUINFO[procnum].SlotBaseIOAddr+FEATURE_CTRL_REG, Fcr);
   Fcr |= FCR_INTR;
   OutWord (CPUINFO[procnum].SlotBaseIOAddr+FEATURE_CTRL_REG, Fcr);


   if (!*CPUNumber)
   {
     _asm sti;                 // NOOP interrupt received here at master cpu
     while (NOPNotReceived);
     _asm cli;

     /*
      * Do a non-specific EOI to the 3rd PIC, the only IRQ unmasked are for
      * the IPIs.
      */

     OutByte (PIC3_PORT0, OCW2_NON_SPECIFIC_EOI);

// start #80130
// since we now fully nest the slave pics, we have to check
// if there is another interrupt in service on the 3rd pic
// before we EOI the master.

     IODelay;
     OutByte(PIC3_PORT0, OCW3_READ_ISR);
     IODelay;
     Isr = InByte(PIC3_PORT0);
     if (!Isr)
     {
       IODelay;
       OutByte(PIC1_PORT0, OCW2_NON_SPECIFIC_EOI);
     }
// end #80130

     OutByte (PIC3_PORT1, Pic3savedMask);
     OutByte (PIC1_PORT1, Pic1savedMask);
     IODelay;

     NOPNotReceived = 1;
   }

   return (NO_ERROR);
}

/***  EndIPI - End an inter-processor interrupt
 *
 *    This function ends an IPI.
 *
 *    ENTRY   procnum - processor number to end interrupt on (0-based)
 *
 *    EXIT    Return Code
 *
 */

ulong_t EndIPI(ulong_t procnum) {

   /*
    * If the processor is CPU 0, then we will need to EOI.  If the
    * processor is CPU 1, then the PIC has already been EOIed.
    */
   IPISent = 0;

   if (!procnum)
   {
     OutByte (PIC3_PORT0, OCW2_NON_SPECIFIC_EOI);
     OutByte (PIC1_PORT0, OCW2_NON_SPECIFIC_EOI);
   }
   return (NO_ERROR);
}

/***  InByte - Read a byte from a port
 *
 *    This function reads a byte from a specified port
 *
 *    ENTRY   port - port number to read from
 *
 *    EXIT    data read
 *
 */

ulong_t InByte(ulong_t port) {

   ulong_t data;

   _asm {
      mov   dx,port
      in    al,dx
      movzx eax,al
      mov   data,eax
   };

   return (data);
}


/***  InWord - Read a word from a port
 *
 *    This function reads a word from a specified port
 *
 *    ENTRY   port - port number to read from
 *
 *    EXIT    data read
 *
 */

ulong_t InWord(ulong_t port) {

   ulong_t data;

   _asm {
      mov   dx,port
      in    ax,dx
      movzx eax,ax
      mov   data,eax
   };

   return (data);
}

/***  OutByte - Writes a byte to a port
 *
 *    This function writes a byte to a specified port
 *
 *    ENTRY   port - port number to read from
 *            data - data to write
 *
 *    EXIT    None
 *
 */

void OutByte(ulong_t port, ulong_t data) {

   _asm {
      mov   dx,port
      mov   al,byte ptr data
      out   dx,al
   };

   return;
}


/***  OutWord - Writes a word to a port
 *
 *    This function writes a word to a specified port
 *
 *    ENTRY   port - port number to read from
 *            data - data to write
 *
 *    EXIT    None
 *
 */

void OutWord(ulong_t port, ulong_t data) {

   _asm {
      mov   dx,port
      mov   ax,word ptr data
      out   dx,ax
   };

   return;
}

ulong_t WhatIOBaseAmI()
{
  ulong_t register Base;

  for (Base =  CPU_BASE_SLOT_BASE_IO_ADDR ;
       Base <= CPU_MAX_SLOT_BASE_IO_ADDR ;
       Base += 0x1000)
  {
    /*
     * If a CPU is in the slot and its bus cycle is active, then it's me.
     */

    if ( ((InByte (EISA_PRODUCT_ID_PORT3+Base) & 0xf0) == 0x10)  // if CPU
         &&
         (InByte (CPU_STATUS_REGISTER+Base) & SR_SLOT_ACTIVE) )  // if active
    {
       return (Base);
    }
  }
  _asm int 3;  //BUGBUG IPE here - can't find processor slot !!!
}

ulong_t GetCPUInfo (ulong_t  Base,
                    ulong_t *pProcessorType,
                    ulong_t *pFlags,
                    ulong_t *Irq,
                    ulong_t *Features)
{
  ulong_t rc;

  if ( ((InByte (EISA_PRODUCT_ID_PORT3+Base) & 0xf0) == 0x10) )     // if CPU
  {
    *pFlags = 0;        //Init flags
    GetCPUTypeAndFlags (Base,
                        pProcessorType,
                        pFlags);


    *Irq = IRQTable[ (Base - CPU_BASE_SLOT_BASE_IO_ADDR) >> 12 ];

    *Features = InWord (Base+FEATURE_CTRL_REG) & ~(FCR_NPX_INT | FCR_RESET);
    if (*pFlags & L2_CPU)
    {
      *Features |= FCR_LOC_NMI;
    }

    rc = NO_ERROR;
  }
  else
  {
    rc = -1;   // ! a CPU
  }
  return (rc);
}

/***  GetCPUTypeAndFlags - Looks at EISA port to get CPU Type
 *
 *    This function Initalizes all three PICs.
 *
 *    NOTE : 3rd PIC is initialized differently than the 2nd PIC.
 *           Buffering is set differently.
 *
 *    ENTRY   None
 *
 *    EXIT    None
 *
 */

void GetCPUTypeAndFlags (ulong_t  Base,
                         ulong_t *ProcessorType,
                         ulong_t *Flags)
{
  switch (InByte(Base+EISA_PRODUCT_ID_PORT3))
  {
    case MP_i486_25:
    case MP_i486_33:
      *Flags  |= MP_CPU;

    case UNI_i486_25:
    case UNI_i486_33:
      *ProcessorType = PD_486;
      break;

    case MPC_i486_25:
    case MPC_i486_33:
    default:          // ********* UNKNOWN PROCESSOR TYPE ************
                      // assume its a 486 w/ L2 cache

      *ProcessorType = PD_486C;
      *Flags  |= (MP_CPU | L2_CPU);
      break;
  }
}

/***  Disable - Disable interrupts
 *
 *    This function disables interrupts, and returns
 *    the original state of eflags
 *
 *    ENTRY   None
 *
 *    EXIT    EFLAGS
 *
 */

ulong_t Disable(void) {

   ulong_t eflags;

   _asm {
      pushfd
      pop   eax
      mov   eflags,eax
      cli
   };

   return (eflags);
}


/***  Enable - Restore the state of eflags
 *
 *    This function restores the state of eflags
 *
 *    ENTRY   eflags - state of eflags to restore
 *
 *    EXIT    None
 *
 */

void Enable(ulong_t eflags) {

   _asm {
      push  eflags
      popfd
   };

   return;
}



/***  InitPIC - Initalize PICS.
 *
 *    This function Initalizes all three PICs.
 *
 *    NOTE : 3rd PIC is initialized differently than the 2nd PIC.
 *           Buffering is set differently.
 *
 *    ENTRY   None
 *
 *    EXIT    None
 *
 */

void InitPIC(void) {

  /* Initialize master PIC */

  OutByte(PIC1_PORT0, ICW1);                 // defines in tricrd\int.h
  IODelay;
  OutByte(PIC1_PORT1, MASTER_ICW2);
  IODelay;
  OutByte(PIC1_PORT1, MASTER_ICW3);
  IODelay;
// #80130 OutByte(PIC1_PORT1, ICW4);
  OutByte(PIC1_PORT1, ICW4 | ICW4_SPEC_FULLY_NESTED);
  IODelay;

  /* Initialize slave PIC */

  OutByte(PIC2_PORT0, ICW1);
  IODelay;
  OutByte(PIC2_PORT1, SLAVE_ICW2);
  IODelay;
  OutByte(PIC2_PORT1, SLAVE_ICW3);
  IODelay;
  OutByte(PIC2_PORT1, ICW4);
  IODelay;

  /* Initialize 3rd PIC */

  OutByte(PIC3_PORT0, ICW1);
  IODelay;
  OutByte(PIC3_PORT1, PIC3_ICW2);
  IODelay;
  OutByte(PIC3_PORT1, PIC3_ICW3);
  IODelay;
// #80130 OutByte(PIC3_PORT1, PIC3_ICW4);
  OutByte(PIC3_PORT1, PIC3_ICW4 | ICW4_SPEC_FULLY_NESTED);
  IODelay;

  return;
}

/***  SendEOI - Send an end of interrupt
 *
 *    This function sends an end of interrupt.
 *
 *    Support is provided for EOIing Tricord's 3rd PIC.
 *
 *    ENTRY   irq - irq level to end
 *
 *    EXIT    None
 *
 */

ulong_t SendEOI (PSD_IRQ *psd_irq)
{
   ulong_t flags;
   ulong_t rc = NO_ERROR;
   ulong_t Isr;

   flags = Disable();

   if (psd_irq->data < NUM_IRQ_PER_PIC)         // irq on 1st PIC ??
   {
      OutByte(PIC1_PORT0, OCW2_NON_SPECIFIC_EOI);
   }
   else
   {
     if (psd_irq->data < NUM_IRQ_PER_PIC * 2)   // irq on 2nd PIC ??
     {
       OutByte(PIC2_PORT0, OCW2_NON_SPECIFIC_EOI);
//       IODelay;
//       OutByte(PIC2_PORT0, OCW3_READ_ISR);
//       IODelay;
//       Isr = InByte(PIC2_PORT0);
//       if (!Isr)
//       {
//         IODelay;
         OutByte(PIC1_PORT0, OCW2_NON_SPECIFIC_EOI);
//       }
     }
     else
     {
       if (psd_irq->data < NUM_IRQ_PER_PIC * 3) // irq on 3rd PIC ??
       {
         OutByte(PIC3_PORT0, OCW2_NON_SPECIFIC_EOI);
         IODelay;
         OutByte(PIC3_PORT0, OCW3_READ_ISR);
         IODelay;
         Isr = InByte(PIC3_PORT0);
         if (!Isr)
         {
           IODelay;
           OutByte(PIC1_PORT0, OCW2_NON_SPECIFIC_EOI);
         }
       }
       else
       {
         rc = -1;  // There is no 4th PIC on Tricrd
       }
     }
   }

   Enable(flags);

   return (rc);
}
