#define INCL_ERROR_H

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


/*
 *  Global Variables
 */

P_F_2   router = 0;
char   *pParmString = 0;
int     IODelayCount = 30;

PLMA   *pPSDPLMA = 0;
ulong_t sizePLMA = 0;

#define  MAXLOCALPORTS 9
ushort_t localports[] = {0x40,0x41,0x42,0x43,0x48,0x49,0x4a,0x4b,0x61};


/***  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;
}


/***  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);
}


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

ulong_t InDword(ulong_t port) {

   ulong_t data;

   _asm {
      mov   dx,port
      in    eax,dx
      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;
}


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

void OutDword(ulong_t port, ulong_t data) {

   _asm {
      mov   dx,port
      mov   eax,data
      out   dx,eax
   };

   return;
}


/***  InitPIC - Initalize master and slave PIC
 *
 *    This function Initalizes the master and slave pic.
 *
 *    ENTRY   None
 *
 *    EXIT    None
 *
 */

void InitPIC(void) {

  /* Initialize master PIC */

  OutByte(PIC1_PORT0, ICW1);
  IODelay;
  OutByte(PIC1_PORT1, MASTER_ICW2);
  IODelay;
  OutByte(PIC1_PORT1, MASTER_ICW3);
  IODelay;
  OutByte(PIC1_PORT1, ICW4);
  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;

  return;
}


/***  SendEOI - Send an end of interrupt
 *
 *    This function sends an end of interrupt.
 *
 *    ENTRY   irq - irq level to end
 *
 *    EXIT    None
 *
 */

ulong_t SendEOI(ulong_t irq) {

   ulong_t flags;

   flags = Disable();

   if (irq < NUM_IRQ_PER_PIC)
      OutByte(PIC1_PORT0, OCW2_NON_SPECIFIC_EOI);
   else {
      OutByte(PIC2_PORT0, OCW2_NON_SPECIFIC_EOI);
      IODelay;
      OutByte(PIC1_PORT0, OCW2_NON_SPECIFIC_EOI);
   }

   Enable(flags);
}


/***  WHO_AM_I - Returns the current logical processor number
 *
 *    This function returns the current logical processor number
 *
 *    ENTRY   NONE
 *
 *    EXIT    Current processor number
 *
 */

ulong_t WHO_AM_I (void) {
   return(InByte(WHO_AM_I_PORT));
}


/***  IO - Perform an I/O cycle
 *
 *    This function performs an I/O cycle.
 *
 *    ENTRY   port    - port number
 *            procnum - logical processor number (ignored if not INDEXED_IO)
 *            flags   - flags
 *            pdata   - pointer to data read, or data to write
 *
 *    EXIT    None
 *
 */

#define INDEXED_IO 0x8000

void IO(ulong_t port, ulong_t procnum, ulong_t flags, ulong_t *pdata) {

   if (flags & INDEXED_IO) {

      OutByte(CPU_INDEX_PORT, procnum);
      OutByte(INDEX_ADDRESS_REGISTER_LOWBYTE, port);
      OutByte(INDEX_ADDRESS_REGISTER_HIGHBYTE, (port >> 8));

      port = INDEX_DATA_REGISTER;
   }

   switch (flags & IO_FLAGMASK) {

      case IO_READ_BYTE:
         *pdata = InByte(port);
          break;

      case IO_READ_WORD:
         *pdata = InWord(port);
          break;

      case IO_READ_DWORD:
         *pdata = InDword(port);
          break;

      case IO_WRITE_BYTE:
          OutByte(port, *pdata);
          break;

      case IO_WRITE_WORD:
          OutWord(port, *pdata);
          break;

      case IO_WRITE_DWORD:
          OutDword(port, *pdata);
          break;
   }

   return;
}


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

ulong_t IPIPresent (void) {

   ulong_t rc = -1;
   struct irqcontrol_s irq;

   irq.b_irq_all = InByte(IPI_IRQ_PORT);
   if (irq.b_pintreq)
      rc = NO_ERROR;

   return (rc);
}


/***  Install - Install PSD
 *
 *    This function checks to see if this PSD is installable on the
 *    current platform.
 *
 *    ENTRY   pinstall - pointer to an INSTALL structure
 *
 *    EXIT    NO_ERROR - PSD Installed
 *            -1       - PSD not valid for this platform
 *
 */

ulong_t Install(INSTALL *pinstall) {

   VMALLOC vmac;
   int i;
   char *p;
   char EISA_String[] = "EISA";
   char COMPAQ_String[] =  "COMPAQ";
   ulong_t rc;

// _asm int 3;

   /* Setup Global variables */

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

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

   /* Map BIOS area */

   if ((rc = PSDHelp(router, PSDHLP_VMALLOC, &vmac)) == NO_ERROR) {

      /* Check for COMPAQ string */

      p = (char *)vmac.addr + COMPAQ_STRING_OFFSET;

      for (i = 0; COMPAQ_String[i] != '\0'; i++)
         if (p[i] != COMPAQ_String[i]) {
            rc = -1;
            break;
         }

      if (rc == NO_ERROR) {

         /* Check for EISA string */

         p = (char *)vmac.addr + EISA_STRING_OFFSET;

         for (i = 0; EISA_String[i] != '\0'; i++)
            if (p[i] != EISA_String[i]) {
               rc = -1;
               break;
            }

         if (rc == NO_ERROR) {

            /* Check for BELIZE machine */

            rc = InByte(EISA_PRODUCT_ID_PORT3);

            if ((rc & BELIZE_MASK) == BELIZE_MASK)
               rc = NO_ERROR;
            else
               rc = -1;
         }
      }

      /* Free BIOS mapping */

      PSDHelp(router, PSDHLP_VMFREE, vmac.addr);
   }

   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.
 *
 *    ENTRY   pinit - pointer to INIT structure
 *
 *    EXIT    NO_ERROR - PSD initialized
 *            -1       - PSD not initialized
 *
 */

ulong_t Init(INIT *pinit) {

   struct cpucontrol_s cpu;
   SET_IRQ irq;
   ulong_t b;

   /* Select belize mode */

   b = InByte(CPU_MODE_SELECT_PORT);
   OutByte(CPU_MODE_SELECT_PORT, (b | BELIZE_MODE_MASK));

   /* Init CPU0 control port */

   cpu.b_cpu_all = 0;
   cpu.b_cacheena = 1;

   OutByte(CPU_CONTROL_PORT, cpu.b_cpu_all);

   /* Setup IPI IRQ info */

   irq.irq = 13;
   irq.flags = IRQf_IPI;
   irq.vector = 0;
   irq.handler = (P_F_2)IPIPresent;

   PSDHelp(router, PSDHLP_SET_IRQ, &irq);

   /* Fill init structure */

   pinit->flags = INIT_GLOBAL_IRQ_ACCESS;
   pinit->version = VERSION;

   return (NO_ERROR);
}


/***  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
 *            -1       - Processor not initialized
 *
 */

ulong_t ProcInit(void) {

   struct irqcontrol_s irq;
   ulong_t p, p1;
   int i;

   /* Get logical proc num */

   p = WHO_AM_I();
   pPSDPLMA->logical_procnum = p;

   /* Get physical proc num */

   for(i = CPU0; i < MAXCPU; i++) {

      OutByte(CPU_INDEX_PORT, i);
      p1 = InByte(LOGICAL_CPU_ASSIGNMENT_PORT);
      if (p1 == p) {
         pPSDPLMA->physical_procnum = i;
         break;
      }
   }

   /* Initalize PIC */

   InitPIC();

   OutByte(PIC1_PORT1, ~(1 << PIC_SLAVE_IRQ));

   OutByte(PIC2_PORT1, ~(1 << (IRQD - NUM_IRQ_PER_PIC)));

   /* Setup PINT info */

   irq.b_irq_all = 0;
   irq.b_pintclear = 1;
   irq.b_pintena = 1;

   OutByte(IPI_IRQ_PORT, irq.b_irq_all);

   return (NO_ERROR);
}

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

ulong_t StartProcessor(ulong_t procnum) {

   struct cpucontrol_s cpu;
   ulong_t rc = -1;

   IO(CPU_CONTROL_PORT, procnum, IO_READ_BYTE | INDEXED_IO, &cpu.b_cpu_all);

   if (cpu.b_sleepstat) {

      /* Reset processor */

      cpu.b_cpu_all = 0;
      cpu.b_reset = 1;

      IO(CPU_CONTROL_PORT, procnum, IO_WRITE_BYTE | INDEXED_IO,
         &cpu.b_cpu_all);

      /* Dispatch processor */

      cpu.b_cpu_all = 0;
      cpu.b_sleepclear = 1;
      cpu.b_cacheena = 1;
      cpu.b_cacheflush = 1;

      IO(CPU_CONTROL_PORT, procnum, IO_WRITE_BYTE | INDEXED_IO,
         &cpu.b_cpu_all);

      rc = NO_ERROR;
   }

   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 cprocs = 0;
   ulong_t p;
   int i;

   for (i = CPU0; i < MAXCPU; i++) {

      OutByte(CPU_INDEX_PORT, i);
      p = InByte(LOGICAL_CPU_ASSIGNMENT_PORT);
      if ((p & PROC_NOT_PRESENT) == PROC_NOT_PRESENT)
         break;
      cprocs++;
   }

   return (cprocs);
}


/***  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) {

   struct irqcontrol_s irq;

   irq.b_irq_all = 0;
   irq.b_pintset = 1;

   IO(IPI_IRQ_PORT, procnum, IO_WRITE_BYTE | INDEXED_IO, &irq.b_irq_all);

   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) {

   struct irqcontrol_s irq;
   ulong_t rc = -1;

   if (procnum == pPSDPLMA->logical_procnum) {

      irq.b_irq_all = 0;
      irq.b_pintclear = 1;

      OutByte(IPI_IRQ_PORT, irq.b_irq_all);

      rc = NO_ERROR;
   }

   SendEOI(IPI_IRQ);

   return (rc);
}


/***  Port_IO - Perform I/O to a specified port
 *
 *    This function reads from or writes to any given I/O port.
 *    Instead of accessing certain processor local I/O ports directly
 *    it uses indexed I/O to use CPU0's ports. If this is not done
 *    a processor can start accessing a local port on one processor, and
 *    finish using it another.
 *
 *    ENTRY   pport_io - pointer to PORT_IO structure
 *
 *    EXIT    NO_ERROR
 *
 */

ulong_t Port_IO(PORT_IO *pport_io) {

   int i;
   int IODone = 0;

   /* If not on CPU0 */

   if (pPSDPLMA->logical_procnum != CPU0) {

      for(i = 0; i < MAXLOCALPORTS; i++) {

         if (pport_io->port == localports[i]) {

            IO(pport_io->port, CPU0, pport_io->flags | INDEXED_IO,
               &(pport_io->data));

            IODone++;
            break;
         }
      }
   }

   if (!IODone)
      IO(pport_io->port, 0, pport_io->flags, &(pport_io->data));

   return (NO_ERROR);
}


