diff options
author | Matthew Barth <msbarth@us.ibm.com> | 2012-02-17 15:50:22 -0600 |
---|---|---|
committer | A. Patrick Williams III <iawillia@us.ibm.com> | 2012-03-06 10:18:58 -0600 |
commit | f538d4cbe355a98be2675468da6f3a24078893e8 (patch) | |
tree | 2eca68492641c61f8714f6d7ed790ece29a1e969 /src/usr/mbox/mboxdd.C | |
parent | 18036e00119f1b3757678331173a02eb71b5d179 (diff) | |
download | blackbird-hostboot-f538d4cbe355a98be2675468da6f3a24078893e8.tar.gz blackbird-hostboot-f538d4cbe355a98be2675468da6f3a24078893e8.zip |
Base Mailbox device driver read/write function
Change-Id: I71cee7950d4dff6279422b6ee7fbcc94dcfaf8df
Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/679
Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
Tested-by: Jenkins Server
Diffstat (limited to 'src/usr/mbox/mboxdd.C')
-rw-r--r-- | src/usr/mbox/mboxdd.C | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/src/usr/mbox/mboxdd.C b/src/usr/mbox/mboxdd.C new file mode 100644 index 000000000..441925fd1 --- /dev/null +++ b/src/usr/mbox/mboxdd.C @@ -0,0 +1,541 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/usr/mbox/mboxdd.C $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2012 +// +// p1 +// +// Object Code Only (OCO) source materials +// Licensed Internal Code Source Materials +// IBM HostBoot Licensed Internal Code +// +// The source code for this program is not published or other- +// wise divested of its trade secrets, irrespective of what has +// been deposited with the U.S. Copyright Office. +// +// Origin: 30 +// +// IBM_PROLOG_END +#include "mboxdd.H" +#include <mbox/mboxif.H> +#include <mbox/mbox_reasoncodes.H> +#include <devicefw/driverif.H> +#include <trace/interface.H> +#include <errl/errlentry.H> +#include <targeting/targetservice.H> + +trace_desc_t* g_trac_mbox = NULL; +TRAC_INIT(&g_trac_mbox, "MBOX", 4096); //4K + +//TODO - May or may not be necessary for MBOX error logs +uint64_t target_to_uint64(const TARGETING::Target* i_target) +{ + uint64_t id = 0; + if( i_target == NULL ) + { + id = 0x0; + } + else if( i_target == TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ) + { + id = 0xFFFFFFFFFFFFFFFF; + } + else + { + // physical path, 3 nibbles per type/instance pair + // TIITIITII... etc. + TARGETING::EntityPath epath; + i_target->tryGetAttr<TARGETING::ATTR_PHYS_PATH>(epath); + for( uint32_t x = 0; x < epath.size(); x++ ) + { + id = id << 12; + id |= (uint64_t)((epath[x].type << 8) & 0xF00); + id |= (uint64_t)(epath[x].instance & 0x0FF); + } + } + return id; +} + +namespace MBOX +{ + +/** + * @brief Performs an MBOX Read Operation + * This function performs a MBOX Read operation. It follows a pre-defined + * prototype functions in order to be registered with the device-driver + * framework. + * + * @param[in] i_opType Operation type, see DeviceFW::OperationType + * in driverif.H + * @param[in] i_target MBOX target + * @param[in/out] io_buffer Read: Pointer to output data storage + * Write: Pointer to input data storage + * @param[in/out] io_buflen Input: size of io_buffer (in bytes) + * Output: + * Read: Size of output data + * Write: Size of data written + * @param[in] i_accessType DeviceFW::AccessType enum (userif.H) + * @param[in] i_args This is an argument list for DD framework. + * + * @return errlHndl_t + */ +errlHndl_t ddRead(DeviceFW::OperationType i_opType, + TARGETING::Target* i_target, + void* io_buffer, + size_t& io_buflen, + int64_t i_accessType, + va_list i_args) +{ + errlHndl_t l_err = NULL; + uint64_t* o_status = va_arg(i_args,uint64_t*); + + do + { + l_err = Singleton<MboxDD>::instance().read(i_target,io_buffer, + io_buflen,o_status); + if (l_err) + { + break; + } + } while(0); + + return l_err; +} + +/** + * @brief Performs an MBOX Write Operation + * This function performs a MBOX Write operation. It follows a pre-defined + * prototype functions in order to be registered with the device-driver + * framework. + * + * @param[in] i_opType Operation type, see DeviceFW::OperationType + * in driverif.H + * @param[in] i_target MBOX target + * @param[in/out] io_buffer Read: Pointer to output data storage + * Write: Pointer to input data storage + * @param[in/out] io_buflen Input: size of io_buffer (in bytes) + * Output: + * Read: Size of output data + * Write: Size of data written + * @param[in] i_accessType DeviceFW::AccessType enum (userif.H) + * @param[in] i_args This is an argument list for DD framework. + * + * @return errlHndl_t + */ +errlHndl_t ddWrite(DeviceFW::OperationType i_opType, + TARGETING::Target* i_target, + void* io_buffer, + size_t& io_buflen, + int64_t i_accessType, + va_list i_args) +{ + errlHndl_t l_err = NULL; + + do + { + l_err = Singleton<MboxDD>::instance().write(i_target,io_buffer, + io_buflen); + if(l_err) + { + break; + } + } while(0); + + return l_err; +} + +// Register MBOXDD access functions to DD framework +DEVICE_REGISTER_ROUTE(DeviceFW::READ, + DeviceFW::MAILBOX, + TARGETING::TYPE_PROC, + ddRead); + +DEVICE_REGISTER_ROUTE(DeviceFW::WRITE, + DeviceFW::MAILBOX, + TARGETING::TYPE_PROC, + ddWrite); + +}; //end MBOX namespace + +/** + * @brief Performs a mailbox read operation + */ +errlHndl_t MboxDD::read(TARGETING::Target* i_target,void *o_buffer, + size_t &io_buflen,uint64_t* o_status) +{ + uint64_t l_stat = 0; + errlHndl_t l_err = NULL; + uint32_t l_64bitBuf[2] = {0}; + uint32_t l_IntReg[2] = {0}; + size_t l_64bitSize = sizeof(uint64_t); + + do + { + if (MBOX::MBOX_MAX_DATA_BYTES < io_buflen) + { + TRACFCOMP(g_trac_mbox, ERR_MRK "MBOX::read> Invalid data length : io_buflen=%d", io_buflen); + /*@ + * @errortype + * @moduleid MBOX::MOD_MBOXDD_READ + * @reasoncode MBOX::RC_INVALID_LENGTH + * @userdata1 Target ID String... + * @userdata2 Data Length + * @devdesc MboxDD::read> Invalid data length (< msg_t size) + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + MBOX::MOD_MBOXDD_READ, + MBOX::RC_INVALID_LENGTH, + target_to_uint64(i_target), + TO_UINT64(io_buflen)); + l_err->collectTrace("MBOX",1024); + break; + } + + l_err = deviceOp(DeviceFW::READ,i_target, + l_IntReg,l_64bitSize, + DEVICE_XSCOM_ADDRESS(MBOX_DB_INT_REG_PIB)); + if (l_err) + { + TRACFCOMP(g_trac_mbox, ERR_MRK "MBOX::read> Unable to read PIB Interrupt Register"); + break; + } + if ((l_IntReg[0] & MBOX::MBOX_DOORBELL_ERROR) == + MBOX::MBOX_DOORBELL_ERROR) + { + TRACFCOMP(g_trac_mbox, INFO_MRK "MBOX::read> Found interrupt on error status register"); + l_err = getErrStat(i_target,l_stat); + if (l_err) + { + break; + } + } + + l_err = deviceOp(DeviceFW::READ,i_target, + l_64bitBuf,l_64bitSize, + DEVICE_XSCOM_ADDRESS(MBOX_DB_STAT_CNTRL_1)); + if (l_err) + { + TRACFCOMP(g_trac_mbox, ERR_MRK "MBOX::read> Unable to read Doorbell Status/Control Register"); + break; + } + /* + * DB_STATUS_1A_REG: doorbell status and control 1a + * Bit31(MSB) : Permission to Send Doorbell 1 + * Bit30 : Abort Doorbell 1 + * Bit29 : PIB Slave B Pending Doorbell 1 + * Bit28 : LBUS Slave A Pending Doorbell 1 + * Bit27 : Reserved + * Bit26 : Xdn Doorbell 1 + * Bit25 : Xup Doorbell 1 + * Bit24 : Reserved + * Bit23-20 : Header Count LBUS Slave A Doorbell 1 + * Bit19-12 : Data Count LBUS Slave A Doorbell 1 + * Bit11-8 : Header Count PIB Slave B Doorbell 1 + * Bit7-0 : Data Count PIB Slave B Doorbell 1 + */ + //Check for Abort + if ((l_IntReg[0] & MBOX::MBOX_ABORT_LAST_MSG) == + MBOX::MBOX_ABORT_LAST_MSG && + (l_64bitBuf[0] & 0x40000000) == 0x40000000) + { + l_stat |= MBOX::MBOX_ABORT_LAST_MSG; + } + //Check for Xdn + if ((l_IntReg[0] & MBOX::MBOX_XDN_ACK) == + MBOX::MBOX_XDN_ACK && + (l_64bitBuf[0] & 0x04000000) == 0x04000000) + { + l_stat |= MBOX::MBOX_XDN_ACK; + } + //Check for PIB Pending + if ((l_IntReg[0] & MBOX::MBOX_DATA_PENDING) == + MBOX::MBOX_DATA_PENDING && + (l_64bitBuf[0] & 0x20000000) == 0x20000000) + { + l_stat |= MBOX::MBOX_DATA_PENDING; + //Read how many bytes are significant + if (io_buflen < ((l_64bitBuf[0] & 0x000FF000) >> 12)) + { + TRACFCOMP(g_trac_mbox, INFO_MRK "MBOX::read> Data truncated, input buffer length less than number of significant bytes"); + } + uint32_t i = 0; + uint32_t l_data[2]; + uint8_t l_numRegs = (io_buflen*sizeof(uint8_t))/sizeof(uint32_t); + if ((io_buflen*sizeof(uint8_t))%sizeof(uint32_t) != 0) l_numRegs++; + uint8_t *l_buf = static_cast<uint8_t *>(o_buffer); + //Extract Data + while (i < l_numRegs) + { + l_err = deviceOp(DeviceFW::READ,i_target, + l_data,l_64bitSize, + DEVICE_XSCOM_ADDRESS(MBOX_DATA_LBUS_START+i)); + if (l_err) + { + TRACFCOMP(g_trac_mbox, ERR_MRK "MBOX::read> Unable to read Data Area Register 0x%X",MBOX_DATA_LBUS_START+i); + break; + } + //TODO Use memcpy() since byte aligned? + for (uint8_t byteNum = 0;byteNum<sizeof(uint32_t);++byteNum) + { + *(l_buf+(byteNum+i*sizeof(uint32_t))) = + (l_data[0]>>(sizeof(uint32_t)*8-(byteNum+1)*8) & 0x000000FF); + } + i++; + } + if (l_err) + { + break; + } + + //Write-to-Clear PIB Pending,Xup,and bits 23-12 + l_64bitBuf[0] = 0x22FFF000; + l_err = deviceOp(DeviceFW::WRITE,i_target, + l_64bitBuf,l_64bitSize, + DEVICE_XSCOM_ADDRESS(MBOX_DB_STAT_CNTRL_1)); + if (l_err) + { + TRACFCOMP(g_trac_mbox, ERR_MRK "MBOX::read> Unable to clear Doorbell Status/Control Register"); + break; + } + } + + //Write-to-Clear 'on' bits of interrupt reg + l_err = deviceOp(DeviceFW::WRITE,i_target, + l_IntReg,l_64bitSize, + DEVICE_XSCOM_ADDRESS(MBOX_DB_INT_REG_PIB)); + if (l_err) + { + TRACFCOMP(g_trac_mbox, ERR_MRK "MBOX::read> Unable to clear PIB Interrupt Register"); + break; + } + (*o_status) = l_stat; + + } while(0); + + return l_err; +} + +/** + * @brief Performs a mailbox write operation + */ +errlHndl_t MboxDD::write(TARGETING::Target* i_target,void* i_buffer, + size_t& i_buflen) +{ + errlHndl_t l_err = NULL; + uint32_t l_64bitBuf[2] = {0}; + size_t l_64bitSize = sizeof(uint64_t); + + do + { + //Expect size in bytes + if (i_buflen > MBOX::MBOX_MAX_DATA_BYTES) + { + TRACFCOMP(g_trac_mbox, ERR_MRK "MBOX::write> Invalid data length : i_buflen=%d", i_buflen); + /*@ + * @errortype + * @moduleid MBOX::MOD_MBOXDD_WRITE + * @reasoncode MBOX::RC_INVALID_LENGTH + * @userdata1 Target ID String... + * @userdata2 Data Length + * @devdesc MboxDD::write> Invalid data length (> msg_t size) + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + MBOX::MOD_MBOXDD_WRITE, + MBOX::RC_INVALID_LENGTH, + target_to_uint64(i_target), + TO_UINT64(i_buflen)); + l_err->collectTrace("MBOX",1024); + break; + } + + l_err = deviceOp(DeviceFW::READ,i_target, + l_64bitBuf,l_64bitSize, + DEVICE_XSCOM_ADDRESS(MBOX_DB_STAT_CNTRL_1)); + if (l_err) + { + TRACFCOMP(g_trac_mbox, ERR_MRK "MBOX::write> Unable to read Doorbell Status/Control Register"); + break; + } + + /* + * DB_STATUS_1A_REG: doorbell status and control 1a + * Bit31(MSB) : Permission to Send Doorbell 1 + * Bit30 : Abort Doorbell 1 + * Bit29 : PIB Slave B Pending Doorbell 1 + * Bit28 : LBUS Slave A Pending Doorbell 1 + * Bit27 : Reserved + * Bit26 : Xdn Doorbell 1 + * Bit25 : Xup Doorbell 1 + * Bit24 : Reserved + * Bit23-20 : Header Count LBUS Slave A Doorbell 1 + * Bit19-12 : Data Count LBUS Slave A Doorbell 1 + * Bit11-8 : Header Count PIB Slave B Doorbell 1 + * Bit7-0 : Data Count PIB Slave B Doorbell 1 + */ + //Verify Permission-to-Send + if ((l_64bitBuf[0] & 0x80000000) == 0x80000000) + { + //Verify LBUS Pending, + if ((l_64bitBuf[0] & 0x10000FFF) == 0) + { + uint32_t i = 0; + uint32_t l_data[2] = {0}; + uint8_t l_numRegs = (i_buflen*sizeof(uint8_t))/sizeof(uint32_t); + if ((i_buflen*sizeof(uint8_t))%sizeof(uint32_t) != 0) l_numRegs++; + uint32_t *l_buf = static_cast<uint32_t *>(i_buffer); + //Write Data registers + while (i < l_numRegs) + { + l_data[0] = *(l_buf+i); + l_err = deviceOp(DeviceFW::WRITE,i_target, + l_data,l_64bitSize, + DEVICE_XSCOM_ADDRESS(MBOX_DATA_PIB_START+i)); + if (l_err) + { + TRACFCOMP(g_trac_mbox, ERR_MRK "MBOX::write> Unable to write Data Area Register 0x%X",MBOX_DATA_PIB_START+i); + break; + } + i++; + } + if (l_err) + { + break; + } + + //Write LBUS Pending(28) and Data Count bits(11-0) + l_64bitBuf[0] = 0x10000000 | i_buflen; + l_err = deviceOp(DeviceFW::WRITE,i_target, + l_64bitBuf,l_64bitSize, + DEVICE_XSCOM_ADDRESS(MBOX_DB_STAT_CNTRL_1)); + if (l_err) + { + TRACFCOMP(g_trac_mbox, ERR_MRK "MBOX::write> Unable to set Doorbell Status/Control Register"); + break; + } + } + else + { + TRACFCOMP(g_trac_mbox, ERR_MRK "MBOX::write> Message still pending : MBOX_DB_STAT_CNTRL_1=%X", l_64bitBuf[0]); + /*@ + * @errortype + * @moduleid MBOX::MOD_MBOXDD_WRITE + * @reasoncode MBOX::RC_MSG_PENDING + * @userdata1 Target ID String... + * @userdata2 Status/Control Register + * @devdesc MboxDD::write> Message still pending + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + MBOX::MOD_MBOXDD_WRITE, + MBOX::RC_MSG_PENDING, + target_to_uint64(i_target), + reinterpret_cast<uint64_t>(l_64bitBuf)); + l_err->collectTrace("MBOX",1024); + break; + } + } + else + { + TRACFCOMP(g_trac_mbox, ERR_MRK "MBOX::write> Permission-to-Send bit not set : MBOX_DB_STAT_CNTRL_1=%X", l_64bitBuf[0]); + /*@ + * @errortype + * @moduleid MBOX::MOD_MBOXDD_WRITE + * @reasoncode MBOX::RC_NO_PERM_TO_SEND + * @userdata1 Target ID String... + * @userdata2 Status/Control Register + * @devdesc MboxDD::write> Permission-to-Send not set + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + MBOX::MOD_MBOXDD_WRITE, + MBOX::RC_NO_PERM_TO_SEND, + target_to_uint64(i_target), + reinterpret_cast<uint64_t>(l_64bitBuf)); + l_err->collectTrace("MBOX",1024); + break; + } + + } while(0); + + return l_err; +} + +/** + * @brief Reads the mailbox PIB error status register + */ +errlHndl_t MboxDD::getErrStat(TARGETING::Target* i_target,uint64_t &o_status) +{ + errlHndl_t l_err = NULL; + uint32_t l_64bitBuf[2] = {0}; + size_t l_64bitSize = sizeof(uint64_t); + + do + { + l_err = deviceOp(DeviceFW::READ,i_target, + l_64bitBuf,l_64bitSize, + DEVICE_XSCOM_ADDRESS(MBOX_DB_ERR_STAT_PIB)); + if (l_err) + { + TRACFCOMP(g_trac_mbox, ERR_MRK "MBOX::getErrStat> Unable to read PIB Error Status"); + break; + } + else + { + //Check for Illegal Op + if ((l_64bitBuf[0] & MBOX::MBOX_ILLEGAL_OP) == + MBOX::MBOX_ILLEGAL_OP) + { + o_status |= MBOX::MBOX_ILLEGAL_OP; + } + //Check for Write Full + if ((l_64bitBuf[0] & MBOX::MBOX_DATA_WRITE_ERR) == + MBOX::MBOX_DATA_WRITE_ERR) + { + o_status |= MBOX::MBOX_DATA_WRITE_ERR; + } + //Check for Read Empty + if ((l_64bitBuf[0] & MBOX::MBOX_DATA_READ_ERR) == + MBOX::MBOX_DATA_READ_ERR) + { + o_status |= MBOX::MBOX_DATA_READ_ERR; + } + //Check for Parity Error & add address of parity error + if ((l_64bitBuf[0] & MBOX::MBOX_PARITY_ERR) == + MBOX::MBOX_PARITY_ERR) + { + o_status |= MBOX::MBOX_PARITY_ERR; + uint64_t l_temp = (l_64bitBuf[0] & 0x00FF0000); + o_status |= (l_temp << 40); + } + } + //Write '1' to Clear Status(16) + l_64bitBuf[0] = 0x00010000; + l_err = deviceOp(DeviceFW::WRITE,i_target, + l_64bitBuf,l_64bitSize, + DEVICE_XSCOM_ADDRESS(MBOX_DB_ERR_STAT_PIB)); + if (l_err) + { + TRACFCOMP(g_trac_mbox, ERR_MRK "MBOX::getErrStat> Unable to clear PIB Error Status"); + break; + } + + } while(0); + + return l_err; +} + +/** + * @brief Constructor + */ +MboxDD::MboxDD() +{ + TRACFCOMP(g_trac_mbox, "MboxDD::MboxDD()> "); +} + +/** + * @brief Destructor + */ +MboxDD::~MboxDD() +{ +} |