/******************************************************************************* *Copyright (c) 2014 PMC-Sierra, Inc. All rights reserved. * *Redistribution and use in source and binary forms, with or without modification, are permitted provided *that the following conditions are met: *1. Redistributions of source code must retain the above copyright notice, this list of conditions and the *following disclaimer. *2. Redistributions in binary form must reproduce the above copyright notice, *this list of conditions and the following disclaimer in the documentation and/or other materials provided *with the distribution. * *THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED *WARRANTIES,INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE *FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT *NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT *LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE ********************************************************************************/ /*******************************************************************************/ /*! \file saphy.c * \brief The file implements the functions to Start, Stop a phy * * */ /******************************************************************************/ #include __FBSDID("$FreeBSD$"); #include #include #ifdef SA_ENABLE_TRACE_FUNCTIONS #ifdef siTraceFileID #undef siTraceFileID #endif #define siTraceFileID 'K' #endif extern bit32 gFPGA_TEST; /******************************************************************************/ /*! \brief Start a Phy * * Start a Phy * * \param agRoot handles for this instance of SAS/SATA hardware * \param agContext * \param phyId the phy id of the link will be started * \param agPhyConfig the phy configuration * \param agSASIdentify the SAS identify frame will be sent by the phy * * \return If phy is started successfully * - \e AGSA_RC_SUCCESS phy is started successfully * - \e AGSA_RC_BUSY phy is already started or starting * - \e AGSA_RC_FAILURE phy is not started successfully */ /*******************************************************************************/ GLOBAL bit32 saPhyStart( agsaRoot_t *agRoot, agsaContext_t *agContext, bit32 queueNum, bit32 phyId, agsaPhyConfig_t *agPhyConfig, agsaSASIdentify_t *agSASIdentify ) { agsaLLRoot_t *saRoot = (agsaLLRoot_t *)(agRoot->sdkData); agsaIORequestDesc_t *pRequest; bit32 ret = AGSA_RC_SUCCESS; bit32 using_reserved = agFALSE; smTraceFuncEnter(hpDBG_VERY_LOUD, "7a"); /* sanity check */ SA_ASSERT((agNULL != agRoot), ""); SA_ASSERT((agNULL != agSASIdentify), ""); SA_DBG3(("saPhyStart: phy%d started with ID %08X:%08X\n", phyId, SA_IDFRM_GET_SAS_ADDRESSHI(agSASIdentify), SA_IDFRM_GET_SAS_ADDRESSLO(agSASIdentify))); /* If phyId is invalid, return failure */ if ( phyId >= saRoot->phyCount ) { ret = AGSA_RC_FAILURE; } /* If phyId is valid */ else { /* Get request from free IORequests */ ossaSingleThreadedEnter(agRoot, LL_IOREQ_LOCKEQ_LOCK); pRequest = (agsaIORequestDesc_t *)saLlistIOGetHead(&(saRoot->freeIORequests)); /* */ /* If no LL Control request entry available */ if ( agNULL == pRequest ) { pRequest = (agsaIORequestDesc_t *)saLlistIOGetHead(&(saRoot->freeReservedRequests)); /* If no LL Control request entry available */ if(agNULL != pRequest) { using_reserved = agTRUE; SA_DBG1(("saPhyStart, using saRoot->freeReservedRequests\n")); } else { ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); SA_DBG1(("saPhyStart, No request from free list Not using saRoot->freeReservedRequests\n")); smTraceFuncExit(hpDBG_VERY_LOUD, 'a', "7a"); return AGSA_RC_BUSY; } } SA_ASSERT((!pRequest->valid), "The pRequest is in use"); pRequest->valid = agTRUE; /* If LL Control request entry avaliable */ if( using_reserved ) { saLlistIORemove(&(saRoot->freeReservedRequests), &(pRequest->linkNode)); } else { /* Remove the request from free list */ saLlistIORemove(&(saRoot->freeIORequests), &(pRequest->linkNode)); } ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); saRoot->IOMap[pRequest->HTag].Tag = pRequest->HTag; saRoot->IOMap[pRequest->HTag].IORequest = (void *)pRequest; saRoot->IOMap[pRequest->HTag].agContext = agContext; pRequest->valid = agTRUE; /* Build the Phy Start IOMB command and send to SPC */ smTrace(hpDBG_VERY_LOUD,"P2", phyId); /* TP:P2 phyId */ ret = mpiPhyStartCmd(agRoot, pRequest->HTag, phyId, agPhyConfig, agSASIdentify, queueNum); if (AGSA_RC_SUCCESS != ret) { /* remove the request from IOMap */ saRoot->IOMap[pRequest->HTag].Tag = MARK_OFF; saRoot->IOMap[pRequest->HTag].IORequest = agNULL; saRoot->IOMap[pRequest->HTag].agContext = agNULL; pRequest->valid = agFALSE; ossaSingleThreadedEnter(agRoot, LL_IOREQ_LOCKEQ_LOCK); /* return the request to free pool */ if(saLlistIOGetCount(&(saRoot->freeReservedRequests)) < SA_RESERVED_REQUEST_COUNT) { SA_DBG1(("saPhyStart: saving pRequest (%p) for later use\n", pRequest)); saLlistIOAdd(&(saRoot->freeReservedRequests), &(pRequest->linkNode)); } else { /* return the request to free pool */ saLlistIOAdd(&(saRoot->freeIORequests), &(pRequest->linkNode)); } ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); SA_DBG1(("saPhyStart, sending IOMB failed\n" )); } } smTraceFuncExit(hpDBG_VERY_LOUD, 'b', "7a"); return ret; } /******************************************************************************/ /*! \brief Stop a Phy * * Stop a Phy * * \param agRoot handles for this instance of SAS/SATA hardware * \param agContext the context of this API * \param phyId the phy id of the link will be stopped * * \return If phy is stopped successfully * - \e AGSA_RC_SUCCESS phy is stopped successfully * - \e AGSA_RC_FAILURE phy is not stopped successfully */ /*******************************************************************************/ GLOBAL bit32 saPhyStop( agsaRoot_t *agRoot, agsaContext_t *agContext, bit32 queueNum, bit32 phyId ) { agsaLLRoot_t *saRoot = (agsaLLRoot_t *)(agRoot->sdkData); agsaIORequestDesc_t *pRequest; bit32 ret = AGSA_RC_SUCCESS; bit32 using_reserved = agFALSE; smTraceFuncEnter(hpDBG_VERY_LOUD,"7b"); /* sanity check */ SA_ASSERT((agNULL != agRoot), ""); SA_DBG2(("saPhyStop: phy%d stop\n", phyId)); if(1) { mpiOCQueue_t *circularQ; int i; SA_DBG4(("saPhyStop:\n")); for ( i = 0; i < saRoot->QueueConfig.numOutboundQueues; i++ ) { circularQ = &saRoot->outboundQueue[i]; OSSA_READ_LE_32(circularQ->agRoot, &circularQ->producerIdx, circularQ->piPointer, 0); if(circularQ->producerIdx != circularQ->consumerIdx) { SA_DBG1(("saPhyStop: PI 0x%03x CI 0x%03x\n",circularQ->producerIdx, circularQ->consumerIdx )); } } } if(smIS_SPC(agRoot)) { phyId &= 0xF; } /* If phyId is invalid, return failure */ if ( (phyId & 0xF) >= saRoot->phyCount ) { ret = AGSA_RC_FAILURE; SA_DBG1(("saPhyStop: phy%d - failure with phyId\n", phyId)); } else { /* If phyId is valid */ /* Get request from free IORequests */ ossaSingleThreadedEnter(agRoot, LL_IOREQ_LOCKEQ_LOCK); pRequest = (agsaIORequestDesc_t *)saLlistIOGetHead(&(saRoot->freeIORequests)); /**/ /* If no LL Control request entry available */ if ( agNULL == pRequest ) { pRequest = (agsaIORequestDesc_t *)saLlistIOGetHead(&(saRoot->freeReservedRequests)); /* If no LL Control request entry available */ if(agNULL != pRequest) { using_reserved = agTRUE; SA_DBG1(("saPhyStop: using saRoot->freeReservedRequests\n")); } else { ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); SA_DBG1(("saPhyStop, No request from free list Not using saRoot->freeReservedRequests\n")); smTraceFuncExit(hpDBG_VERY_LOUD, 'a', "7b"); return AGSA_RC_BUSY; } } /* Remove the request from free list */ if( using_reserved ) { saLlistIORemove(&(saRoot->freeReservedRequests), &(pRequest->linkNode)); } else { saLlistIORemove(&(saRoot->freeIORequests), &(pRequest->linkNode)); } ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); SA_ASSERT((!pRequest->valid), "The pRequest is in use"); saRoot->IOMap[pRequest->HTag].Tag = pRequest->HTag; saRoot->IOMap[pRequest->HTag].IORequest = (void *)pRequest; saRoot->IOMap[pRequest->HTag].agContext = agContext; pRequest->valid = agTRUE; /* build IOMB command and send to SPC */ ret = mpiPhyStopCmd(agRoot, pRequest->HTag, phyId, queueNum); if (AGSA_RC_SUCCESS != ret) { /* remove the request from IOMap */ saRoot->IOMap[pRequest->HTag].Tag = MARK_OFF; saRoot->IOMap[pRequest->HTag].IORequest = agNULL; saRoot->IOMap[pRequest->HTag].agContext = agNULL; ossaSingleThreadedEnter(agRoot, LL_IOREQ_LOCKEQ_LOCK); /* return the request to free pool */ if(saLlistIOGetCount(&(saRoot->freeReservedRequests)) < SA_RESERVED_REQUEST_COUNT) { SA_DBG2(("saPhyStop: saving pRequest (%p) for later use\n", pRequest)); saLlistIOAdd(&(saRoot->freeReservedRequests), &(pRequest->linkNode)); } else { /* return the request to free pool */ saLlistIOAdd(&(saRoot->freeIORequests), &(pRequest->linkNode)); } ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); SA_DBG1(("saPhyStop, sending IOMB failed\n" )); } } smTraceFuncExit(hpDBG_VERY_LOUD, 'b', "7b"); return ret; } /******************************************************************************/ /*! \brief CallBack Routine to stop a Phy * * CallBack for Stop a Phy * * \param agRoot handles for this instance of SAS/SATA hardware * \param phyId the phy id of the link will be stopped * \param status the status of the phy * \param agContext the context of the saPhyStop * * \return If phy is stopped successfully * - \e AGSA_RC_SUCCESS phy is stopped successfully * - \e AGSA_RC_FAILURE phy is not stopped successfully */ /*******************************************************************************/ GLOBAL bit32 siPhyStopCB( agsaRoot_t *agRoot, bit32 phyId, bit32 status, agsaContext_t *agContext, bit32 portId, bit32 npipps ) { agsaLLRoot_t *saRoot = (agsaLLRoot_t *)(agRoot->sdkData); agsaPhy_t *pPhy; agsaPort_t *pPort; bit32 ret = AGSA_RC_SUCCESS; bit32 iomb_status = status; smTraceFuncEnter(hpDBG_VERY_LOUD,"7c"); /* sanity check */ SA_ASSERT((agNULL != agRoot), ""); /* If phyId is invalid, return failure */ if ( phyId >= saRoot->phyCount ) { ret = AGSA_RC_FAILURE; SA_DBG1(("siPhyStopCB: phy%d - failure with phyId\n", phyId)); /* makeup for CB */ status = (status << SHIFT8) | phyId; status |= ((npipps & PORT_STATE_MASK) << SHIFT16); ossaHwCB(agRoot, agNULL, OSSA_HW_EVENT_PHY_STOP_STATUS, status, agContext, agNULL); } /* If phyId is valid */ else { pPhy = &(saRoot->phys[phyId]); /* get the port of the phy */ pPort = pPhy->pPort; /* makeup for CB */ status = (status << SHIFT8) | phyId; status |= ((npipps & PORT_STATE_MASK) << SHIFT16); /* Callback to stop phy */ if ( agNULL != pPort ) { if ( iomb_status == OSSA_SUCCESS && (OSSA_PORT_INVALID == (npipps & PORT_STATE_MASK) )) { SA_DBG1(("siPhyStopCB: phy%d invalidating port\n", phyId)); /* invalid port state, remove the port */ pPort->status |= PORT_INVALIDATING; saRoot->PortMap[portId].PortStatus |= PORT_INVALIDATING; /* invalid the port */ siPortInvalid(agRoot, pPort); /* map out the portmap */ saRoot->PortMap[pPort->portId].PortContext = agNULL; saRoot->PortMap[pPort->portId].PortID = PORT_MARK_OFF; saRoot->PortMap[pPort->portId].PortStatus |= PORT_INVALIDATING; } ossaHwCB(agRoot, &(pPort->portContext), OSSA_HW_EVENT_PHY_STOP_STATUS, status, agContext, agNULL); } else { SA_DBG1(("siPhyStopCB: phy%d - Port is not established\n", phyId)); ossaHwCB(agRoot, agNULL, OSSA_HW_EVENT_PHY_STOP_STATUS, status, agContext, agNULL); } /* set PHY_STOPPED status */ PHY_STATUS_SET(pPhy, PHY_STOPPED); /* Exclude the phy from a port */ if ( agNULL != pPort ) { /* Acquire port list lock */ ossaSingleThreadedEnter(agRoot, LL_PORT_LOCK); /* Delete the phy from the port */ pPort->phyMap[phyId] = agFALSE; saRoot->phys[phyId].pPort = agNULL; /* Release port list lock */ ossaSingleThreadedLeave(agRoot, LL_PORT_LOCK); } } smTraceFuncExit(hpDBG_VERY_LOUD, 'a', "7c"); /* return */ return ret; } /******************************************************************************/ /*! \brief Initiate a Local PHY control command * * This function is called to initiate a PHY control command to the local PHY. * The completion of this function is reported in ossaLocalPhyControlCB() * * \param agRoot handles for this instance of SAS/SATA hardware * \param agContext the context of this API * \param phyId phy number * \param phyOperation * one of AGSA_PHY_LINK_RESET, AGSA_PHY_HARD_RESET, AGSA_PHY_ENABLE_SPINUP * * \return * - none */ /*******************************************************************************/ GLOBAL bit32 saLocalPhyControl( agsaRoot_t *agRoot, agsaContext_t *agContext, bit32 queueNum, bit32 phyId, bit32 phyOperation, ossaLocalPhyControlCB_t agCB ) { agsaLLRoot_t *saRoot = (agsaLLRoot_t *)(agRoot->sdkData); agsaIORequestDesc_t *pRequest; agsaPhyErrCounters_t errorParam; bit32 ret = AGSA_RC_SUCCESS; bit32 value, value1, value2, copyPhyId; bit32 count = 100; bit32 using_reserved = agFALSE; /* sanity check */ SA_ASSERT((agNULL != saRoot), ""); if(saRoot == agNULL) { SA_DBG1(("saLocalPhyControl: saRoot == agNULL\n")); return(AGSA_RC_FAILURE); } smTraceFuncEnter(hpDBG_VERY_LOUD,"7d"); si_memset(&errorParam,0,sizeof(agsaPhyErrCounters_t)); SA_DBG2(("saLocalPhyControl: phy%d operation %08X\n", phyId, phyOperation)); switch(phyOperation) { case AGSA_PHY_LINK_RESET: case AGSA_PHY_HARD_RESET: case AGSA_PHY_NOTIFY_ENABLE_SPINUP: case AGSA_PHY_BROADCAST_ASYNCH_EVENT: case AGSA_PHY_COMINIT_OOB: { /* Get request from free IORequests */ ossaSingleThreadedEnter(agRoot, LL_IOREQ_LOCKEQ_LOCK); pRequest = (agsaIORequestDesc_t *)saLlistIOGetHead(&(saRoot->freeIORequests)); /**/ /* If no LL Control request entry available */ if ( agNULL == pRequest ) { pRequest = (agsaIORequestDesc_t *)saLlistIOGetHead(&(saRoot->freeReservedRequests)); /* If no LL Control request entry available */ if(agNULL != pRequest) { using_reserved = agTRUE; SA_DBG1(("saLocalPhyControl, using saRoot->freeReservedRequests\n")); } else { ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); SA_DBG1(("saLocalPhyControl, No request from free list Not using saRoot->freeReservedRequests\n")); smTraceFuncExit(hpDBG_VERY_LOUD, 'a', "7d"); return AGSA_RC_BUSY; } } if( using_reserved ) { saLlistIORemove(&(saRoot->freeReservedRequests), &(pRequest->linkNode)); } else { saLlistIORemove(&(saRoot->freeIORequests), &(pRequest->linkNode)); } /* Remove the request from free list */ SA_ASSERT((!pRequest->valid), "The pRequest is in use"); pRequest->completionCB = (void*)agCB; // pRequest->abortCompletionCB = agCB; saRoot->IOMap[pRequest->HTag].Tag = pRequest->HTag; saRoot->IOMap[pRequest->HTag].IORequest = (void *)pRequest; saRoot->IOMap[pRequest->HTag].agContext = agContext; pRequest->valid = agTRUE; ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); /* Build the local phy control IOMB command and send to SPC */ ret = mpiLocalPhyControlCmd(agRoot, pRequest->HTag, phyId, phyOperation, queueNum); if (AGSA_RC_SUCCESS != ret) { /* remove the request from IOMap */ saRoot->IOMap[pRequest->HTag].Tag = MARK_OFF; saRoot->IOMap[pRequest->HTag].IORequest = agNULL; saRoot->IOMap[pRequest->HTag].agContext = agNULL; pRequest->valid = agFALSE; ossaSingleThreadedEnter(agRoot, LL_IOREQ_LOCKEQ_LOCK); /* return the request to free pool */ if(saLlistIOGetCount(&(saRoot->freeReservedRequests)) < SA_RESERVED_REQUEST_COUNT) { SA_DBG1(("saLocalPhyControl: saving pRequest (%p) for later use\n", pRequest)); saLlistIOAdd(&(saRoot->freeReservedRequests), &(pRequest->linkNode)); } else { /* return the request to free pool */ saLlistIOAdd(&(saRoot->freeIORequests), &(pRequest->linkNode)); } SA_DBG1(("saLocalPhyControl, sending IOMB failed\n" )); ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); return ret; } } break; case AGSA_PHY_GET_ERROR_COUNTS: { if(smIS_SPCV(agRoot)) { SA_ASSERT((smIS_SPC(agRoot)), "SPC only"); SA_DBG1(("saLocalPhyControl: V AGSA_PHY_GET_ERROR_COUNTS\n" )); smTraceFuncExit(hpDBG_VERY_LOUD, 'b', "7d"); return AGSA_RC_FAILURE; } /* If phyId is invalid, return failure */ if ( phyId >= saRoot->phyCount ) { ret = AGSA_RC_FAILURE; si_memset(&errorParam, 0, sizeof(agsaPhyErrCounters_t)); SA_DBG1(("saLocalPhyControl: phy%d - failure with phyId\n", phyId)); /* call back with the status */ if( agCB == agNULL ) { ossaLocalPhyControlCB(agRoot, agContext, phyId, phyOperation, OSSA_FAILURE, (void *)&errorParam); } else { agCB(agRoot, agContext, phyId, phyOperation, OSSA_FAILURE, (void *)&errorParam); } smTraceFuncExit(hpDBG_VERY_LOUD, 'c', "7d"); return ret; } /* save phyId */ copyPhyId = phyId; /* map 0x030000 or 0x040000 based on phyId to BAR4(0x20), BAT2(win) to access the register */ if (phyId < 4) { /* for phyId = 0, 1, 2, 3 */ value = 0x030000; } else { /* for phyId = 4, 5, 6, 7 */ phyId = phyId - 4; value = 0x040000; } /* Need to make sure DEVICE_LCLK_GENERATION register bit 6 is 0 */ value1 = ossaHwRegReadExt(agRoot, PCIBAR2, SPC_REG_DEVICE_LCLK); SA_DBG3(("saLocalPhyControl: TOP DEVICE LCLK Register value = %08X\n", value1)); /* If LCLK_CLEAR bit set then disable it */ if (value1 & DEVICE_LCLK_CLEAR) { ossaHwRegWriteExt(agRoot, PCIBAR2, SPC_REG_DEVICE_LCLK, (value1 & 0xFFFFFFBF) ); SA_DBG3(("saLocalPhyControl: TOP DEVICE LCLK value = %08X\n", (value1 & 0xFFFFFFBF))); } if (AGSA_RC_FAILURE == siBar4Shift(agRoot, value)) { SA_DBG1(("saLocalPhyControl:Shift Bar4 to 0x%x failed\n", value)); phyId = copyPhyId; /* call back with the status */ if( agCB == agNULL ) { ossaLocalPhyControlCB(agRoot, agContext, phyId, phyOperation, OSSA_FAILURE, (void *)&errorParam); } else { agCB(agRoot, agContext, phyId, phyOperation, OSSA_FAILURE, (void *)&errorParam); } smTraceFuncExit(hpDBG_VERY_LOUD, 'd', "7d"); return AGSA_RC_FAILURE; } /* set LCLK = 1 and LCLK_CLEAR = 0 */ SPC_WRITE_COUNTER_CNTL(phyId, LCLK); /* LCLK bit should be low to be able to read error registers */ while((value = SPC_READ_COUNTER_CNTL(phyId)) & LCLK) { if(--count == 0) { SA_DBG1(("saLocalPhyControl: Timeout,SPC_COUNTER_CNTL value = %08X\n", value)); ret = AGSA_RC_FAILURE; break; } } /* while */ value = SPC_READ_COUNTER_CNTL(phyId); SA_DBG3(("saLocalPhyControl: SPC_COUNTER_CNTL value = %08X\n", value)); /* invalidDword */ errorParam.invalidDword = SPC_READ_INV_DW_COUNT(phyId); /* runningDisparityError */ errorParam.runningDisparityError = SPC_READ_DISP_ERR_COUNT(phyId); /* lossOfDwordSynch */ errorParam.lossOfDwordSynch = SPC_READ_LOSS_DW_COUNT(phyId); /* phyResetProblem */ errorParam.phyResetProblem = SPC_READ_PHY_RESET_COUNT(phyId); /* codeViolation */ errorParam.codeViolation = SPC_READ_CODE_VIO_COUNT(phyId); /* never occurred in SPC8x6G */ errorParam.elasticityBufferOverflow = 0; errorParam.receivedErrorPrimitive = 0; errorParam.inboundCRCError = 0; SA_DBG3(("saLocalPhyControl:INV_DW_COUNT 0x%x\n", SPC_READ_INV_DW_COUNT(phyId))); SA_DBG3(("saLocalPhyControl:DISP_ERR_COUNT 0x%x\n", SPC_READ_DISP_ERR_COUNT(phyId))); SA_DBG3(("saLocalPhyControl:LOSS_DW_COUNT 0x%x\n", SPC_READ_LOSS_DW_COUNT(phyId))); SA_DBG3(("saLocalPhyControl:PHY_RESET_COUNT 0x%x\n", SPC_READ_PHY_RESET_COUNT(phyId))); SA_DBG3(("saLocalPhyControl:CODE_VIOLATION_COUNT 0x%x\n", SPC_READ_CODE_VIO_COUNT(phyId))); /* Shift back to BAR4 original address */ if (AGSA_RC_FAILURE == siBar4Shift(agRoot, 0x0)) { SA_DBG1(("saLocalPhyControl:Shift Bar4 to 0x%x failed\n", 0x0)); ret = AGSA_RC_FAILURE; } /* restore back the Top Device LCLK generation register value */ ossaHwRegWriteExt(agRoot, PCIBAR2, SPC_REG_DEVICE_LCLK, value1); /* restore phyId */ phyId = copyPhyId; /* call back with the status */ if (AGSA_RC_SUCCESS == ret) { if( agCB == agNULL ) { ossaLocalPhyControlCB(agRoot, agContext, copyPhyId, phyOperation, OSSA_SUCCESS, (void *)&errorParam); } else { agCB(agRoot, agContext, copyPhyId, phyOperation, OSSA_SUCCESS, (void *)&errorParam); } } else { if( agCB == agNULL ) { ossaLocalPhyControlCB(agRoot, agContext, phyId, phyOperation, OSSA_FAILURE, (void *)&errorParam); } else { agCB(agRoot, agContext, phyId, phyOperation, OSSA_FAILURE, (void *)&errorParam); } } break; } case AGSA_PHY_CLEAR_ERROR_COUNTS: { if(smIS_SPCV(agRoot)) { SA_ASSERT((smIS_SPC(agRoot)), "SPC only"); SA_DBG1(("saLocalPhyControl: V AGSA_PHY_CLEAR_ERROR_COUNTS\n" )); smTraceFuncExit(hpDBG_VERY_LOUD, 'e', "7d"); return AGSA_RC_FAILURE; } /* If phyId is invalid, return failure */ if ( phyId >= saRoot->phyCount ) { si_memset(&errorParam, 0, sizeof(agsaPhyErrCountersPage_t)); SA_DBG3(("saLocalPhyControl(CLEAR): phy%d - failure with phyId\n", phyId)); /* call back with the status */ if( agCB == agNULL ) { ossaLocalPhyControlCB(agRoot, agContext, phyId, phyOperation, OSSA_FAILURE, (void *)&errorParam); } else { agCB(agRoot, agContext, phyId, phyOperation, OSSA_FAILURE, (void *)&errorParam); } smTraceFuncExit(hpDBG_VERY_LOUD, 'f', "7d"); return AGSA_RC_FAILURE; } /* save phyId */ copyPhyId = phyId; /* map 0x030000 or 0x040000 based on phyId to BAR4(0x20), BAT2(win) to access the register */ if (phyId < 4) { /* for phyId = 0, 1, 2, 3 */ value = 0x030000; } else { /* for phyId = 4, 5, 6, 7 */ phyId = phyId - 4; value = 0x040000; } /* Need to make sure DEVICE_LCLK_GENERATION register bit 6 is 1 */ value2 = ossaHwRegReadExt(agRoot, PCIBAR2, SPC_REG_DEVICE_LCLK); SA_DBG3(("saLocalPhyControl: TOP DEVICE LCLK Register value = %08X\n", value2)); /* If LCLK_CLEAR bit not set then set it */ if ((value2 & DEVICE_LCLK_CLEAR) == 0) { ossaHwRegWriteExt(agRoot, PCIBAR2, SPC_REG_DEVICE_LCLK, (value2 | DEVICE_LCLK_CLEAR) ); SA_DBG3(("saLocalPhyControl: TOP DEVICE LCLK value = %08X\n", (value2 & 0xFFFFFFBF))); } if (AGSA_RC_FAILURE == siBar4Shift(agRoot, value)) { SA_DBG1(("saLocalPhyControl(CLEAR):Shift Bar4 to 0x%x failed\n", value)); phyId = copyPhyId; /* call back with the status */ if( agCB == agNULL ) { ossaLocalPhyControlCB(agRoot, agContext, phyId, phyOperation, OSSA_FAILURE, (void *)&errorParam); } else { agCB(agRoot, agContext, phyId, phyOperation, OSSA_FAILURE, (void *)&errorParam); } smTraceFuncExit(hpDBG_VERY_LOUD, 'g', "7d"); return AGSA_RC_FAILURE; } /* read Counter Control register */ value1 = SPC_READ_COUNTER_CNTL(phyId); SA_DBG3(("saLocalPhyControl(CLEAR): SPC_COUNTER_CNTL value = %08X\n", value1)); /* set LCLK and LCLK_CLEAR */ SPC_WRITE_COUNTER_CNTL(phyId, (LCLK_CLEAR | LCLK)); /* read back the value of register */ /* poll LCLK bit = 0 */ while((value = SPC_READ_COUNTER_CNTL(phyId)) & LCLK) { if(--count == 0) { SA_DBG1(("saLocalPhyControl: Timeout,SPC_COUNTER_CNTL value = %08X\n", value)); ret = AGSA_RC_FAILURE; break; } } /* while */ value = SPC_READ_COUNTER_CNTL(phyId); SA_DBG3(("saLocalPhyControl(CLEAR): SPC_COUNTER_CNTL value = %08X\n", value)); /* restore the value */ SPC_WRITE_COUNTER_CNTL(phyId, value1); /* Shift back to BAR4 original address */ if (AGSA_RC_FAILURE == siBar4Shift(agRoot, 0x0)) { SA_DBG1(("saLocalPhyControl:Shift Bar4 to 0x%x failed\n", 0x0)); ret = AGSA_RC_FAILURE; } /* restore back the Top Device LCLK generation register value */ ossaHwRegWriteExt(agRoot, PCIBAR2, SPC_REG_DEVICE_LCLK, value2); /* restore phyId */ phyId = copyPhyId; /* call back with the status */ if (AGSA_RC_SUCCESS == ret) { if( agCB == agNULL ) { ossaLocalPhyControlCB(agRoot, agContext, phyId, phyOperation, OSSA_SUCCESS, agNULL); } else { agCB(agRoot, agContext, phyId, phyOperation, OSSA_SUCCESS, agNULL); } } else { if( agCB == agNULL ) { ossaLocalPhyControlCB(agRoot, agContext, phyId, phyOperation, OSSA_FAILURE, (void *)&errorParam); } else { agCB(agRoot, agContext, phyId, phyOperation, OSSA_FAILURE, (void *)&errorParam); } } break; } case AGSA_PHY_GET_BW_COUNTS: { SA_ASSERT((smIS_SPC(agRoot)), "SPCv only"); SA_DBG1(("saLocalPhyControl: AGSA_PHY_GET_BW_COUNTS\n" )); break; } default: ret = AGSA_RC_FAILURE; SA_ASSERT(agFALSE, "(saLocalPhyControl) Unknown operation"); break; } smTraceFuncExit(hpDBG_VERY_LOUD, 'h', "7d"); return ret; } GLOBAL bit32 saGetPhyProfile( agsaRoot_t *agRoot, agsaContext_t *agContext, bit32 queueNum, bit32 ppc, bit32 phyId ) { bit32 ret = AGSA_RC_SUCCESS; agsaLLRoot_t *saRoot = agNULL; agsaPhyErrCountersPage_t errorParam; ossaLocalPhyControlCB_t agCB = ossaGetPhyProfileCB; /* sanity check */ SA_ASSERT((agNULL != agRoot), ""); saRoot = (agsaLLRoot_t *) (agRoot->sdkData); SA_ASSERT((agNULL != saRoot), ""); if(saRoot == agNULL) { SA_DBG3(("saGetPhyProfile : saRoot is NULL")); return AGSA_RC_FAILURE; } SA_DBG1(("saGetPhyProfile: ppc 0x%x phyID %d\n", ppc,phyId)); switch(ppc) { case AGSA_SAS_PHY_ERR_COUNTERS_PAGE: { if(smIS_SPCV(agRoot)) { SA_DBG1(("saGetPhyProfile: V AGSA_SAS_PHY_ERR_COUNTERS_PAGE\n" )); ret = mpiGetPhyProfileCmd( agRoot,agContext,ppc ,phyId,agCB); smTraceFuncExit(hpDBG_VERY_LOUD, 'i', "7d"); return ret; } } case AGSA_SAS_PHY_ERR_COUNTERS_CLR_PAGE: { /* If phyId is invalid, return failure */ if ( phyId >= saRoot->phyCount ) { si_memset(&errorParam, 0, sizeof(agsaPhyErrCountersPage_t)); SA_DBG3(("saGetPhyProfile(CLEAR): phy%d - failure with phyId\n", phyId)); /* call back with the status */ ossaGetPhyProfileCB(agRoot, agContext, phyId, ppc, OSSA_FAILURE, (void *)&errorParam); smTraceFuncExit(hpDBG_VERY_LOUD, 'j', "7d"); return AGSA_RC_FAILURE; } if(smIS_SPCV(agRoot)) { SA_DBG1(("saGetPhyProfile: V AGSA_SAS_PHY_ERR_COUNTERS_CLR_PAGE\n" )); ret = mpiGetPhyProfileCmd( agRoot,agContext, ppc,phyId,agCB); smTraceFuncExit(hpDBG_VERY_LOUD, 'k', "7d"); return ret; } } case AGSA_SAS_PHY_BW_COUNTERS_PAGE: { SA_DBG1(("saGetPhyProfile: AGSA_SAS_PHY_BW_COUNTERS_PAGE\n" )); ret = mpiGetPhyProfileCmd( agRoot,agContext,ppc ,phyId,agCB); break; } case AGSA_SAS_PHY_ANALOG_SETTINGS_PAGE: { SA_DBG1(("saGetPhyProfile: AGSA_SAS_PHY_ANALOG_SETTINGS_PAGE\n" )); ret = mpiGetPhyProfileCmd( agRoot,agContext,ppc ,phyId,agCB); break; } case AGSA_SAS_PHY_GENERAL_STATUS_PAGE: { SA_DBG1(("saGetPhyProfile: AGSA_SAS_PHY_GENERAL_STATUS_PAGE\n" )); ret = mpiGetPhyProfileCmd( agRoot,agContext,ppc ,phyId,agCB); break; } case AGSA_PHY_SNW3_PAGE: { SA_DBG1(("saGetPhyProfile: AGSA_PHY_SNW3_PAGE\n" )); ret = mpiGetPhyProfileCmd( agRoot,agContext,ppc ,phyId,agCB); break; } case AGSA_PHY_RATE_CONTROL_PAGE: { SA_DBG1(("saGetPhyProfile: AGSA_PHY_RATE_CONTROL_PAGE\n" )); ret = mpiGetPhyProfileCmd( agRoot,agContext,ppc ,phyId,agCB); break; } case AGSA_SAS_PHY_OPEN_REJECT_RETRY_BACKOFF_THRESHOLD_PAGE: { SA_DBG1(("saGetPhyProfile: AGSA_SAS_PHY_OPEN_REJECT_RETRY_BACKOFF_THRESHOLD_PAGE\n" )); ret = mpiGetPhyProfileCmd( agRoot,agContext,ppc ,phyId,agCB); break; } default: SA_DBG1(("saGetPhyProfile: Unknown operation 0x%X\n",ppc )); SA_ASSERT(agFALSE, "saGetPhyProfile Unknown operation " ); break; } return ret; } GLOBAL bit32 saSetPhyProfile ( agsaRoot_t *agRoot, agsaContext_t *agContext, bit32 queueNum, bit32 ppc, bit32 length, void *buffer, bit32 phyID ) { bit32 ret = AGSA_RC_SUCCESS; SA_DBG1(("saSetPhyProfile: ppc 0x%x length 0x%x phyID %d\n", ppc,length,phyID)); switch(ppc) { case AGSA_SAS_PHY_ANALOG_SETTINGS_PAGE: { SA_DBG1(("saSetPhyProfile: AGSA_SAS_PHY_ANALOG_SETTINGS_PAGE\n" )); ret = mpiSetPhyProfileCmd( agRoot,agContext,ppc ,phyID,length,buffer); break; } case AGSA_PHY_SNW3_PAGE: { SA_DBG1(("saSetPhyProfile: AGSA_PHY_SNW3_PAGE\n" )); ret = mpiSetPhyProfileCmd( agRoot,agContext,ppc ,phyID,length,buffer); break; } case AGSA_PHY_RATE_CONTROL_PAGE: { SA_DBG1(("saSetPhyProfile: AGSA_PHY_RATE_CONTROL_PAGE\n" )); ret = mpiSetPhyProfileCmd( agRoot,agContext,ppc ,phyID,length,buffer); break; } case AGSA_SAS_PHY_MISC_PAGE: { SA_DBG1(("saSetPhyProfile: AGSA_SAS_PHY_MISC_PAGE\n")); ret = mpiSetPhyProfileCmd( agRoot,agContext,ppc ,phyID,length,buffer); break; } default: SA_DBG1(("saSetPhyProfile: Unknown operation 0x%X\n",ppc )); SA_ASSERT(agFALSE, "saSetPhyProfile Unknown operation " ); ret = AGSA_RC_FAILURE; break; } return ret; } /******************************************************************************/ /*! \brief Initiate a HW Event Ack command * * This function is called to initiate a HW Event Ack command to the SPC. * The completion of this function is reported in ossaHwEventAckCB(). * * \param agRoot handles for this instance of SAS/SATA hardware * \param agContext the context of this API * \param queueNum queue number * \param eventSource point to the event source structure * \param param0 * \param param1 * * \return * - none */ /*******************************************************************************/ GLOBAL bit32 saHwEventAck( agsaRoot_t *agRoot, agsaContext_t *agContext, bit32 queueNum, agsaEventSource_t *eventSource, bit32 param0, bit32 param1 ) { agsaLLRoot_t *saRoot = (agsaLLRoot_t *)(agRoot->sdkData); agsaIORequestDesc_t *pRequest; agsaPortContext_t *agPortContext; agsaPort_t *pPort = agNULL; agsaSASHwEventAckCmd_t payload; bit32 phyportid; bit32 ret = AGSA_RC_SUCCESS; bit32 using_reserved = agFALSE; smTraceFuncEnter(hpDBG_VERY_LOUD,"7e"); /* sanity check */ SA_ASSERT((agNULL != saRoot), ""); if(saRoot == agNULL) { SA_DBG1(("saHwEventAck: saRoot == agNULL\n")); return(AGSA_RC_FAILURE); } SA_DBG2(("saHwEventAck: agContext %p eventSource %p\n", agContext, eventSource)); SA_DBG1(("saHwEventAck: event 0x%x param0 0x%x param1 0x%x\n", eventSource->event, param0, param1)); agPortContext = eventSource->agPortContext; /* Get request from free IORequests */ ossaSingleThreadedEnter(agRoot, LL_IOREQ_LOCKEQ_LOCK); pRequest = (agsaIORequestDesc_t *)saLlistIOGetHead(&(saRoot->freeIORequests)); /**/ /* If no LL Control request entry available */ if ( agNULL == pRequest ) { pRequest = (agsaIORequestDesc_t *)saLlistIOGetHead(&(saRoot->freeReservedRequests)); /**/ if(agNULL != pRequest) { using_reserved = agTRUE; SA_DBG1(("saHwEventAck, using saRoot->freeReservedRequests\n")); } else { ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); /* If no LL Control request entry available */ SA_DBG1(("saHwEventAck, No request from free list Not using saRoot->freeReservedRequests\n")); smTraceFuncExit(hpDBG_VERY_LOUD, 'a', "7e"); return AGSA_RC_BUSY; } } if( using_reserved ) { saLlistIORemove(&(saRoot->freeReservedRequests), &(pRequest->linkNode)); } else { /* Remove the request from free list */ saLlistIORemove(&(saRoot->freeIORequests), &(pRequest->linkNode)); } ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); SA_ASSERT((!pRequest->valid), "The pRequest is in use"); SA_DBG2(("saHwEventAck: queueNum 0x%x HTag 0x%x\n",queueNum ,pRequest->HTag)); saRoot->IOMap[pRequest->HTag].Tag = pRequest->HTag; saRoot->IOMap[pRequest->HTag].IORequest = (void *)pRequest; saRoot->IOMap[pRequest->HTag].agContext = agContext; pRequest->valid = agTRUE; /* build IOMB command and send to SPC */ /* set payload to zeros */ si_memset(&payload, 0, sizeof(agsaSASHwEventAckCmd_t)); /* find port id */ if (agPortContext) { pPort = (agsaPort_t *) (agPortContext->sdkData); if (pPort) { if(eventSource->event == OSSA_HW_EVENT_PHY_DOWN) { pPort->tobedeleted = agTRUE; } SA_DBG3(("saHwEventAck,pPort->portId %X\n",pPort->portId)); if(smIS_SPC(agRoot)) { /* fillup PORT_ID field */ phyportid = pPort->portId & 0xF; } else { /* fillup PORT_ID field */ phyportid = pPort->portId & 0xFF; } } else { /* pPort is NULL - set PORT_ID to not intialized */ if(smIS_SPC(agRoot)) { phyportid = 0xF; } else { phyportid = 0xFF; } } } else { /* agPortContext is NULL - set PORT_ID to not intialized */ if(smIS_SPC(agRoot)) { phyportid = 0xF; } else { phyportid = 0xFF; } } pRequest->pPort = pPort; SA_DBG3(("saHwEventAck,eventSource->param 0x%X\n",eventSource->param)); SA_DBG3(("saHwEventAck,eventSource->event 0x%X\n",eventSource->event)); if(smIS_SPC(agRoot)) { /* fillup up PHY_ID */ phyportid |= ((eventSource->param & 0x0000000F) << 4); /* fillup SEA field */ phyportid |= (eventSource->event & 0x0000FFFF) << 8; SA_DBG3(("saHwEventAck: portId 0x%x phyId 0x%x SEA 0x%x\n", phyportid & 0xF, eventSource->param & 0x0000000F, eventSource->event & 0x0000FFFF)); } else { /* fillup up PHY_ID */ phyportid |= ((eventSource->param & 0x000000FF) << SHIFT24); /* fillup SEA field */ phyportid |= (eventSource->event & 0x00FFFFFF) << SHIFT8; SA_DBG3(("saHwEventAck: portId 0x%x phyId 0x%x SEA 0x%x\n", phyportid & 0xFF, eventSource->param & 0x0000000F, eventSource->event & 0x0000FFFF)); } pRequest->HwAckType = (bit16)phyportid; SA_DBG1(("saHwEventAck,phyportid 0x%X HwAckType 0x%X\n",phyportid,pRequest->HwAckType)); /* set tag */ OSSA_WRITE_LE_32(agRoot, &payload, OSSA_OFFSET_OF(agsaSASHwEventAckCmd_t, tag), pRequest->HTag); OSSA_WRITE_LE_32(agRoot, &payload, OSSA_OFFSET_OF(agsaSASHwEventAckCmd_t, sEaPhyIdPortId), phyportid); OSSA_WRITE_LE_32(agRoot, &payload, OSSA_OFFSET_OF(agsaSASHwEventAckCmd_t, Param0), param0); OSSA_WRITE_LE_32(agRoot, &payload, OSSA_OFFSET_OF(agsaSASHwEventAckCmd_t, Param1), param1); /* build IOMB command and send to SPC */ if(smIS_SPC(agRoot)) { ret = mpiBuildCmd(agRoot, (bit32 *)&payload, MPI_CATEGORY_SAS_SATA, OPC_INB_SPC_SAS_HW_EVENT_ACK, IOMB_SIZE64, queueNum); } else { ret = mpiBuildCmd(agRoot, (bit32 *)&payload, MPI_CATEGORY_SAS_SATA, OPC_INB_SAS_HW_EVENT_ACK, IOMB_SIZE64, queueNum); } if (AGSA_RC_SUCCESS != ret) { /* remove the request from IOMap */ saRoot->IOMap[pRequest->HTag].Tag = MARK_OFF; saRoot->IOMap[pRequest->HTag].IORequest = agNULL; saRoot->IOMap[pRequest->HTag].agContext = agNULL; pRequest->valid = agFALSE; ossaSingleThreadedEnter(agRoot, LL_IOREQ_LOCKEQ_LOCK); /* return the request to free pool */ if(saLlistIOGetCount(&(saRoot->freeReservedRequests)) < SA_RESERVED_REQUEST_COUNT) { SA_DBG1(("saHwEventAck: saving pRequest (%p) for later use\n", pRequest)); saLlistIOAdd(&(saRoot->freeReservedRequests), &(pRequest->linkNode)); } else { /* return the request to free pool */ saLlistIOAdd(&(saRoot->freeIORequests), &(pRequest->linkNode)); } ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); SA_DBG1(("saHwEventAck, sending IOMB failed\n" )); } smTraceFuncExit(hpDBG_VERY_LOUD, 'b', "7e"); return ret; } GLOBAL bit32 saVhistCapture( agsaRoot_t *agRoot, agsaContext_t *agContext, bit32 queueNum, bit32 Channel, bit32 NumBitLo, bit32 NumBitHi, bit32 PcieAddrLo, bit32 PcieAddrHi, bit32 ByteCount ) { agsaLLRoot_t *saRoot = (agsaLLRoot_t *)(agRoot->sdkData); agsaIORequestDesc_t *pRequest; bit32 ret = AGSA_RC_SUCCESS; bit32 using_reserved = agFALSE; smTraceFuncEnter(hpDBG_VERY_LOUD,"3N"); /* sanity check */ SA_ASSERT((agNULL != agRoot), ""); SA_DBG1(("saVhistCapture:Channel 0x%08X 0x%08X%08X 0x%08X%08X count 0x%X\n",Channel, NumBitHi, NumBitLo ,PcieAddrHi,PcieAddrLo,ByteCount)); { /* Get request from free IORequests */ ossaSingleThreadedEnter(agRoot, LL_IOREQ_LOCKEQ_LOCK); pRequest = (agsaIORequestDesc_t *)saLlistIOGetHead(&(saRoot->freeIORequests)); /* */ /* If no LL Control request entry available */ if ( agNULL == pRequest ) { pRequest = (agsaIORequestDesc_t *)saLlistIOGetHead(&(saRoot->freeReservedRequests)); /* If no LL Control request entry available */ if(agNULL != pRequest) { using_reserved = agTRUE; SA_DBG1((", using saRoot->freeReservedRequests\n")); } else { ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); SA_DBG1(("saVhistCapture: No request from free list Not using saRoot->freeReservedRequests\n")); smTraceFuncExit(hpDBG_VERY_LOUD, 'a', "3N"); return AGSA_RC_BUSY; } } SA_ASSERT((!pRequest->valid), "The pRequest is in use"); pRequest->valid = agTRUE; /* If LL Control request entry avaliable */ if( using_reserved ) { saLlistIORemove(&(saRoot->freeReservedRequests), &(pRequest->linkNode)); } else { /* Remove the request from free list */ saLlistIORemove(&(saRoot->freeIORequests), &(pRequest->linkNode)); } ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); saRoot->IOMap[pRequest->HTag].Tag = pRequest->HTag; saRoot->IOMap[pRequest->HTag].IORequest = (void *)pRequest; saRoot->IOMap[pRequest->HTag].agContext = agContext; pRequest->valid = agTRUE; /* Build the VhisCapture IOMB command and send to SPCv */ ret = mpiVHistCapCmd(agRoot,agContext, queueNum, Channel, NumBitLo, NumBitHi ,PcieAddrLo, PcieAddrHi, ByteCount); if (AGSA_RC_SUCCESS != ret) { /* remove the request from IOMap */ saRoot->IOMap[pRequest->HTag].Tag = MARK_OFF; saRoot->IOMap[pRequest->HTag].IORequest = agNULL; saRoot->IOMap[pRequest->HTag].agContext = agNULL; pRequest->valid = agFALSE; ossaSingleThreadedEnter(agRoot, LL_IOREQ_LOCKEQ_LOCK); /* return the request to free pool */ if(saLlistIOGetCount(&(saRoot->freeReservedRequests)) < SA_RESERVED_REQUEST_COUNT) { SA_DBG1(("saPhyStart: saving pRequest (%p) for later use\n", pRequest)); saLlistIOAdd(&(saRoot->freeReservedRequests), &(pRequest->linkNode)); } else { /* return the request to free pool */ saLlistIOAdd(&(saRoot->freeIORequests), &(pRequest->linkNode)); } ossaSingleThreadedLeave(agRoot, LL_IOREQ_LOCKEQ_LOCK); SA_DBG1(("saVhistCapture: sending IOMB failed\n" )); } } smTraceFuncExit(hpDBG_VERY_LOUD, 'b', "3N"); return ret; }