/*********************************************************
 * $Id
 *
 * copyright @ Motorola, 1999
 *********************************************************/
#include "i2o.h"

extern unsigned int load_runtime_reg( unsigned int eumbbar, unsigned int reg );
#pragma Alias( load_runtime_reg, "load_runtime_reg" );

extern void store_runtime_reg( unsigned int eumbbar, unsigned int reg, unsigned int val );
#pragma Alias( store_runtime_reg, "store_runtime_reg" );

typedef struct _fifo_stat
{
    QUEUE_SIZE   qsz;
    unsigned int qba;
} FIFOSTAT;

FIFOSTAT fifo_stat = { QSIZE_4K, 0xffffffff };

/**********************************************************************************
 * function: I2OMsgEnable
 *
 * description: Enable the interrupt associated with in/out bound msg
 *              return I2OSUCCESS if no error, otherwise return I2OMSGINVALID.
 *
 *              All previously enabled interrupts are preserved.
 * note:
 * Inbound message interrupt generated by PCI master and serviced by local processor
 * Outbound message interrupt generated by local processor and serviced by PCI master
 *
 * local processor needs to enable its inbound interrupts it wants to handle(LOCAL)
 * PCI master needs to enable the outbound interrupts of devices it wants to handle(REMOTE)
 ************************************************************************************/
I2OSTATUS I2OMsgEnable ( LOCATION loc,        /*  REMOTE/LOCAL   */
			 unsigned int base,   /* pcsrbar/eumbbar */
			 unsigned char n )    /* b'1' - msg 0
					       * b'10'- msg 1
					       * b'11'- both
					       */
{
    unsigned int reg, val;
    if ( ( n & 0x3 ) == 0 )
    {
	/* neither msg 0, nor msg 1 */
	return I2OMSGINVALID;
    }

    n = (~n) & 0x3;
    /* LOCATION - REMOTE : enable outbound message of device, pcsrbar as base
     *            LOCAL  : enable local inbound message, eumbbar as base
     */
    reg = ( loc == REMOTE ? I2O_OMIMR : I2O_IMIMR );
    val = load_runtime_reg( base, reg );

    val &= 0xfffffffc; /* masked out the msg interrupt bits */
    val |= n;          /* LSB are the one we want */
    store_runtime_reg( base, reg, val );

    return I2OSUCCESS;
}

/*********************************************************************************
 * function: I2OMsgDisable
 *
 * description: Disable the interrupt associated with in/out bound msg
 *              Other previously enabled interrupts are preserved.
 *              return I2OSUCCESS if no error otherwise return I2OMSGINVALID
 *
 * note:
 *  local processor needs to disable its inbound interrupts it is not interested(LOCAL)
 *  PCI master needs to disable outbound interrupts of devices it is not interested(REMOTE)
 *********************************************************************************/
I2OSTATUS I2OMsgDisable( LOCATION loc,      /*  REMOTE/LOCAL   */
			 unsigned int base, /* pcsrbar/eumbbar */
			 unsigned char n )  /* b'1' - msg 0
					     * b'10'- msg 1
					     * b'11'- both
					     */
{
    unsigned int reg, val;

    if ( ( n & 0x3 ) == 0 )
    {
	/* neither msg 0, nor msg 1 */
	return I2OMSGINVALID;
    }

    /* LOCATION - REMOTE : disable outbound message interrupt of device, pcsrbar as base
     *            LOCAL  : disable local inbound message interrupt, eumbbar as base
     */
    reg = ( loc == REMOTE ? I2O_OMIMR : I2O_IMIMR );
    val = load_runtime_reg( base, reg );

    val &= 0xfffffffc; /* masked out the msg interrupt bits */
    val |= ( n & 0x3 );
    store_runtime_reg( base, reg, val );

    return I2OSUCCESS;

}

/**************************************************************************
 * function: I2OMsgGet
 *
 * description: Local processor reads the nth Msg register from its inbound msg,
 *              or a PCI Master reads nth outbound msg from device
 *
 *              return I2OSUCCESS if no error, otherwise return I2OMSGINVALID.
 *
 * note:
 * If it is not local, pcsrbar must be passed to the function. Otherwise eumbbar is passed.
 * If it is remote, outbound msg on the device is read; otherwise local inbound msg is read
 *************************************************************************/
I2OSTATUS I2OMsgGet ( LOCATION loc,             /* REMOTE/LOCAL */
			 unsigned int base,        /*pcsrbar/eumbbar */
			 unsigned int n,           /* 0 or 1 */
			 unsigned int *msg )
{
    if ( n >= I2O_NUM_MSG || msg == 0 )
    {
	return I2OMSGINVALID;
    }

    if ( loc == REMOTE )
    {
	/* read the outbound msg of the device, pcsrbar as base */
	*msg = load_runtime_reg( base, I2O_OMR0+n*I2O_REG_OFFSET );
    }
    else
    {
	/* read the inbound msg sent by PCI master, eumbbar as base */
	*msg = load_runtime_reg( base, I2O_IMR0+n*I2O_REG_OFFSET );
    }

    return I2OSUCCESS;
}

/***************************************************************
 * function: I2OMsgPost
 *
 * description: Kahlua  writes to its nth outbound msg register
 *              PCI master writes to nth inbound msg register of device
 *
 *              return I2OSUCCESS if no error, otherwise return I2OMSGINVALID.
 *
 * note:
 * If it is not local, pcsrbar must be passed to the function. Otherwise eumbbar is passed.
 *
 * If it is remote, inbound msg on the device is written; otherwise local outbound msg is written
 ***************************************************************/
I2OSTATUS I2OMsgPost( LOCATION loc,             /* REMOTE/LOCAL */
		      unsigned int base,        /*pcsrbar/eumbbar */
		      unsigned int n,           /* 0 or 1 */
		      unsigned int msg )
{
    if ( n >= I2O_NUM_MSG )
    {
	return I2OMSGINVALID;
    }

    if ( loc == REMOTE )
    {
	/* write to the inbound msg register of the device, pcsrbar as base  */
	store_runtime_reg( base, I2O_IMR0+n*I2O_REG_OFFSET, msg );
    }
    else
    {
	/* write to the outbound msg register for PCI master to read, eumbbar as base */
	store_runtime_reg( base, I2O_OMR0+n*I2O_REG_OFFSET, msg );
    }

    return I2OSUCCESS;
}

/***********************************************************************
 * function: I2ODBEnable
 *
 * description: Local processor enables it's inbound doorbell interrupt
 *              PCI master enables outbound doorbell interrupt of devices
 *              Other previously enabled interrupts are preserved.
 *              Return I2OSUCCESS if no error otherwise return I2ODBINVALID
 *
 * note:
 * In DoorBell interrupt is generated by PCI master and serviced by local processor
 * Out Doorbell interrupt is generated by local processor and serviced by PCI master
 *
 * Out Doorbell interrupt is generated by local processor and serviced by PCI master
 * PCI master needs to enable the outbound doorbell interrupts of device it wants to handle
 **********************************************************************/
I2OSTATUS I2ODBEnable( LOCATION loc,        /*  REMOTE/LOCAL   */
		  unsigned int base,   /* pcsrbar/eumbbar */
		  unsigned int in_db ) /* when LOCAL, I2O_IN_DB, MC, I2O_IN_DB|MC */
{

    /* LOCATION - REMOTE : PCI master initializes outbound doorbell message of device
     *            LOCAL  : Kahlua initializes its inbound doorbell message
     */
    unsigned int val;

    if ( loc == LOCAL && ( in_db & 0x3 ) == 0 )
    {
	return I2ODBINVALID;
    }

    if ( loc == REMOTE )
    {
	/* pcsrbar is base */
	val = load_runtime_reg( base, I2O_OMIMR );
	val &= 0xfffffff7;
	store_runtime_reg( base, I2O_OMIMR , val );
    }
    else
    {
	/* eumbbar is base */
	val = load_runtime_reg( base, I2O_IMIMR);
	in_db = ( (~in_db) & 0x3 ) << 3;
	val = ( val & 0xffffffe7) | in_db;
	store_runtime_reg( base,  I2O_IMIMR, val );
    }

    return I2OSUCCESS;
}

/**********************************************************************************
 * function: I2ODBDisable
 *
 * description: local processor disables its inbound DoorBell Interrupt
 *              PCI master disables outbound DoorBell interrupt of device
 *              Other previously enabled interrupts are preserved.
 *              return I2OSUCCESS if no error.Otherwise return I2ODBINVALID
 *
 * note:
 * local processor needs to disable its inbound doorbell interrupts it is not interested
 *
 * PCI master needs to disable outbound doorbell interrupts of device it is not interested
 ************************************************************************************/
I2OSTATUS I2ODBDisable( LOCATION loc,          /*  REMOTE/LOCAL   */
			unsigned int base,     /* pcsrbar/eumbbar */
			unsigned int in_db )   /* when LOCAL, I2O_IN_DB, MC, I2O_IN_DB|MC */
{
    /* LOCATION - REMOTE : handle device's out bound message initialization
     *            LOCAL  : handle local in bound message initialization
     */
    unsigned int val;

    if ( loc == LOCAL && ( in_db & 0x3 ) == 0 )
    {
	    return I2ODBINVALID;
    }

    if ( loc == REMOTE )
    {
	/* pcsrbar is the base */
	val = load_runtime_reg( base, I2O_OMIMR );
	val |= 0x8;
	store_runtime_reg( base, I2O_OMIMR, val );
    }
    else
    {
	    val = load_runtime_reg( base, I2O_IMIMR);
	    in_db = ( in_db & 0x3 ) << 3;
	    val |= in_db;
	    store_runtime_reg( base, I2O_IMIMR, val );
    }

    return I2OSUCCESS;
}

/**********************************************************************************
 * function: I2ODBGet
 *
 * description: Local processor reads its in doorbell register,
 *              PCI master reads the outdoorbell register of device.
 *              After a doorbell register is read, the whole register will be cleared.
 *              Otherwise, HW keeps generating interrupt.
 *
 * note:
 * If it is not local, pcsrbar must be passed to the function.
 * Otherwise eumbbar is passed.
 *
 * If it is remote, out doorbell register on the device is read.
 * Otherwise local in doorbell is read
 *
 * If the register is not cleared by write to it, any remaining bit of b'1's
 * will cause interrupt pending.
 *********************************************************************************/
unsigned int I2ODBGet( LOCATION loc,         /*  REMOTE/LOCAL   */
		       unsigned int base)    /* pcsrbar/eumbbar */
{
    unsigned int msg, val;

    if ( loc == REMOTE )
    {
	/* read outbound doorbell register of device, pcsrbar is the base */
	val = load_runtime_reg( base, I2O_ODBR );
	msg = val & 0xe0000000;
	store_runtime_reg( base, I2O_ODBR, val ); /* clear the register */
    }
    else
    {
	/* read the inbound doorbell register, eumbbar is the base */
	val = load_runtime_reg( base, I2O_IDBR );
	store_runtime_reg( base, I2O_IDBR, val ); /* clear the register */
	msg = val;
    }

    return msg;
}

/**********************************************************************
 * function: I2ODBPost
 *
 * description: local processor writes to a outbound doorbell register,
 *              PCI master writes to the inbound doorbell register of device
 *
 * note:
 * If it is not local, pcsrbar must be passed to the function.
 * Otherwise eumbbar is passed.
 *
 * If it is remote, in doorbell register on the device is written.
 * Otherwise local out doorbell is written
 *********************************************************************/
void I2ODBPost( LOCATION loc,             /*  REMOTE/LOCAL   */
		unsigned int base,        /* pcsrbar/eumbbar */
		unsigned int msg )        /*   in   / out    */
{
    if ( loc == REMOTE )
    {
	/* write to inbound doorbell register of device, pcsrbar is the base */
	store_runtime_reg( base, I2O_IDBR, msg );
    }
    else
    {
	/* write to local outbound doorbell register, eumbbar is the base */
	store_runtime_reg( base, I2O_ODBR, msg & 0x1fffffff );
    }

}

/********************************************************************
 * function: I2OOutMsgStatGet
 *
 * description: PCI master reads device's outbound msg unit interrupt status.
 *              Reading an interrupt status register,
 *              the register will be cleared.
 *
 *              The value of the status register is AND with the outbound
 *              interrupt mask and result is returned.
 *
 * note:
 * pcsrbar must be passed to the function.
 ********************************************************************/
I2OSTATUS I2OOutMsgStatGet( unsigned int pcsrbar, I2OOMSTAT *val )
{
    unsigned int stat;
    unsigned int mask;

    if ( val == 0 )
    {
	    return I2OINVALID;
    }

    /* read device's outbound status */
    stat = load_runtime_reg( pcsrbar, I2O_OMISR );
    mask = load_runtime_reg( pcsrbar, I2O_OMIMR );
    store_runtime_reg( pcsrbar, I2O_OMISR, stat & 0xffffffd7);

    stat &= mask;
    val->rsvd0 = ( stat & 0xffffffc0 ) >> 6;
    val->opqi  = ( stat & 0x00000020 ) >> 5;
    val->rsvd1 = ( stat & 0x00000010 ) >> 4;
    val->odi   = ( stat & 0x00000008 ) >> 3;
    val->rsvd2 = ( stat & 0x00000004 ) >> 2;
    val->om1i  = ( stat & 0x00000002 ) >> 1;
    val->om0i  = ( stat & 0x00000001 );

    return I2OSUCCESS;
}

/********************************************************************
 * function: I2OInMsgStatGet
 *
 * description: Local processor reads its inbound msg unit interrupt status.
 *              Reading an interrupt status register,
 *              the register will be cleared.
 *
 *              The inbound msg interrupt status is AND with the inbound
 *              msg interrupt mask and result is returned.
 *
 * note:
 * eumbbar must be passed to the function.
 ********************************************************************/
I2OSTATUS I2OInMsgStatGet(unsigned int eumbbar, I2OIMSTAT *val)
{
    unsigned int stat;
    unsigned int mask;

    if ( val == 0 )
    {
	    return I2OINVALID;
    }

    /* read device's outbound status */
    stat = load_runtime_reg( eumbbar, I2O_OMISR );
    mask = load_runtime_reg( eumbbar, I2O_OMIMR );
    store_runtime_reg( eumbbar, I2O_OMISR, stat & 0xffffffe7 );

    stat &= mask;
    val->rsvd0 = ( stat & 0xfffffe00 ) >> 9;
    val->ofoi  = ( stat & 0x00000100 ) >> 8;
    val->ipoi  = ( stat & 0x00000080 ) >> 7;
    val->rsvd1 = ( stat & 0x00000040 ) >> 6;
    val->ipqi  = ( stat & 0x00000020 ) >> 5;
    val->mci   = ( stat & 0x00000010 ) >> 4;
    val->idi   = ( stat & 0x00000008 ) >> 3;
    val->rsvd2 = ( stat & 0x00000004 ) >> 2;
    val->im1i  = ( stat & 0x00000002 ) >> 1;
    val->im0i  = ( stat & 0x00000001 );

    return I2OSUCCESS;

}

/***********************************************************
 * function: I2OFIFOInit
 *
 * description: Configure the I2O FIFO, including QBAR,
 *              IFHPR/IFTPR, IPHPR/IPTPR, OFHPR/OFTPR,
 *              OPHPR/OPTPR, MUCR.
 *
 *              return I2OSUCCESS if no error,
 *              otherwise return I2OQUEINVALID
 *
 * note: It is NOT this driver's responsibility of initializing
 *       MFA blocks, i.e., FIFO queue itself. The MFA blocks
 *       must be initialized before I2O unit can be used.
 ***********************************************************/
I2OSTATUS I2OFIFOInit( unsigned int eumbbar,
		       QUEUE_SIZE   sz,      /* value of CQS of MUCR */
		       unsigned int qba)     /* queue base address that must be aligned at 1M */
{

    if ( ( qba & 0xfffff ) != 0 )
    {
	/* QBA must be aligned at 1Mbyte boundary */
	return I2OQUEINVALID;
    }

    store_runtime_reg( eumbbar, I2O_QBAR, qba );
    store_runtime_reg( eumbbar, I2O_MUCR, (unsigned int)sz );
    store_runtime_reg( eumbbar, I2O_IFHPR, qba );
    store_runtime_reg( eumbbar, I2O_IFTPR, qba );
    store_runtime_reg( eumbbar, I2O_IPHPR, qba + 1 * ( sz << 11 ));
    store_runtime_reg( eumbbar, I2O_IPTPR, qba + 1 * ( sz << 11 ));
    store_runtime_reg( eumbbar, I2O_OFHPR, qba + 2 * ( sz << 11 ));
    store_runtime_reg( eumbbar, I2O_OFTPR, qba + 2 * ( sz << 11 ));
    store_runtime_reg( eumbbar, I2O_OPHPR, qba + 3 * ( sz << 11 ));
    store_runtime_reg( eumbbar, I2O_OPTPR, qba + 3 * ( sz << 11 ));

    fifo_stat.qsz = sz;
    fifo_stat.qba = qba;

    return I2OSUCCESS;
}

/**************************************************
 * function: I2OFIFOEnable
 *
 * description: Enable the circular queue
 *              return I2OSUCCESS if no error.
 *              Otherwise I2OQUEINVALID is returned.
 *
 * note:
 *************************************************/
I2OSTATUS I2OFIFOEnable( unsigned int eumbbar )
{
    unsigned int val;

    if ( fifo_stat.qba == 0xfffffff )
    {
	return I2OQUEINVALID;
    }

    val = load_runtime_reg( eumbbar, I2O_MUCR );
    store_runtime_reg( eumbbar, I2O_MUCR, val | 0x1 );

    return I2OSUCCESS;
}

/**************************************************
 * function: I2OFIFODisable
 *
 * description: Disable the circular queue
 *
 * note:
 *************************************************/
void I2OFIFODisable( unsigned int eumbbar )
{
    if ( fifo_stat.qba == 0xffffffff )
    {
	/* not enabled */
	return;
    }

    unsigned int val = load_runtime_reg( eumbbar, I2O_MUCR );
    store_runtime_reg( eumbbar, I2O_MUCR, val & 0xfffffffe );
}

/****************************************************
 * function: I2OFIFOAlloc
 *
 * description: Allocate a free MFA from free FIFO.
 *              return I2OSUCCESS if no error.
 *              return I2OQUEEMPTY if no more free MFA.
 *              return I2OINVALID on other errors.
 *
 *              A free MFA must be allocated before a
 *              message can be posted.
 *
 * note:
 * PCI Master allocates a free MFA from inbound queue of device
 * (pcsrbar is the base,) through the inbound queue port of device
 * while local processor allocates a free MFA from its outbound
 * queue (eumbbar is the base.)
 *
 ****************************************************/
I2OSTATUS I2OFIFOAlloc( LOCATION loc,
			unsigned int base,
			void         **pMsg )
{
    I2OSTATUS stat = I2OSUCCESS;
    void *pHdr, *pTil;

    if ( pMsg == 0 || *pMsg == 0 || fifo_stat.qba == 0xffffffff )
    {
	/* not configured */
	return I2OQUEINVALID;
    }

    if ( loc == REMOTE )
    {
	/* pcsrbar is the base and read the inbound free tail ptr */
	pTil = (void *)load_runtime_reg( base, I2O_IFQPR );
	if ( ( (unsigned int)pTil & 0xFFFFFFF ) == 0xFFFFFFFF )
	{
	    stat = I2OQUEEMPTY;
	}
	else
	{
	    *pMsg = pTil;
	}
    }
    else
    {
	/* eumbbar is the base and read the outbound free tail ptr */
	pHdr = (void *)load_runtime_reg( base, I2O_OFHPR ); /* queue head */
	pTil = (void *)load_runtime_reg( base, I2O_OFTPR ); /* queue tail */

	/* check underflow */
	if ( pHdr == pTil )
	{
	    /* hdr and til point to the same fifo item, no free MFA */
	    stat = I2OQUEEMPTY;
	}
	else
	{
	  /* update OFTPR */
	  *pMsg = (void *)(*(unsigned char *)pTil);
	  pTil = (void *)((unsigned int)pTil + 4);
	  if ( (unsigned int)pTil == fifo_stat.qba + ( 4 * ( fifo_stat.qsz << 11 ) ) )
	  {
		/* reach the upper limit */
		pTil = (void *)(fifo_stat.qba + ( 3 * (fifo_stat.qsz << 11) ));
	  }
	  store_runtime_reg( base, I2O_OFTPR, (unsigned int)pTil );
	}
    }

    return stat;
}

/******************************************************
 * function: I2OFIFOFree
 *
 * description: Free a used MFA back to free queue after
 *              use.
 *              return I2OSUCCESS if no error.
 *              return I2OQUEFULL if inbound free queue
 *              overflow
 *
 * note: PCI Master frees a MFA into device's outbound queue
 *       (OFQPR) while local processor frees a MFA into its
 *       inbound queue (IFHPR).
 *****************************************************/
I2OSTATUS I2OFIFOFree( LOCATION loc,
		  unsigned int base,
		  void *pMsg )
{
    void **pHdr, **pTil;
    I2OSTATUS stat = I2OSUCCESS;

    if ( fifo_stat.qba == 0xffffffff || pMsg == 0 )
    {
	    return I2OQUEINVALID;
    }

    if ( loc == REMOTE )
    {
	/* pcsrbar is the base */
	store_runtime_reg( base, I2O_OFQPR, (unsigned int)pMsg );
    }
    else
    {
	/* eumbbar is the base */
	pHdr = (void **)load_runtime_reg( base, I2O_IFHPR );
	pTil = (void **)load_runtime_reg( base, I2O_IFTPR );

	/* store MFA */
	*pHdr = pMsg;

	/* update IFHPR */
	pHdr += 4;

	if ( (unsigned int)pHdr == fifo_stat.qba + ( fifo_stat.qsz << 11 ) )
	{
	  /* reach the upper limit */
	  pHdr = (void **)fifo_stat.qba;
	}

	/* check inbound free queue overflow */
	if ( pHdr != pTil )
	{
	   store_runtime_reg( base, I2O_OPHPR, (unsigned int)pHdr);
	}
	else
	{
	    stat = I2OQUEFULL;
	}

    }

    return stat;

}

/*********************************************
 * function: I2OFIFOPost
 *
 * description: Post a msg into FIFO post queue
 *              the value of msg must be the one
 *              returned by I2OFIFOAlloc
 *
 * note: PCI Master posts a msg into device's inbound queue
 *       (IFQPR) while local processor post a msg into device's
 *       outbound queue (OPHPR)
 *********************************************/
I2OSTATUS I2OFIFOPost( LOCATION loc,
		       unsigned int base,
		       void *pMsg )
{
    void **pHdr, **pTil;
    I2OSTATUS stat = I2OSUCCESS;

    if ( fifo_stat.qba == 0xffffffff || pMsg == 0 )
    {
	return I2OQUEINVALID;
    }

    if ( loc == REMOTE )
    {
	/* pcsrbar is the base */
	store_runtime_reg( base, I2O_IFQPR, (unsigned int)pMsg );
    }
    else
    {
	/* eumbbar is the base */
	pHdr = (void **)load_runtime_reg( base, I2O_OPHPR );
	pTil = (void **)load_runtime_reg( base, I2O_OPTPR );

	/* store MFA */
	*pHdr = pMsg;

	/* update IFHPR */
	pHdr += 4;

	if ( (unsigned int)pHdr == fifo_stat.qba + 3 * ( fifo_stat.qsz << 11 ) )
	{
	  /* reach the upper limit */
	  pHdr = (void **)(fifo_stat.qba + 2 * ( fifo_stat.qsz << 11 ) );
	}

	/* check post queue overflow */
	if ( pHdr != pTil )
	{
	   store_runtime_reg( base, I2O_OPHPR, (unsigned int)pHdr);
	}
	else
	{
	    stat = I2OQUEFULL;
	}
    }

    return stat;
}

/************************************************
 * function: I2OFIFOGet
 *
 * description:  Read a msg from FIFO
 *               This function should be called
 *               only when there is a corresponding
 *               msg interrupt.
 *
 * note: PCI Master reads a msg from device's outbound queue
 *       (OFQPR) while local processor reads a msg from device's
 *       inbound queue (IPTPR)
 ************************************************/
I2OSTATUS I2OFIFOGet( LOCATION loc,
		       unsigned int base,
		       void **pMsg )
{
    I2OSTATUS stat = I2OSUCCESS;
    void *pHdr, *pTil;

    if ( pMsg == 0 || *pMsg == 0 || fifo_stat.qba == 0xffffffff )
    {
	/* not configured */
	return I2OQUEINVALID;
    }

    if ( loc == REMOTE )
    {
	/* pcsrbar is the base */
	pTil = (void *)load_runtime_reg( base, I2O_OFQPR );
	if ( ( (unsigned int)pTil & 0xFFFFFFF ) == 0xFFFFFFFF )
	{
	    stat = I2OQUEEMPTY;
	}
	else
	{
	    *pMsg = pTil;
	}
    }
    else
    {
	/* eumbbar is the base and read the outbound free tail ptr */
	pHdr = (void *)load_runtime_reg( base, I2O_IPHPR ); /* queue head */
	pTil = (void *)load_runtime_reg( base, I2O_IPTPR ); /* queue tail */

	/* check underflow */
	if ( pHdr == pTil )
	{
	    /* no free MFA */
	    stat = I2OQUEEMPTY;
	}
	else
	{
	  /* update OFTPR */
	  *pMsg = (void *)(*(unsigned char *)pTil);
	  pTil = (void *)((unsigned int)pTil + 4);
	  if ( (unsigned int)pTil == fifo_stat.qba + 2 * ( fifo_stat.qsz << 11 ) )
	  {
		/* reach the upper limit */
		pTil = (void *)(fifo_stat.qba + 1 * (fifo_stat.qsz << 11) );
	  }

	  store_runtime_reg( base, I2O_IPTPR, (unsigned int)pTil );
	}
    }

    return stat;
}

/********************************************************
 * function: I2OIOP
 *
 * description: Get the I2O PCI configuration identification
 *              register.
 *
 * note: PCI master should pass pcsrbar while local processor
 *       should pass eumbbar.
 *********************************************************/
I2OSTATUS I2OPCIConfigGet( LOCATION loc,
			unsigned int base,
			I2OIOP * val)
{
    unsigned int tmp;
    if ( val == 0 )
    {
	    return I2OINVALID;
    }
    tmp = load_runtime_reg( base, PCI_CFG_CLA );
    val->base_class = ( tmp & 0xFF) << 16;
    tmp = load_runtime_reg( base, PCI_CFG_SCL );
    val->sub_class= ( (tmp & 0xFF) << 8 );
    tmp = load_runtime_reg( base, PCI_CFG_PIC );
    val->prg_code = (tmp & 0xFF);
    return I2OSUCCESS;
}

/*********************************************************
 * function: I2OFIFOIntEnable
 *
 * description: Enable the circular post queue interrupt
 *
 * note:
 * PCI master enables outbound FIFO interrupt of device
 * pscrbar is the base
 * Device enables its inbound FIFO interrupt
 * eumbbar is the base
 *******************************************************/
void I2OFIFOIntEnable( LOCATION loc, unsigned int base  )
{
    unsigned int reg, val;

    /* LOCATION - REMOTE : enable outbound message of device, pcsrbar as base
     *            LOCAL  : enable local inbound message, eumbbar as base
     */
    reg = ( loc == REMOTE ? I2O_OMIMR : I2O_IMIMR );
    val = load_runtime_reg( base, reg );

    val &= 0xffffffdf; /* clear the msg interrupt bits */
    store_runtime_reg( base, reg, val );

}

/****************************************************
 * function: I2OFIFOIntDisable
 *
 * description: Disable the circular post queue interrupt
 *
 * note:
 * PCI master disables outbound FIFO interrupt of device
 * (pscrbar is the base)
 * Device disables its inbound FIFO interrupt
 * (eumbbar is the base)
 *****************************************************/
void I2OFIFOIntDisable( LOCATION loc, unsigned int base )
{

    /* LOCATION - REMOTE : disable outbound message interrupt of device, pcsrbar as base
     *            LOCAL  : disable local inbound message interrupt, eumbbar as base
     */
    unsigned int reg = ( loc == REMOTE ? I2O_OMIMR : I2O_IMIMR );
    unsigned int val = load_runtime_reg( base, reg );

    val |= 0x00000020; /* masked out the msg interrupt bits */
    store_runtime_reg( base, reg, val );

}

/*********************************************************
 * function: I2OFIFOOverflowIntEnable
 *
 * description: Enable the circular queue overflow interrupt
 *
 * note:
 * Device enables its inbound FIFO post overflow interrupt
 * and outbound free overflow interrupt.
 * eumbbar is the base
 *******************************************************/
void I2OFIFOOverflowIntEnable( unsigned int eumbbar  )
{
    unsigned int val = load_runtime_reg( eumbbar, I2O_IMIMR );

    val &= 0xfffffe7f; /* clear the two overflow interrupt bits */
    store_runtime_reg( eumbbar, I2O_IMIMR, val );

}

/****************************************************
 * function: I2OFIFOOverflowIntDisable
 *
 * description: Disable the circular queue overflow interrupt
 *
 * note:
 * Device disables its inbound post FIFO overflow interrupt
 * and outbound free FIFO overflow interrupt
 * (eumbbar is the base)
 *****************************************************/
void I2OFIFOOverflowIntDisable( unsigned int eumbbar )
{

    unsigned int val = load_runtime_reg( eumbbar, I2O_IMIMR );

    val |= 0x00000180; /* masked out the msg overflow interrupt bits */
    store_runtime_reg( eumbbar, I2O_IMIMR, val );
}
