#define UwsPci_c


// **********************************************************************
// * uwspci.c                                                           *
// *                                                                    *
// * Written by Matthew DeLoera (deloera@us.ibm.com)                    *
// * (C) Copyright IBM Corporation, 1998-2001                           *
// *                                                                    *
// *  This software may be used and distributed according to the terms  *
// *  of the GNU Public License, incorporated herein by reference.      *
// **********************************************************************

// **********************************************************************
// * uwspci.c - This source file provides all ISA-specific functionality*
// *            such as hardware detection, interrupt handling, and     *
// *            device I/O.                                             *
// *                                                                    *
// * Exported Functions                                                 *
// * ------------------                                                 *
// * sysSpGetPciInfo        - PCI hardware detection/probing            *
// * sysSpProcessPciRequest - preparation and sending of commands to ASM*
// * sysSp_pciIntrHandler   - PCI interrupt handler                     *
// *                                                                    *
// * Changes                                                            *
// * ------------------------------------------------------------------ *
// * 01/29/2001 - Cleaned up for open source release.                   *
// * ------------------------------------------------------------------ *
// * 06/04/2001 - PHR_162848 - Removed uresolved reference to           *
// *                           current->signal                          *
// * ------------------------------------------------------------------ *
// * 06/27/2002 - PHR_MULTI_4 - Added multi node support                *
// *                                                                    *
// **********************************************************************


// ************
// * INCLUDES *
// ************
#include "uwscmd.h"

// **********************
// * EXTERNAL FUNCTIONS *
// **********************
extern void  sysSpCmdTimeout(PDRIVER_INFO pDriverNode); 
extern void  sysSpRunSpCmd(PDRIVER_INFO pDriverNode);
extern void  sysSpRunEvent(PDRIVER_INFO pDriverNode);

extern int   REMIrqRoutine(PDRIVER_INFO pDriverNode, ULONG MouseIntStatus);   

extern PWORK_BLOCK sysSpPeekQueue(void *pHead);
extern PWORK_BLOCK sysSpDequeue(PDRIVER_INFO pDriverNode, void *pHead);
extern PWORK_BLOCK sysSpEnqueue(PDRIVER_INFO pDriverNode, void *pHead, PWORK_BLOCK pBlock);

// ********************
// * LOCAL PROTOTYPES *
// ********************
int   sysSpGetPciInfo(PDRIVER_INFO pDriverNode);
void  sysSpProcessPciRequest(PDRIVER_INFO pDriverNode, PCMD_BLOCK pCmdBlk, unsigned long ultimeoutValue);
void  sysSpCreateI2OMessage(PI2O_MESSAGE_FRAME pI2Omessage, PUCHAR pBuffer, USHORT bufferSize);
void  sysSpProcessIncomingPCI(PDRIVER_INFO pDriverNode, PI2O_MESSAGE_HEADER pI2OmessageHeader);
void  sysSp_pciIntrHandler(int iRq, void * pDev_id, struct pt_regs *regs);

unsigned int sysSpCountEagles(void);

// Start of PHR_180930
u32   READ_REGISTER_ULONG(void *x);
void  WRITE_REGISTER_ULONG(unsigned long *x, u32 y);
//unsigned long READ_REGISTER_ULONG(void *x);
//void          WRITE_REGISTER_ULONG(unsigned long *x, unsigned long y);
// End of PHR_180930

// ****************************************************
// * GLOBALS FOR COMMUNICATION WITH SHARED COM DRIVER *
// ****************************************************
unsigned char  IBMASMwisemanexists = 0;
unsigned char  IBMASMeagleexists   = 0;
unsigned short IBMASMirq           = 0;
unsigned char  *IBMASMeaglebase    = NULL;
unsigned short IBMASMwisemanport   = 0;
unsigned short IBMASMwisemanbase   = 0;

extern struct   pci_dev *PciDev;                              // PHR_198187

// *************************************************************************
// * sysSpGetPciInfo() - This function detects the existence of a Wiseman, *
// *                     and determines information for Wiseman such as    *
// *                     port number and interrupt level.                  *
// *                                                                       *
// * Parameters:                                                           *
// *      pDriverNode  - Pointer to driver extension                       *
// *************************************************************************
int sysSpGetPciInfo( PDRIVER_INFO pDriverNode )
{
   unsigned int    VendorID, DeviceID;

#if LINUX_VERSION_CODE < 132096
   u32 curr;
#endif

   // SET DEVICE ID AND VENDOR ID 
   if (pDriverNode->AsmType == ASMTYPE_EAGLE)
   {
      VendorID = SCOUT_VENDORID;
      DeviceID = SCOUT_DEVICEID;
   }
   else
   {
      VendorID = PLX9080_VENDORID;
      DeviceID = PLX9080_DEVICEID;
   }

   if (!(PciDev = pci_find_device(VendorID, DeviceID, PciDev)))
   {
      return RC_SYSTEM_ERROR;
   }

   // COMMUNICATE TO SHARED COM DRIVER THAT ibmasm HAS DETECTED WISEMAN/EAGLE
   if (pDriverNode->AsmType == ASMTYPE_EAGLE)
   {
      IBMASMeagleexists = 1;
   }
   else
   {
      IBMASMwisemanexists = 1;
   }

   // DETERMINE IRQ VECTOR
   pDriverNode->intVector = PciDev->irq;
   IBMASMirq              = PciDev->irq;

// KERNEL 2.2 - MAP BASE ADDRESSES ACCORDINGLY
#if LINUX_VERSION_CODE < 132096
   
   if (pDriverNode->AsmType == ASMTYPE_EAGLE)
   {
      // MAP EAGLE'S BASE ADDRESS TO AN ACCESSIBLE VIRTUAL ADDRESS
      curr = PciDev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK;

      pDriverNode->baseAddr  = (PUCHAR) curr;
      pDriverNode->vBaseAddr = ioremap(curr, 0x200000);
      IBMASMeaglebase        = (unsigned char *)pDriverNode->vBaseAddr;
   }
   else   // WISEMAN
   {
      // MAP WISEMAN'S BASE ADDRESS TO AN ACCESSIBLE VIRTUAL ADDRESS
      curr = PciDev->base_address[2] & PCI_BASE_ADDRESS_MEM_MASK;

      pDriverNode->baseAddr  = (PUCHAR) curr;
      pDriverNode->vBaseAddr = ioremap(curr, 0x10000);
   
      // STORE WISEMAN'S BASE I/O ADDRESS
      curr = PciDev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;

      pDriverNode->ioAddr  = (PUCHAR) curr;
      IBMASMwisemanbase    = curr;

      // EXPORT WISEMAN'S UART BASE
      IBMASMwisemanport = (PciDev->base_address[3] & PCI_BASE_ADDRESS_IO_MASK) + 8;
   }

#else    // KERNEL 2.4 - MAP BASE ADDRESSES ACCORDINGLY

   if (pDriverNode->AsmType == ASMTYPE_EAGLE)
   {
        // MAP EAGLE'S BASE ADDRESS TO AN ACCESSIBLE VIRTUAL ADDRESS
        pDriverNode->baseAddr  = (PUCHAR)pci_resource_start(PciDev, 0);
        pDriverNode->vBaseAddr = ioremap(pci_resource_start(PciDev, 0),
                                         pci_resource_len(PciDev, 0));

        IBMASMeaglebase = (unsigned char *)pDriverNode->vBaseAddr;
   }
   else  // WISEMAN
   {
        // MAP WISEMAN'S BASE ADDRESS TO AN ACCESSIBLE VIRTUAL ADDRESS
        pDriverNode->baseAddr  = (PUCHAR)pci_resource_start(PciDev, 2);
        pDriverNode->vBaseAddr = ioremap(pci_resource_start(PciDev, 2),
                                         pci_resource_len(PciDev,   2));

        // STORE WISEMAN'S BASE I/O ADDRESS
        pDriverNode->ioAddr = (PUCHAR)pci_resource_start(PciDev, 1);
        IBMASMwisemanbase   = pci_resource_start(PciDev, 1);

        // EXPORT WISEMAN'S UART BASE
        IBMASMwisemanport = pci_resource_start(PciDev, 3) + 8;
   }
#endif

   // ENSURE THE ADDRESS MAPPING WAS SUCCESSFUL
   if (!pDriverNode->vBaseAddr)
   {
        printk(KERN_CRIT "sysSpGetPciInfo: Failed to remap base address!\n");
        return RC_SYSTEM_ERROR;
   }

   return RC_SUCCESS;
}


// *********************************************************************
// * sysSpProcessPciRequest() - This function sends an I2O  message to *
// *                            Wiseman, via the PLX device.           *
// *                                                                   *
// * NOTE: The command queue is currently locked!                      *
// *                                                                   *
// * Parameters:                                                       *
// *      pDriverNode    - Pointer to driver extension                 *
// *      pCmdBlk        - Pointer to command block to be sent         *
// *      ultimeoutValue - Comnand timeout value in ms.                *
// *********************************************************************
void sysSpProcessPciRequest(PDRIVER_INFO pDriverNode, PCMD_BLOCK pCmdBlk, unsigned long ultimeoutValue)
{
     PCMD_BUFFER pCmdBuffer = (PCMD_BUFFER)pCmdBlk->pBuffer;
     u32 MFAaddr = 0x0000;          // PHR_180930
   //ULONG       MFAaddr;           // PHR_180930

     // GET ADDRESS FOR SEND MAILBOX
     if (pDriverNode->AsmType == ASMTYPE_EAGLE)
     {
         MFAaddr = SCOUT_READ_SP_INBOUND_Q(pDriverNode->vBaseAddr);
     }
     #ifndef IA64                   // PHR_180930
     else             
     {
         MFAaddr = READ_SEND_MAILBOX(pDriverNode->ioAddr);
     }
     #endif                         // PHR_180930 

     // DETERMINE WHETHER MAILBOX IS AVAILABLE FOR SENDING
     if (MBX_EMPTY(MFAaddr))
     {
        // MARK IRP AS PENDING
        pCmdBlk->completionCode = DDERR_CMD_PENDING;

        if (pCmdBlk->PID == PID_HEART_BEAT)  // PHR_MULTI_2
        {
            pCmdBlk->completionCode = DDERR_CMD_PENDING;
        }

        // PHR_180930
//      sysSpCreateI2OMessage((PI2O_MESSAGE_FRAME)(GET_MFA_ADDR((MFAaddr) + (u32)(pDRVInfo->vBaseAddr))),
        sysSpCreateI2OMessage((PI2O_MESSAGE_FRAME)(((MFAaddr & 0xFFFFFF00) + (unsigned long)(pDriverNode->vBaseAddr))),
                              // ACTUAL COMMAND STRING
                              (PUCHAR)pCmdBlk->pBuffer,
                              // LENGTH OF COMMAND STRING
                              (USHORT)((sizeof(spCmd) + pCmdBuffer->cmdBlock.CommandLgth + pCmdBuffer->cmdBlock.DataLgth) < I2O_MESSAGE_SIZE -
                              sizeof(I2O_MESSAGE_HEADER)) ? (sizeof(spCmd) + pCmdBuffer->cmdBlock.CommandLgth + pCmdBuffer->cmdBlock.DataLgth) : I2O_MESSAGE_SIZE - sizeof(I2O_MESSAGE_HEADER));

        // INITIALIZE INFO STRUCTURE FOR THE COMMAND TIMEOUT ROUTINE
        pDriverNode->pActiveCmdBlk = pCmdBlk;   // PHR_MULTI

        // SET UP THE COMMAND TIMER
        pDriverNode->CommandTimer.function = (void *)sysSpCmdTimeout;
        pDriverNode->CommandTimer.data     = (unsigned long)pDriverNode; 
        pDriverNode->CommandTimer.expires  = jiffies + ultimeoutValue * HZ;

        add_timer(&(pDriverNode->CommandTimer));

        if (DBG_CMDTIMEOUT)
        {
           printk(KERN_CRIT "ibmasm: DBG_CMDTIMEOUT - Starting 45 second command time out timer.\n"); 
        }

        // SIGNAL PLX THAT THIS MESSAGE IS VALID
        if (pDriverNode->AsmType == ASMTYPE_EAGLE)
        {
            SCOUT_WRITE_SP_INBOUND_Q(pDriverNode->vBaseAddr, MFAaddr);
        }
        #ifndef IA64                                                          // PHR_180930
        else 
        {
            WRITE_SEND_MAILBOX(pDriverNode->ioAddr, GET_MFA_ADDR(MFAaddr) | 1L); 
            SET_LOCAL_DOORBELL(pDriverNode->ioAddr); 
        }
        #endif                                                                // PHR_180930 
     }
     else
     {
          // MAILBOX WAS FULL - HIGHLY UNLIKELY SITUATION! TRADITIONALLY, WE RETURN A TIMEOUT ERROR CODE HERE.
          printk(KERN_CRIT "ibmasm: ERROR - PCI mailbox is full. Returning failure rc = DDERR_RCV_TIMEOUT.\n");

          // MARK THIS COMMAND BLOCK'S COMPLETION CODE AS FAILED
          pCmdBlk->completionCode = DDERR_RCV_TIMEOUT;
     }
     return;
}

// *********************************************************************
// * sysSpCreateI2OMessage()  - This function creates an I2O message   *
// *                            frame for the given SP command buffer. *
// *                                                                   *
// * Parameters:                                                       *
// *      pI2Omessage    - Pointer to empty I2O message frame          *
// *      pBuffer        - Pointer to command string                   *
// *      bufferSize     - Length of command string                    *
// *********************************************************************
void sysSpCreateI2OMessage(PI2O_MESSAGE_FRAME pI2Omessage, PUCHAR pBuffer, USHORT bufferSize)
{
     I2O_MESSAGE_HEADER I2OmessageHeader;
     // PHR_180930
  // USHORT msgSize = (sizeof(I2O_MESSAGE_HEADER) + bufferSize) / sizeof(ULONG);
     USHORT msgSize = (sizeof(I2O_MESSAGE_HEADER) + bufferSize) / sizeof(u32);

     // CALCULATE MESSAGE SIZE, INCLUDING HEADER (IN DWORDS)
     I2OmessageHeader.msg_size          = msgSize;
     // PHR_180930
   //if ((msgSize*sizeof(ULONG)) < (sizeof(I2O_MESSAGE_HEADER) + bufferSize)) ++I2OmessageHeader.msg_size;
     if ((msgSize*sizeof(u32)) < (sizeof(I2O_MESSAGE_HEADER) + bufferSize)) ++I2OmessageHeader.msg_size;
     I2OmessageHeader.ver_offset        = I2O_MSG_VERSION;
     I2OmessageHeader.msg_flags         = 0x0000;

     // INTIIALIZE TO BOGUS VALUE
     I2OmessageHeader.function          = 0xFF;

     // BITS 16-23 FOR THE INITIATOR ADDRESS
     I2OmessageHeader.initiator_addr    = I2O_HOST_DD_ADDR;

     // BITS 8-11 FOR THE TARGET ADDRESS / BITS 12-15 FOR THE INITIATOR ADDRESS
     I2OmessageHeader.initAndtgt_addr   = I2O_HOST_AND_TARGET_ADDR;

     // BITS 0-8 FOR THE TARGET ADDRESS
     I2OmessageHeader.target_addr       = I2O_TARGET_ADDR;
     // PHR_180930
   //I2OmessageHeader.initiator_context = 0L;
     I2OmessageHeader.initiator_context = 0x0000;

     // COPY HEADER AND COMMAND INTO SPECIFIED I2O MESSAGE FRAME
     memcpy(pI2Omessage, &I2OmessageHeader, sizeof(I2O_MESSAGE_HEADER));
     memcpy(pI2Omessage->cmd,pBuffer,bufferSize);
     return;
}


// ****************************************************************************
// * sysSpProcessIncomingISA() - This function is the main interrupt handler. *
// *                                                                          *
// * Parameters:                                                              *
// *      pDriverInfo    - Pointer to driver extension                        *
// ****************************************************************************
void sysSpProcessIncomingPCI(PDRIVER_INFO pDriverNode, PI2O_MESSAGE_HEADER pI2OmessageHeader)
{
    UCHAR      commandType;
//  ULONG      dataLength;           // PHR_180930
    u32        dataLength;           // PHR_180930
//  ULONG          flags       = 0;   // PHR_180930
    unsigned long  flags       = 0;   // PHR_180930
    PCMD_BLOCK     pCmdBlk     = NULL;
    spCmd         *pCmdBuffer = (spCmd *)(((PI2O_MESSAGE_FRAME)(pI2OmessageHeader))->cmd);

    // PARSE NEW SP MESSAGE FOR MESSAGE TYPE AND DATA LENGTH
    commandType = ((PI2O_MESSAGE_FRAME)(pI2OmessageHeader))->cmd[0];

    dataLength  = ((sizeof(spCmd) + pCmdBuffer->CommandLgth +
                   pCmdBuffer->DataLgth) < (sizeof(u32) * pI2OmessageHeader->msg_size)) ?
                    (sizeof(spCmd) + pCmdBuffer->CommandLgth +
                   pCmdBuffer->DataLgth) : (sizeof(u32) * pI2OmessageHeader->msg_size);

    switch (commandType)
    {
       case spEvent:   // EVENT 
       {
            if (DBG_IRQ) printk(KERN_CRIT "sysSpProcessIncomingPCI: Received event.\n");
   
            // LOCK THE EVENT QUEUE AND PROCESS THE INCOMING EVENT
            spin_lock_irqsave( &(pDriverNode->EventQueueLock), flags );

            if (DBG_LOCKS) printk(KERN_CRIT "sysSpProcessIncomingPCI: Set EVENTQLOCK.\n");
   
            // STORE THIS EVENT ON THE CIRCULAR EVENT BUFFER
            memcpy(pDriverNode->EvRcvd[pDriverNode->EvBufIndex].pEvBuffer,
                   ((PI2O_MESSAGE_FRAME)(pI2OmessageHeader))->cmd,
                   dataLength);
   
            // GENERATE NEW INDEX FOR THIS EVENT
            if (pDriverNode->MagicNumber < (EV_MAXNUMBER-1))
            {
               pDriverNode->MagicNumber++;
            }
            else
            {
               pDriverNode->MagicNumber = 1;
            }
   
            // STORE ASSOCIATED DATA LENGTH AND EVENT INDEX IN CIRCULAR EVENT BUFFER
            pDriverNode->EvRcvd[pDriverNode->EvBufIndex].EvLength = dataLength;
            pDriverNode->EvRcvd[pDriverNode->EvBufIndex].EvNumber = pDriverNode->MagicNumber;
   
            // ADVANCE TO NEXT NODE IN CIRCULAR BUFFER
            if (pDriverNode->EvBufIndex < (EV_BUFFERS-1))
            {
                pDriverNode->EvBufIndex++;
            }
            else
            {
                pDriverNode->EvBufIndex = 0;
            }
   
            // RELEASE ALL BLOCKED REGISTRATION THREADS
            sysSpRunEvent(pDriverNode);
   
            // UNLOCK THE EVENT QUEUE
            spin_unlock_irqrestore( &(pDriverNode->EventQueueLock), flags );
            
            if (DBG_LOCKS) printk(KERN_CRIT "sysSpProcessIncomingPCI: Cleared EVENTQLOCK.\n");

            break;
       } 
       case spHeartBeat:               // HEARTBEAT // PHR_MULTI_2 
       {
          if (DBG_HB)
          {
             printk(KERN_CRIT "DBG_HB: Received a heart beat from the SP\n");
          }

          // COPY SELECTED NUMBER OF BYTES INTO GLOBAL HB BUFFER
          memcpy((void *)(pDriverNode->pHB_Block->pBuffer),    
                 ((PI2O_MESSAGE_FRAME)(pI2OmessageHeader))->cmd,
                 dataLength);

          // PREPARE THE COMMAND BLOCK TO BE SENT TO THE SP
          pDriverNode->pHB_Block->pBuffer[0]     = spWrite; 
          pDriverNode->pHB_Block->PID            = PID_HEART_BEAT;
          pDriverNode->pHB_Block->bufferLength   = dataLength; 
          pDriverNode->pHB_Block->completionCode = DDERR_CMD_INITIATED;

          // ADD THIS HEART BEAT TO THE COMMAND QUEUE - IF THE QUEUE WAS NOT NULL (INDICATING THAT
          // THERE IS A COMMAND IN FRONT OF THIS ONE), WAIT FOR IT'S TURN TO RUN BY WAITING FOR 
          // sysSpRunCmd() TO CONTINUE THE PROCESSING OF THIS HEART BEAT. OTHERWISE, THE HEART BEAT 
          // RESPONSE CAN BE SENT BACK TO THE SP IMMEDIATELY

          spin_lock_irqsave( &(pDriverNode->CmdQueueLock), flags );

          if (DBG_HB)
          {
             printk(KERN_CRIT "DBG_HB: Enqueueing heart beat.\n");
          }

          if ( (sysSpEnqueue(pDriverNode, pDriverNode->CmdQueue, pDriverNode->pHB_Block)) == NULL)
          { 

             if (DBG_HB)
             {
                printk(KERN_CRIT "DBG_HB: Sending heart beat to the SP (PCI H/W).\n");
             }

             // RETRIEVE THE HEART BEAT FROM THE COMMAND QUEUE
             pCmdBlk = sysSpPeekQueue(pDriverNode->CmdQueue);
   
             // UNLOCK THE COMMAND QUEUE
             spin_unlock_irqrestore( &(pDriverNode->CmdQueueLock), flags );

             // SEND THE HEART BEAT RESPONSE TO THE SP
             sysSpProcessPciRequest(pDriverNode, pCmdBlk, CMD_TIMEOUT_45);
          }
          else // THERE WAS A COMMAND ON THE QUEUE SO WAIT FOR THIS TASKS TURN TO RUN
          {
              // UNLOCK THE COMMAND QUEUE
             spin_unlock_irqrestore( &(pDriverNode->CmdQueueLock), flags );

          }

          break;
       }
       case spCmdResp:               // COMMAND RESPONSE 
       {
          if ( DBG_PHR )
          {
              printk(KERN_CRIT "uwspci: Received a command response.\n");
          }

          spin_lock_irqsave( &(pDriverNode->CmdQueueLock), flags );

          if (pDriverNode->CmdQueue == NULL)
          {
              printk(KERN_CRIT "ibmasm: ERROR - Received a command response without an associated command.\n");
              spin_unlock_irqrestore( &(pDriverNode->CmdQueueLock), flags );
              break;
          }

          // GET THE COMMAND FROM THE COMMAND QUEUE THAT HAS BEEN WAITING FOR A COMMAND RESPONSE
          pCmdBlk = sysSpPeekQueue(pDriverNode->CmdQueue);
   
          if ( pCmdBlk == NULL )
          {
              printk(KERN_CRIT "ibmasm: ERROR - Received a command response without an associated command.\n");
              spin_unlock_irqrestore( &(pDriverNode->CmdQueueLock), flags );
              break;
          }

          // UNLOCK THE COMMAND QUEUE
          spin_unlock_irqrestore( &(pDriverNode->CmdQueueLock), flags );
   
          if ( (pCmdBlk->completionCode == DDERR_CMD_PENDING )  ||
               (pCmdBlk->completionCode == DDERR_PENDING_CMD_INTERRUPTED) ) 
          {
             // DEACTIVATE THE COMMAND TIMER 
             del_timer(&(pDriverNode->CommandTimer));

             if (DBG_CMDTIMEOUT)
             {
                 printk(KERN_CRIT "DBG_CMDTIMEOUT: Deleting 45 second command time out timer.\n"); 
             }
             
             if (pCmdBlk->PID == PID_HEART_BEAT)
             {
                if(DBG_HB)
                {
                    printk(KERN_CRIT "DBG_HB: Received a command response for a heart beat.\n"); 
                    printk(KERN_CRIT "DBG_HB: Dequeueing Heart Beat.\n"); 
                }

                // DEQUEUE THE HEART BEAT FROM THE COMMAND QUEUE
                spin_lock_irqsave( &(pDriverNode->CmdQueueLock), flags );
                sysSpDequeue(pDriverNode, pDriverNode->CmdQueue);
                spin_unlock_irqrestore( &(pDriverNode->CmdQueueLock), flags );

                sysSpRunSpCmd(pDriverNode);
             }
             else 
             {
                if ( pCmdBlk->completionCode == DDERR_CMD_PENDING )
                {
                    // IF THIS WAS A INTERNAL DRIVER GENERATED IOCTL CALL JUST WAKE UP THE IOCTL TASK THAT'S WAITING
                    if ( (pCmdBlk->PID == PID_OS_IS_UP) || (pCmdBlk->PID == PID_STOP_DRIVER) )
                    {
                        pCmdBlk->completionCode = DDERR_SUCCESS;
                        
                        wake_up(&(pCmdBlk->pWaitQueueRun));                   // PHR_187769
                        break;
                    }
                    else // ELSE - COPY THE SP's RESPONSE INTO THE GLOBAL RESPONSE BUFFER THEN WAKE UP THE WAITING TASK
                    {
                        pCmdBlk->completionCode = DDERR_SUCCESS;
                        pCmdBlk->bufferLength   = dataLength;
                        memcpy(pDriverNode->pSpResponseBuffer, ((PI2O_MESSAGE_FRAME)(pI2OmessageHeader))->cmd, dataLength);

                        if ( (signal_pending(current)) && (current->pid == pCmdBlk->PID) ) 
                        {
                            if(DBG_SIGNALS)
                            {
                                printk(KERN_CRIT "DBG_SIGNALS: Received an interrupted command response prior to wake up.\n"); 
                            }

                            pCmdBlk->completionCode = DDERR_PENDING_CMD_INTERRUPTED;
                        }
                        else
                        {
                            wake_up(&(pCmdBlk->pWaitQueueRun));                    // PHR_187769
                            sysSpRunSpCmd(pDriverNode);
                            break;
                        }
                    }
                }   
                else     // THIS IS AN INTERRUPTED COMMAND
                {
                    if(DBG_SIGNALS)
                    {
                        printk(KERN_CRIT "DBG_SIGNALS: Received an interrupted command response from RW_IOCTL.\n"); 
                    }
                }   

                if ( pCmdBlk->completionCode == DDERR_PENDING_CMD_INTERRUPTED ) 
                {
                    // IF THIS WAS A INTERNAL DRIVER GENERATED IOCTL CALL JUST WAKE UP THE IOCTL TASK THAT'S WAITING
                    if ( (pCmdBlk->PID == PID_OS_IS_UP) || (pCmdBlk->PID == PID_STOP_DRIVER) )
                    {
                        pCmdBlk->completionCode = DDERR_SUCCESS;
                    }
                    else // ELSE - DEQUEUE THE COMMAND AND FREE ITS MEMORY
                    {
                        if(DBG_SIGNALS)
                        {
                            printk(KERN_CRIT "DBG_SIGNALS: Dequeueing interrupted Command.\n"); 
                        }

                        // DEQUEUE THE HEART BEAT FROM THE COMMAND QUEUE
                        spin_lock_irqsave( &(pDriverNode->CmdQueueLock), flags );
                        sysSpDequeue(pDriverNode, pDriverNode->CmdQueue);
                        spin_unlock_irqrestore( &(pDriverNode->CmdQueueLock), flags );

                        // FREE MEMORY
                        if (pCmdBlk)
                        {
                            if (pCmdBlk->pBuffer)
                            {
                                kfree(pCmdBlk->pBuffer);
                                pCmdBlk->pBuffer = NULL;
                            }

                            kfree(pCmdBlk);
                            pCmdBlk = NULL;
                        }

                        sysSpRunSpCmd(pDriverNode);
                    }
                }
             }
          }
          else   // pCmdBlk = NULL - SHOULD NOT BE HERE, IF WE ARE, WE ARE OUT OF SYNC WITH THE SP  
          {
             pCmdBlk->completionCode = DDERR_UNEXPECTED_CMD_RESPONSE;
             printk(KERN_CRIT "ibmasm: ERROR - Received a unexpected command response return code.\n");
          }
          

          break;
       }    // end case - spCmdResp
       default:
       {
           printk(KERN_CRIT "ibmasm: ERROR - Unknown command Received from the SP.\n");
       }
    }       // end switch

    return;
}           // end sysSpProcessIncomingPCI()


























// ******************************************************************************
// * sysSp_pciIntrHandler - This is the driver's interrupt handler entry point. *
// *                        The kernel calls this function when an interrupt    *
// *                        occurs on the PCI host adapter.                     *
// *                                                                            *
// * Parameters:                                                                *
// *      iRq     - # of the IRQ                                                *
// *      pDev_id - Pointer to the driver extension                             *
// *      regs    - Pointer to the processor registers at the time of IRQ       *
// ******************************************************************************
void sysSp_pciIntrHandler(int iRq, void * pDev_id, struct pt_regs *regs)
{
     u32  MFAaddr;                                // PHR_180930
     u32  intStatus;                              // PHR_180930
     u_long  MouseIntStatus = 0;                  // PHR_187116

     void   *pMsgAddr;
     int    i = 0;
     int    MouseRC = 0;                          

     PDRIVER_INFO pDriverNode = pDev_id;          
     
     if ( pDriverNode->AsmType == ASMTYPE_EAGLE )
     {
         // READ PCI9080_INT_CONTROL_STATUS REGISTER
         intStatus = SCOUT_READ_INT_STATUS(pDriverNode->vBaseAddr);

         // CHECK TO SEE IF THERE IS REMOTE MOUSE/KEYBOARD DATA AVAILABLE
         MouseIntStatus = READ_REGISTER_ULONG( (PULONG)(pDriverNode->pRemoteDataBaseAddr + CONDOR_MOUSE_ISR_STATUS_OFFSET) );  // PHR_187116 
     }
     #ifndef IA64                                 // PHR_180930 
     else
     {
         intStatus = READ_INT_STATUS(pDriverNode->ioAddr);
     }
     #endif                                       // PHR_180930 

     // CHECK STATUS TO DETERMINE WHETHER THIS INTERRUPT IS FOR THIS DRIVER
     if ( (pDriverNode->AsmType == ASMTYPE_EAGLE) && 
          (intStatus & SCOUT_INTR_STATUS_MASK) )
     {
        // Start of PHR_187116
        if ( MouseIntStatus )                
        {  
           if (DBG_MOUSE)
           {
               printk(KERN_CRIT "DBG_MOUSE: sysSp_pciIntrHandler received a remote mouse\\keyboard interrupt.\n");
           }
        
           MouseRC = REMIrqRoutine(pDriverNode, MouseIntStatus);

           if ( MouseRC )
           {
               //if (DBG_MOUSE)
               //{
               //    printk(KERN_CRIT "DBG_MOUSE: sysSp_pciIntrHandler will unblock with RemoteInputCount = %li.\n",
               //           pDriverNode->RemoteInputCount);
               //}
               
               // RELEASE THE BLOCKED THREAD WAITING FOR A COMMAND RESPONSE
               wake_up_interruptible(&(pDriverNode->pRmtDataWaitQueue));
           }
           else
           {
               printk(KERN_CRIT "ibmasm: ERROR - sysSp_pciIntrHandler was passed a spurious remote mouse interrupt. \n");
           }                                       
        }    // End of PHR_187116

        // DETERMINE WHETHER THERE'S VALID DATA IN THE MAILBOX (Bit31=1)   
        MFAaddr = SCOUT_READ_SP_OUTBOUND_Q(pDriverNode->vBaseAddr);

        while ( MFAaddr == SCOUT_NO_MFAS_AVAILABLE )
        {
            MFAaddr = SCOUT_READ_SP_OUTBOUND_Q(pDriverNode->vBaseAddr);

            i++;

            if (i == 10)
            {
               // SIGNAL THE PLX MESSAGING UNIT THAT WE NO LONGER NEED THIS MFA ADDRESS
               SCOUT_WRITE_SP_OUTBOUND_Q(pDriverNode->vBaseAddr, MFAaddr);

               printk(KERN_CRIT "PCI interrupt handler never got a good buffer, returning failure. \n");
               return;
            }
        }

        // POINT TO THE I2O MESSAGE AND PROCESS IT
        pMsgAddr = GET_MFA_ADDR(MFAaddr) + pDriverNode->vBaseAddr;

        if ( !MouseIntStatus)
        {
            sysSpProcessIncomingPCI(pDriverNode, (PI2O_MESSAGE_HEADER )pMsgAddr);
        }
        else 
        {
            // We are done so clear the Remote Data ISR status bit and let the SP know it can return the  *
            // I2O packet back to free pool.                                                              *
            WRITE_REGISTER_ULONG( (PULONG)(pDriverNode->pRemoteDataBaseAddr + CONDOR_MOUSE_ISR_STATUS_OFFSET) , 0);
        }

        // SIGNAL THE PLX MESSAGING UNIT THAT WE NO LONGER NEED THIS MFA ADDRESS
        SCOUT_WRITE_SP_OUTBOUND_Q(pDriverNode->vBaseAddr, MFAaddr);
     }
     #ifndef IA64                                    // PHR_180930
     else if (intStatus & PLX_DOORBELL_INTR_ACTIVE)
     {
          // WISEMAN - CHECK FOR OUR DOORBELL INTERRUPT
          if (READ_PCI_DOORBELL(pDriverNode->ioAddr))
          {
               // DETERMINE WHETHER THERE'S VALID DATA IN THE MAILBOX (Bit31=1)   
               MFAaddr = READ_RECEIVE_MAILBOX(pDriverNode->ioAddr);
               if (MBX_FULL(MFAaddr))
               {
                    // POINT TO THE I2O MESSAGE AND PROCESS IT
                    pMsgAddr = GET_MFA_ADDR(MFAaddr) + pDriverNode->vBaseAddr;
                    sysSpProcessIncomingPCI(pDriverNode, (PI2O_MESSAGE_HEADER )pMsgAddr);
               }
    
               // TURN OFF PCI DOORBELL AND SIGNAL THE PLX MESSAGING UNIT THAT WE NO LONGER NEED THIS MFA ADDRESS
               RESET_PCI_DOORBELL(pDriverNode->ioAddr);
               WRITE_RECEIVE_MAILBOX(pDriverNode->ioAddr, GET_MFA_ADDR(MFAaddr));
          }
     }
     #endif                                          // PHR_180930 
     return;
}


// ************************
// * READ_REGISTER_ULONG  *
// * WRITE_REGISTER_ULONG *
// ************************
// Start of PHR_180930
u32 READ_REGISTER_ULONG(void *x)
{
     u32 tempulong;
     tempulong = readl(x);
     return tempulong;
}

void WRITE_REGISTER_ULONG(unsigned long *x, u32 y)
{
     u32 tempulong;
     tempulong = y;
     writel(tempulong, x);
     return;
}

// *************************************************************************
// * sysSpCountEagles() -   This function returns the total number of      *
//                          Eagle service processors found.                * 
// *                                                                       *
// *************************************************************************
UINT sysSpCountEagles(void)
{
     unsigned int numEagles = 0;
     unsigned int VendorID = SCOUT_VENDORID,                  
                  DeviceID = SCOUT_DEVICEID;
     struct pci_dev *p = NULL;

     while ((p = pci_find_device(VendorID, DeviceID, p)) != NULL)
       numEagles++;
     return numEagles;
}

/*
unsigned long READ_REGISTER_ULONG(void *x)
{
     unsigned long tempulong;
     memcpy(&tempulong, x, sizeof(unsigned long));
     return tempulong;
}

void WRITE_REGISTER_ULONG(unsigned long *x, unsigned long y)
{
     unsigned long tempulong;
     tempulong = y;
     memcpy(x, &tempulong, sizeof(unsigned long));
     return;
}

// End of PHR_180930
*/
#undef UwsPci_c
