/*******************************************************************************
*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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <dev/pms/config.h>

#include <dev/pms/RefTisa/sallsdk/spc/saglobal.h>
#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;
}