/********************************************************* * $Id * * copyright @ Motorola, 1999 *********************************************************/ #include "i2o.h" extern unsigned int get_kahlua_pcsrbar( ); /* for I2O_ISR_host */ extern unsigned int get_eumbbar( ); /* for I2O_ISR_agent */ 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 * 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, clear the ODIM bit to enable interrupt */ val = load_runtime_reg( base, I2O_OMIMR ); val &= 0xfffffff7; store_runtime_reg( base, I2O_OMIMR , val ); } else { /* eumbbar is base, clear the IDIM and/or MCIM bit(s) to enable * interrupt */ 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; /* set ODIM to disable */ store_runtime_reg( base, I2O_OMIMR, val ); } else { val = load_runtime_reg( base, I2O_IMIMR); in_db = ( in_db & 0x3 ) << 3; val |= in_db; /* set IDIM and/or MC to disable */ 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: I2O_ISR_host * * This function is a very simple interrupt service routine to be * installed in the host. It must poll the Kahlua agent to * determine the cause of the interrupt, without knowing anything * about the nature. Note that the agent's ISR is already aware * that the interrupt is for the Message unit. At first, this ISR * only looks at the message unit, but it must be extended in the * future to examine any part of the Kahlua's status registers for * any kind of expected interrupt. If it doesn't find a cause, it * reports that an unexpected interrupt occurred. * ********************************************************************/ extern int dink_printf( unsigned char *, ... ); #define PRINT dink_printf void I2O_ISR_host () { unsigned int kahlua_pcsrbar = get_kahlua_pcsrbar(); unsigned int found_cause = 0; I2OOMSTAT i2o_status; unsigned int db_reg_content; /* Try the Message Unit status */ if (I2OOutMsgStatGet(kahlua_pcsrbar, &i2o_status) != I2OSUCCESS) PRINT("Unable to read Kahlua's Message Unit status.\n"); else { /* check the doorbell status */ if (i2o_status.odi) { PRINT("Doorbell interrupt detected:\n"); db_reg_content = I2ODBGet(REMOTE,kahlua_pcsrbar); PRINT("Agent's outbound doorbell register: 0x%x\n",db_reg_content); found_cause++; } /* check for Outbound Message 0/1 and Outbound Post Queue interrupts here */ } /* can't find the cause */ if (found_cause == 0) PRINT("A non-Message Unit external interrupt has been detected.\n"); return; } /******************************************************************** * function: I2O_ISR_agent * * This function is a very simple interrupt service routine to be * installed in the Kahlua agent. It knows that it is servicing * Message Unit interrupts only. Initially, it only reports doorbell * interrupts, but should be extended to handle message 0/1 and * the inbound FIFO. * If it doesn't find a cause, it reports that an unexpected message * unit interrupt occurred. * ********************************************************************/ void I2O_ISR_agent () { unsigned int eumbbar = get_eumbbar(); unsigned int found_cause = 0; I2OIMSTAT i2o_status; unsigned int db_reg_content; /* Get the Inbound Message Unit status */ if (I2OInMsgStatGet(eumbbar, &i2o_status) != I2OSUCCESS) PRINT("Unable to read Inbound Message Unit status.\n"); else { #ifdef I2O_DBG union{ I2OIMSTAT status; unsigned int raw; } s; s.status = i2o_status; PRINT("inbound message status register, masked: 0x%x\n",s.raw); #endif /* check the doorbell status */ if (i2o_status.idi) { PRINT("Doorbell interrupt detected:\n"); db_reg_content = I2ODBGet(LOCAL,eumbbar); PRINT("Agent's inbound doorbell register: 0x%x\n",db_reg_content); found_cause++; } /* handle Inbound Message 0/1 and Outbound Post Queue interrupts here */ } /* can't find the cause */ if (found_cause == 0) PRINT("Unable to determine cause of Inbound Message Unit interrupt.\n"); return; } /******************************************************************** * function: I2OOutMsgStatGet * * description: PCI master reads device's outbound msg unit interrupt status. * Reading an interrupt status register, * the register will be cleared. * Only the two outbound message interrupt bits (OM1I and OM0I) are cleared, * the other bits are reserved (so leave them alone), or are read only and * are cleared by reading all MFA's or by the host writing 1's to the outbound * doorbell bits. * * The value of the status register is AND with the outbound * interrupt mask and result is returned. * The outbound message interrupt mask register has a 1 to mask an interrupt * and a 0 to allow an interrupt, so the mask has to be complemented prior * to the AND operation. Also note that the OPQI bit may be set irrespective * of the OPQIM bit setting. I don't see why there is a OPQIM bit in the * OMIMR, but this is what book IV says. Since the OPQI status cannot be masked, * do not use the OPQIM bit from the OMIMR. * * note: * pcsrbar must be passed to the function. ********************************************************************/ I2OSTATUS I2OOutMsgStatGet( unsigned int pcsrbar, I2OOMSTAT *val ) { union { unsigned int raw_value; I2OOMSTAT status; } s; unsigned int mask; if ( val == 0 ) { return I2OINVALID; } /* read device's outbound status */ s.raw_value = load_runtime_reg( pcsrbar, I2O_OMISR ); mask = load_runtime_reg( pcsrbar, I2O_OMIMR ); /* Use only the ODIM and OM1IM and OM0IM bits, OPQIM doesn't do * anything, others are reserved. */ mask &= 0xb; /* only clear the OM1I and OM0I status bits */ store_runtime_reg( pcsrbar, I2O_OMISR, s.raw_value & 0x3); /* complement the mask, a 0 means the interrupt is NOT masked */ s.raw_value &= ~mask; /* use status register mapping part of the union */ *val = s.status; return I2OSUCCESS; } /******************************************************************** * function: I2OInMsgStatGet * * description: Local processor reads its inbound msg unit interrupt status. * Reading an interrupt status register, * the register will be cleared. * Only the defined bits in the register are cleared. * * The inbound msg interrupt status is AND with the inbound * msg interrupt mask and result is returned. * As in the outbound, the mask register has a 0 to NOT mask the status, * so the mask register is complemented prior to the AND operation. * * note: * eumbbar must be passed to the function. ********************************************************************/ I2OSTATUS I2OInMsgStatGet(unsigned int eumbbar, I2OIMSTAT *val) { union { unsigned int raw_value; I2OIMSTAT status; } s; unsigned int mask; if ( val == 0 ) { return I2OINVALID; } /* read device's inbound status */ s.raw_value = load_runtime_reg( eumbbar, I2O_IMISR ); #ifdef I2O_DBG PRINT("inbound message status register, not masked: 0x%x\n",s.raw_value); #endif mask = load_runtime_reg( eumbbar, I2O_IMIMR ); /* only use defined parts of the mask register, ignore reserved parts */ mask &= 0x1bb; /* When resetting, leave the reserved parts alone and do not write to * the MCI and IDI bits which are cleared by writing to the doorbell * register, not the status bits. */ store_runtime_reg( eumbbar, I2O_IMISR, s.raw_value & 0x1a3 ); /* use the complement of the mask, a 0 means NOT mask the status bit */ s.raw_value &= ~mask; /* use the status register mapping part of the union */ *val = s.status; 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 ) { unsigned int val = load_runtime_reg( eumbbar, I2O_MUCR ); if ( fifo_stat.qba == 0xffffffff ) { /* not enabled */ return; } 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 ); }