/*  ============================================================================
 *   Copyright (c) Texas Instruments Incorporated 2010
 * 
 *  Redistribution and use in source and binary forms, with or without 
 *  modification, are permitted provided that the following conditions 
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer.
 *
 *    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.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT 
 *  OWNER 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  pcie_sample.c
 *
 *   @brief   
 *      This is the PCIe example code.   
 *
 */

/** 
 * In the PCIe sample example two Shannon EVMs are used to test the PCIe driver. 
 * As described in the following figure, Shannon 1 is configured as a Root Complex
 * and Shannon 2 is configured as End Point.
 *
 *       Shannon 1                                       Shannon 2
 *   ------------------                             -------------------
 *   |                |                             |                 |
 *   |   Root         |          PCIe Link          |  End Point      |
 *   |   Complex      | <-------------------------->|                 |
 *   |                |                             |                 |
 *   ------------------                             -------------------
 *  
 * Once the PCIe link is established, the following sequence of actions will happen:
 *  - Shannon 1 sends data to Shannon 2
 *  - Shannon 2 waits to receive all the data
 *  - Shannon 2 sends the data back to Shannon 1
 *  - Shannon 1 waits to receive all the data
 *  - Shannon 1 verifies if the received data matches the sent data and declares test pass or fail.
 *
 */
 

#include "pcie_sample.h"

/* Use regular "printf" when not using BIOS */
//#include <xdc/runtime/System.h>
#define System_printf printf
#include <stdio.h>

#include <ti/csl/csl_bootcfgAux.h>
#include <ti/csl/csl_cacheAux.h>
#include <ti/csl/csl_chip.h>
#include <ti/csl/csl_xmcAux.h>


#pragma DATA_SECTION(dstBuf, ".dstBufSec")
/* Cache coherence: Align must be a multiple of cache line size (L2=128 bytes, L1=64 bytes) to operate with cache enabled. */
/* Aligning to 256 bytes because the PCIe inbound offset register masks the last 8bits of the buffer address  */
#pragma DATA_ALIGN(dstBuf, 256)
/* last element in the buffer is a marker that indicates the buffer status: full/empty */
#define PCIE_EXAMPLE_MAX_CACHE_LINE_SIZE 128
#define PCIE_EXAMPLE_UINT32_SIZE           4 /* preprocessor #if requires a real constant, not a sizeof() */

#define PCIE_EXAMPLE_DSTBUF_BYTES ((PCIE_BUFSIZE_APP + 1) * PCIE_EXAMPLE_UINT32_SIZE)
#define PCIE_EXAMPLE_DSTBUF_REM (PCIE_EXAMPLE_DSTBUF_BYTES % PCIE_EXAMPLE_MAX_CACHE_LINE_SIZE)
#define PCIE_EXAMPLE_DSTBUF_PAD (PCIE_EXAMPLE_DSTBUF_REM ? (PCIE_EXAMPLE_MAX_CACHE_LINE_SIZE - PCIE_EXAMPLE_DSTBUF_REM) : 0)
struct dstBuf_s {
  volatile uint32_t buf[PCIE_BUFSIZE_APP + 1];
  /* Cache coherence: Must pad to cache line size in order to enable cacheability */
#if PCIE_EXAMPLE_DSTBUF_PAD
  uint8_t padding[PCIE_EXAMPLE_DSTBUF_PAD];
#endif
} dstBuf;

#define PCIE_EXAMPLE_BUF_EMPTY 0
#define PCIE_EXAMPLE_BUF_FULL  1

/* Does not need to be aligned (even for cache) since it is only accessed locally */
uint32_t srcBuf[PCIE_BUFSIZE_APP];

extern volatile unsigned int cregister TSCL;

/* Global config variable that controls
   the PCIe mode. It is global so it can be poked
   from CCS. It should be set either to EP or RC. */
pcieMode_e PcieModeGbl = pcie_EP_MODE;      


/*****************************************************************************
 * Function: Power domain configuration
 ****************************************************************************/
pcieRet_e pciePowerCfg(void)
{
  /* Turn on the PCIe power domain */
  if (CSL_PSC_getPowerDomainState (CSL_PSC_PD_PCIEX) != PSC_PDSTATE_ON) {
    /* Enable the domain */
    CSL_PSC_enablePowerDomain (CSL_PSC_PD_PCIEX);
    /* Enable MDCTL */
    CSL_PSC_setModuleNextState (CSL_PSC_LPSC_PCIEX, PSC_MODSTATE_ENABLE);
    /* Apply the domain */
    CSL_PSC_startStateTransition (CSL_PSC_PD_PCIEX);
    /* Wait for it to finish */
    while (! CSL_PSC_isStateTransitionDone (CSL_PSC_PD_PCIEX));
  } else {
    System_printf ("Power domain is already enabled.  You probably re-ran without device reset (which is OK)\n");
  }

  return pcie_RET_OK;
}

/*****************************************************************************
 * Function: Utility function to introduce delay 
 ****************************************************************************/
void cycleDelay (uint32_t count)
{
  uint32_t sat;

  if (count <= 0)
    return;

  sat = TSCL + count;
  while (TSCL < sat);
}

/*****************************************************************************
 * Function: Serdes configuration 
 ****************************************************************************/
pcieRet_e pcieSerdesCfg(void)
{
  uint16_t cfg;
  
  CSL_BootCfgUnlockKicker();

  /* Provide PLL reference clock to SERDES inside PCIESS
     Program PLL settings and enable PLL from PCIe SERDES.*/
  cfg = 0x01C9; /* value based on PCIe userguide */
  CSL_BootCfgSetPCIEConfigPLL(cfg);
  
  CSL_BootCfgLockKicker();

  /*Wait for PLL to lock (3000 CLKIN1 cycles) */
  cycleDelay(10000);
  
  return pcie_RET_OK;
}

/*****************************************************************************
 * Function: Enable/Disable LTSSM (Link Training)
 ****************************************************************************/
pcieRet_e pcieLtssmCtrl(Pcie_Handle handle, uint8_t enable)
{
  pcieCmdStatusReg_t    cmdStatus;
  pcieRegisters_t       setRegs;
  pcieRegisters_t       getRegs;
  pcieRet_e retVal;

  memset (&cmdStatus,    0, sizeof(cmdStatus));
  memset (&setRegs,      0, sizeof(setRegs));
  memset (&getRegs,      0, sizeof(getRegs));

  getRegs.cmdStatus = &cmdStatus;
  if ((retVal = Pcie_readRegs (handle, pcie_LOCATION_LOCAL, &getRegs)) != pcie_RET_OK) 
  {
    System_printf ("Read CMD STATUS register failed!\n");
    return retVal;
  }
  
  if(enable)
    cmdStatus.ltssmEn = 1;
  else  
    cmdStatus.ltssmEn = 0;

  setRegs.cmdStatus = &cmdStatus;   
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET CMD STATUS register failed!\n");
    return retVal;
  }

  return pcie_RET_OK;
}


/*****************************************************************************
 * Function: Configure PCIe in Root Complex Mode
 ****************************************************************************/
pcieRet_e pcieCfgRC(Pcie_Handle handle)
{
  pcieRet_e retVal;

  pcieCmdStatusReg_t     cmdStatus;
  pcieObSizeReg_t        obSize;
  pcieGen2Reg_t          gen2;
  pcieType1Bar32bitIdx_t type1Bar32bitIdx;
  pcieStatusCmdReg_t     statusCmd;
  pcieDevStatCtrlReg_t   devStatCtrl;
  pcieAccrReg_t          accr;
                 
  pcieRegisters_t        setRegs;
  pcieRegisters_t        getRegs;

  memset (&cmdStatus,        0, sizeof(cmdStatus));
  memset (&obSize,           0, sizeof(obSize));
  memset (&gen2,             0, sizeof(gen2));
  memset (&type1Bar32bitIdx, 0, sizeof(type1Bar32bitIdx));
  memset (&statusCmd,        0, sizeof(statusCmd));
  memset (&devStatCtrl,      0, sizeof(devStatCtrl));
  memset (&accr,             0, sizeof(accr));

  /*Disable link training*/
  if ((retVal = pcieLtssmCtrl(handle, FALSE)) != pcie_RET_OK) 
  {
    System_printf ("Failed to disable Link Training!\n");
    return retVal;
  }
  
  /* Configure the size of the translation regions */   
  memset (&setRegs, 0, sizeof(setRegs));
  memset (&getRegs, 0, sizeof(getRegs));
  
  obSize.size = pcie_OB_SIZE_8MB;
  setRegs.obSize = &obSize;
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET OB_SIZE register failed!\n");
    return retVal;
  }
     
  /* Setting PL_GEN2 */   
  memset (&setRegs, 0, sizeof(setRegs));
  gen2.numFts = 0xF;
  gen2.dirSpd = 0x0;
  gen2.lnEn   = 1;
  setRegs.gen2 = &gen2;
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET GEN2 register failed!\n");
    return retVal;
  }
  
  /* Configure BAR Masks */   
  /* First need to enable writing on BAR mask registers */
  memset (&setRegs, 0, sizeof(setRegs));
  memset (&getRegs, 0, sizeof(getRegs));
  memset (&cmdStatus,  0, sizeof(cmdStatus));

  getRegs.cmdStatus = &cmdStatus;
  if ((retVal = Pcie_readRegs (handle, pcie_LOCATION_LOCAL, &getRegs)) != pcie_RET_OK) 
  {
    System_printf ("Read CMD STATUS register failed!\n");
    return retVal;
  }
  cmdStatus.dbi = 1;
  setRegs.cmdStatus = &cmdStatus;   
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET CMD STATUS register failed!\n");
    return retVal;
  }
  
  /* Configure Masks*/
  memset (&setRegs, 0, sizeof(setRegs));
  type1Bar32bitIdx.reg.reg32 = PCIE_BAR_MASK;
  setRegs.type1Bar32bitIdx = &type1Bar32bitIdx;

  /* BAR 0 */
  type1Bar32bitIdx.idx = 0; /* configure BAR 0*/
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET BAR MASK register failed!\n");
    return retVal;
  }
  
  /* BAR 1 */
  type1Bar32bitIdx.idx = 1; /* configure BAR 1*/
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET BAR MASK register failed!\n");
    return retVal;
  }
  
  /* Disable writing on BAR Masks */
  memset (&setRegs, 0, sizeof(setRegs));
  memset (&getRegs, 0, sizeof(getRegs));
  memset (&cmdStatus,  0, sizeof(cmdStatus));

  getRegs.cmdStatus = &cmdStatus;
  if ((retVal = Pcie_readRegs (handle, pcie_LOCATION_LOCAL, &getRegs)) != pcie_RET_OK) 
  {
    System_printf ("Read CMD STATUS register failed!\n");
    return retVal;
  }
  cmdStatus.dbi = 0;
  setRegs.cmdStatus = &cmdStatus;   
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET CMD STATUS register failed!\n");
    return retVal;
  }

  /* Enable memory access and mastership of the bus */
  memset (&setRegs, 0, sizeof(setRegs));
  memset (&getRegs, 0, sizeof(getRegs));

  getRegs.statusCmd = &statusCmd;
  if ((retVal = Pcie_readRegs (handle, pcie_LOCATION_LOCAL, &getRegs)) != pcie_RET_OK) 
  {
    System_printf ("Read Status Comand register failed!\n");
    return retVal;
  }
  statusCmd.memSp  = 1;
  statusCmd.busMs  = 1;
  statusCmd.resp   = 1;
  statusCmd.serrEn = 1;
  setRegs.statusCmd = &statusCmd;   
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET Status Command register failed!\n");
    return retVal;
  }

  /* Enable Error Reporting */
  memset (&setRegs, 0, sizeof(setRegs));
  memset (&getRegs, 0, sizeof(getRegs));

  getRegs.devStatCtrl = &devStatCtrl;
  if ((retVal = Pcie_readRegs (handle, pcie_LOCATION_LOCAL, &getRegs)) != pcie_RET_OK) 
  {
    System_printf ("Regad Device Status Control register failed!\n");
    return retVal;
  }
 
  devStatCtrl.reqRp = 1;
  devStatCtrl.fatalErRp = 1;
  devStatCtrl.nFatalErRp = 1;
  devStatCtrl.corErRp = 1;
  setRegs.devStatCtrl = &devStatCtrl;   
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET Device Status Control register failed!\n");
    return retVal;
  }

  /* Enable ECRC */
  memset (&setRegs, 0, sizeof(setRegs));
  
  accr.chkEn=1;
  accr.chkCap=1;
  accr.genEn=1;
  accr.genCap=1;
  setRegs.accr = &accr;

  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET ACCR register failed!\n");
    return retVal;
  }

  return pcie_RET_OK;
}

/*****************************************************************************
 * Function: Configure PCIe in End Point Mode
 ****************************************************************************/
pcieRet_e pcieCfgEP(Pcie_Handle handle)
{
  pcieRet_e retVal;

  pcieCmdStatusReg_t     cmdStatus;
  pcieObSizeReg_t        obSize;
  pcieGen2Reg_t          gen2;
  pcieType0Bar32bitIdx_t type0Bar32bitIdx;
  pcieStatusCmdReg_t     statusCmd;
  pcieDevStatCtrlReg_t   devStatCtrl;
  pcieAccrReg_t          accr;
                 
  pcieRegisters_t        setRegs;
  pcieRegisters_t        getRegs;

  memset (&cmdStatus,        0, sizeof(cmdStatus));
  memset (&obSize,           0, sizeof(obSize));
  memset (&gen2,             0, sizeof(gen2));
  memset (&type0Bar32bitIdx, 0, sizeof(type0Bar32bitIdx));
  memset (&statusCmd,        0, sizeof(statusCmd));
  memset (&devStatCtrl,      0, sizeof(devStatCtrl));
  memset (&accr,             0, sizeof(accr));

  /*Disable link training*/
  if ((retVal = pcieLtssmCtrl(handle, FALSE)) != pcie_RET_OK) 
  {
    System_printf ("Failed to disable Link Training!\n");
    return retVal;
  }
  
  /* Configure the size of the translation regions */   
  memset (&setRegs, 0, sizeof(setRegs));
  memset (&getRegs, 0, sizeof(getRegs));
  
  obSize.size = pcie_OB_SIZE_8MB;
  setRegs.obSize = &obSize;
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET OB_SIZE register failed!\n");
    return retVal;
  }
     
  /* Setting PL_GEN2 */   
  memset (&setRegs, 0, sizeof(setRegs));
  gen2.numFts = 0xF;
  gen2.dirSpd = 0x0;
  gen2.lnEn   = 1;
  setRegs.gen2 = &gen2;
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET GEN2 register failed!\n");
    return retVal;
  }
  
  /* Configure BAR Masks */   
  /* First need to enable writing on BAR mask registers */
  memset (&setRegs, 0, sizeof(setRegs));
  memset (&getRegs, 0, sizeof(getRegs));
  memset (&cmdStatus,  0, sizeof(cmdStatus));

  getRegs.cmdStatus = &cmdStatus;
  if ((retVal = Pcie_readRegs (handle, pcie_LOCATION_LOCAL, &getRegs)) != pcie_RET_OK) 
  {
    System_printf ("Read CMD STATUS register failed!\n");
    return retVal;
  }
  cmdStatus.dbi = 1;
  setRegs.cmdStatus = &cmdStatus;   
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET CMD STATUS register failed!\n");
    return retVal;
  }
  
  /* Configure Masks*/
  memset (&setRegs, 0, sizeof(setRegs));
  type0Bar32bitIdx.reg.reg32 = PCIE_BAR_MASK;
  setRegs.type0Bar32bitIdx = &type0Bar32bitIdx;

  /* BAR 0 */
  type0Bar32bitIdx.idx = 0; /* configure BAR 0*/
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET BAR MASK register failed!\n");
    return retVal;
  }
  
  /* BAR 1 */
  type0Bar32bitIdx.idx = 1; /* configure BAR 1*/
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET BAR MASK register failed!\n");
    return retVal;
  }
  
  /* Disable writing on BAR Masks */
  memset (&setRegs, 0, sizeof(setRegs));
  memset (&getRegs, 0, sizeof(getRegs));
  memset (&cmdStatus,  0, sizeof(cmdStatus));

  getRegs.cmdStatus = &cmdStatus;
  if ((retVal = Pcie_readRegs (handle, pcie_LOCATION_LOCAL, &getRegs)) != pcie_RET_OK) 
  {
    System_printf ("Read CMD STATUS register failed!\n");
    return retVal;
  }
  cmdStatus.dbi = 0;
  setRegs.cmdStatus = &cmdStatus;   
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET CMD STATUS register failed!\n");
    return retVal;
  }

  /* Enable memory access and mastership of the bus */
  memset (&setRegs, 0, sizeof(setRegs));
  memset (&getRegs, 0, sizeof(getRegs));

  getRegs.statusCmd = &statusCmd;
  if ((retVal = Pcie_readRegs (handle, pcie_LOCATION_LOCAL, &getRegs)) != pcie_RET_OK) 
  {
    System_printf ("Read Status Comand register failed!\n");
    return retVal;
  }
  statusCmd.memSp  = 1;
  statusCmd.busMs  = 1;
  statusCmd.resp   = 1;
  statusCmd.serrEn = 1;
  setRegs.statusCmd = &statusCmd;   
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET Status Command register failed!\n");
    return retVal;
  }

  /* Enable Error Reporting */
  memset (&setRegs, 0, sizeof(setRegs));
  memset (&getRegs, 0, sizeof(getRegs));

  getRegs.devStatCtrl = &devStatCtrl;
  if ((retVal = Pcie_readRegs (handle, pcie_LOCATION_LOCAL, &getRegs)) != pcie_RET_OK) 
  {
    System_printf ("Regad Device Status Control register failed!\n");
    return retVal;
  }
 
  devStatCtrl.reqRp = 1;
  devStatCtrl.fatalErRp = 1;
  devStatCtrl.nFatalErRp = 1;
  devStatCtrl.corErRp = 1;
  setRegs.devStatCtrl = &devStatCtrl;   
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET Device Status Control register failed!\n");
    return retVal;
  }

  /* Enable ECRC */
  memset (&setRegs, 0, sizeof(setRegs));
  
  accr.chkEn=1;
  accr.chkCap=1;
  accr.genEn=1;
  accr.genCap=1;
  setRegs.accr = &accr;

  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET ACCR register failed!\n");
    return retVal;
  }

  return pcie_RET_OK;
}

/*****************************************************************************
 * Function: Configure and enable Outbound Address Translation 
 ****************************************************************************/
pcieRet_e pcieObTransCfg(Pcie_Handle handle, uint32_t obAddrLo, uint32_t obAddrHi, uint8_t region)
{

  pcieRet_e retVal;

  pcieRegisters_t      setRegs;
  pcieRegisters_t      getRegs;

  pcieCmdStatusReg_t   cmdStatus;

  memset (&setRegs,   0, sizeof(setRegs));
  memset (&getRegs,   0, sizeof(getRegs));
  memset (&cmdStatus, 0, sizeof(cmdStatus));

  /* Set outbound offset registers */
  if ((retVal = Pcie_cfgObOffset(handle, obAddrLo, obAddrHi, region)) != pcie_RET_OK) 
  {
    System_printf ("Failed to configure ObOffset registers!\n");
    return retVal;
  }

  /*enable Outbound address translation*/
  memset (&setRegs,    0, sizeof(setRegs));
  memset (&getRegs,    0, sizeof(getRegs));

  getRegs.cmdStatus = &cmdStatus;
  if ((retVal = Pcie_readRegs (handle, pcie_LOCATION_LOCAL, &getRegs)) != pcie_RET_OK) 
  {
    System_printf ("Read CMD STATUS register failed!\n");
    return retVal;
  }
  cmdStatus.obXltEn = 1;
  setRegs.cmdStatus = &cmdStatus;   
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET CMD STATUS register failed!\n");
    return retVal;
  }

  return pcie_RET_OK;
}


/*****************************************************************************
 * Function: Configure and enable Inbound Address Translation 
 ****************************************************************************/
pcieRet_e pcieIbTransCfg(Pcie_Handle handle, pcieIbTransCfg_t *ibCfg)
{
  pcieRet_e retVal;

  pcieRegisters_t      setRegs;
  pcieRegisters_t      getRegs;

  pcieCmdStatusReg_t   cmdStatus;

  memset (&setRegs,   0, sizeof(setRegs));
  memset (&getRegs,   0, sizeof(getRegs));
  memset (&cmdStatus, 0, sizeof(cmdStatus));

  /* Set outbound offset registers */
  if ((retVal = Pcie_cfgIbTrans(handle, ibCfg)) != pcie_RET_OK) 
  {
    System_printf ("Failed to configure Inbound Translation registers!\n");
    return retVal;
  }

  /*enable Inbound address translation*/
  memset (&setRegs,    0, sizeof(setRegs));
  memset (&getRegs,    0, sizeof(getRegs));

  getRegs.cmdStatus = &cmdStatus;
  if ((retVal = Pcie_readRegs (handle, pcie_LOCATION_LOCAL, &getRegs)) != pcie_RET_OK) 
  {
    System_printf ("Read CMD STATUS register failed!\n");
    return retVal;
  }
  cmdStatus.ibXltEn = 1;
  setRegs.cmdStatus = &cmdStatus;   
  
  if ((retVal = Pcie_writeRegs (handle, pcie_LOCATION_LOCAL, &setRegs)) != pcie_RET_OK) 
  {
    System_printf ("SET CMD STATUS register failed!\n");
    return retVal;
  }

  return pcie_RET_OK;
}


/*****************************************************************************
 * Function: Initialize application buffers
 ****************************************************************************/
void pcieInitAppBuf(void)
{
  uint32_t i;
  
  for (i=0; i<PCIE_BUFSIZE_APP; i++)
  {
    dstBuf.buf[i] = 0;	
    srcBuf[i] = i;	
  }
  
  dstBuf.buf[PCIE_BUFSIZE_APP] = PCIE_EXAMPLE_BUF_EMPTY;
}

/*****************************************************************************
 * Function: Check LTSSM status and wait for the link to be up
 ****************************************************************************/
void pcieWaitLinkUp(Pcie_Handle handle)
{
  pcieRegisters_t  getRegs;
  pcieDebug0Reg_t  debug0;
  
  memset (&debug0,  0, sizeof(debug0));
  memset (&getRegs, 0, sizeof(getRegs));
  
  uint8_t ltssmState = 0;
 
  while(ltssmState != pcie_LTSSM_L0)
  {
    cycleDelay(100);
    getRegs.debug0 = &debug0;
    if (Pcie_readRegs (handle, pcie_LOCATION_LOCAL, &getRegs) != pcie_RET_OK) 
    {
      System_printf ("Read LTSSM state failed!\n");
      return;
    }
    ltssmState = debug0.ltssmState;
  }
}



/*****************************************************************************
 * Function: Converts a core local L2 address to a global L2 address 
 *   Input addr:  L2 address to be converted to global.
 *   return:  uint32_t   Global L2 address
 *****************************************************************************/
uint32_t pcieConvert_CoreLocal2GlobalAddr (uint32_t  addr)
{
  uint32_t coreNum;

  /* Get the core number. */
  coreNum = CSL_chipReadReg(CSL_CHIP_DNUM); 
  
  /* Compute the global address. */
  return ((1 << 28) | (coreNum << 24) | (addr & 0x00ffffff));
}    


/*****************************************************************************
 * Function: Main 
 ****************************************************************************/
void main (void)
{
  TSCL = 1;
  uint16_t lock=0;

  pcieRet_e        retVal;
  pcieIbTransCfg_t ibCfg;
  pcieBarCfg_t     barCfg;
  Pcie_Handle      handle = NULL;
  void             *pcieBase;
  uint32_t    i;

  System_printf ("**********************************************\n");
  System_printf ("*             PCIe Test Start                *\n");
  
  if(PcieModeGbl == pcie_RC_MODE)
    System_printf ("*                RC mode                     *\n");
  else
    System_printf ("*                EP mode                     *\n");
  
  System_printf ("**********************************************\n\n");
  
  System_printf ("Version #: 0x%08x; string %s\n\n", Pcie_getVersion(), Pcie_getVersionStr());
  
  /* Initialize application buffers */
  pcieInitAppBuf();

  /* Configure SERDES*/
  if ((retVal = pcieSerdesCfg()) != pcie_RET_OK) {
    System_printf ("PCIe Serdes config failed (%d)\n", (int)retVal);
    exit(1);
  }

  /* Set the PCIe mode*/
  if ((retVal = Pcie_setMode(PcieModeGbl)) != pcie_RET_OK) {
    System_printf ("Set PCIe Mode failed (%d)\n", (int)retVal);
    exit(1);
  }
  
  /* Power up PCIe Module */
  if ((retVal = pciePowerCfg()) != pcie_RET_OK) {
    System_printf ("PCIe Power Up failed (%d)\n", (int)retVal);
    exit(1);
  }

  System_printf ("PCIe Power Up.\n");
  
  /* Wait until the PCIe SERDES PLL locks */
  while (!lock)
  {
    CSL_BootCfgGetPCIEPLLLock(&lock);
  }                    

  if ((retVal = Pcie_open(0, &handle)) != pcie_RET_OK) 
  {
    System_printf ("Open failed (%d)\n", (int)retVal);
    exit(1);
  }

  System_printf ("PLL configured.\n");

  if(PcieModeGbl == pcie_RC_MODE)
  {
    /* Configure application registers for Root Complex*/
    if ((retVal = pcieCfgRC(handle)) != pcie_RET_OK) 
    {
      System_printf ("Failed to configure PCIe in RC mode (%d)\n", (int)retVal);
      exit(1);
    }

    /* Configure Address Translation */
    
    barCfg.location = pcie_LOCATION_LOCAL;
    barCfg.mode     = pcie_RC_MODE;
    barCfg.base     = PCIE_IB_LO_ADDR_M;
    barCfg.prefetch = pcie_BAR_NON_PREF;
    barCfg.type     = pcie_BAR_TYPE32;
    barCfg.memSpace = pcie_BAR_MEM_MEM;
    barCfg.idx      = PCIE_BAR_IDX_M;
    
    if ((retVal = Pcie_cfgBar(handle, &barCfg)) != pcie_RET_OK) 
    {
      System_printf ("Failed to configure BAR (%d)\n", (int)retVal);
      exit(1);
    }

    ibCfg.ibBar         = PCIE_BAR_IDX_M; /* Match BAR that was configured above*/
    ibCfg.ibStartAddrLo = PCIE_IB_LO_ADDR_M;
    ibCfg.ibStartAddrHi = PCIE_IB_HI_ADDR_M;
    ibCfg.ibOffsetAddr  = (uint32_t)pcieConvert_CoreLocal2GlobalAddr ((uint32_t)dstBuf.buf);
    ibCfg.region        = PCIE_IB_REGION_M;       

    if ((retVal = pcieIbTransCfg(handle, &ibCfg)) != pcie_RET_OK) 
    {
      System_printf ("Failed to configure Inbound Translation (%d)\n", (int)retVal);
      exit(1);
    }
    else
    {
      System_printf ("Successfully configured Inbound Translation!\n");
    }

    if ((retVal = pcieObTransCfg (handle, PCIE_OB_LO_ADDR_M, PCIE_OB_HI_ADDR_M, PCIE_OB_REGION_M)) != pcie_RET_OK) 
    {
      System_printf ("Failed to configure Outbound Address Translation (%d)\n", (int)retVal);
      exit(1);
    }
    else
    {
      System_printf ("Successfully configured Outbound Translation!\n");
    }
  }
  else
  {
    /* Configure application registers for End Point*/
    if ((retVal = pcieCfgEP(handle)) != pcie_RET_OK) 
    {
      System_printf ("Failed to configure PCIe in EP mode (%d)\n", (int)retVal);
      exit(1);
    }

    /* Configure Address Translation */
    
    barCfg.location = pcie_LOCATION_LOCAL;
    barCfg.mode     = pcie_EP_MODE;
    barCfg.base     = PCIE_IB_LO_ADDR_S;
    barCfg.prefetch = pcie_BAR_NON_PREF;
    barCfg.type     = pcie_BAR_TYPE32;
    barCfg.memSpace = pcie_BAR_MEM_MEM;
    barCfg.idx      = PCIE_BAR_IDX_S;
    
    if ((retVal = Pcie_cfgBar(handle, &barCfg)) != pcie_RET_OK) 
    {
      System_printf ("Failed to configure BAR!\n");
      exit(1);
    }

    ibCfg.ibBar         = PCIE_BAR_IDX_S; /* Match BAR that was configured above*/
    ibCfg.ibStartAddrLo = PCIE_IB_LO_ADDR_S;
    ibCfg.ibStartAddrHi = PCIE_IB_HI_ADDR_S;
    ibCfg.ibOffsetAddr  = (uint32_t)pcieConvert_CoreLocal2GlobalAddr ((uint32_t)dstBuf.buf);
    ibCfg.region        = PCIE_IB_REGION_S;       

    if ((retVal = pcieIbTransCfg(handle, &ibCfg)) != pcie_RET_OK) 
    {
      System_printf ("Failed to configure Inbound Translation!\n", (int)retVal);
      exit(1);
    }
    else
    {
      System_printf ("Successfully configured Inbound Translation!\n");
    }

    if ((retVal = pcieObTransCfg (handle, PCIE_OB_LO_ADDR_S, PCIE_OB_HI_ADDR_S, PCIE_OB_REGION_S)) != pcie_RET_OK) 
    {
      System_printf ("Failed to configure Outbound Address Translation!\n", (int)retVal);
      exit(1);
    }
    else
    {
      System_printf ("Successfully configured Outbound Translation!\n");
    }
  }

  System_printf ("Starting link training...\n");

  /*Enable link training*/
  if ((retVal = pcieLtssmCtrl(handle, TRUE)) != pcie_RET_OK) 
  {
    System_printf ("Failed to Enable Link Training!\n", (int)retVal);
    exit(1);
  }
  
  /* Wait for link to be up */
  pcieWaitLinkUp(handle);

  System_printf ("Link is up.\n");


  if(PcieModeGbl == pcie_RC_MODE)
  {
    /**********************************************************************/
    /* Push a single message to the EP then verify that it is echoed back */
    /**********************************************************************/

    /* Write from RC to EP                                                */
    if ((retVal = Pcie_getMemSpaceRange (handle, &pcieBase, NULL)) != pcie_RET_OK) {
      System_printf ("getMemSpaceRange failed\n", (int)retVal);
      exit(1);
    }

    for (i=0; i<PCIE_BUFSIZE_APP; i++)
    {
      *((volatile uint32_t *)pcieBase + i) = srcBuf[i];
    }
    
    /* Mark that the buffer is full, so EP can process it */
    *((volatile uint32_t *)pcieBase + PCIE_BUFSIZE_APP) = PCIE_EXAMPLE_BUF_FULL;

    /* Note on cache coherence: Write back is not necessary because pcieBase is in
       peripheral address space instead of physical memory*/
    
    /* Data sent to EP.
       RC waits for the loopback to be completed and
       receive data back from EP */

    do {
      unsigned int key;

      /* Disable Interrupts */
      key = _disable_interrupts();

      /*  Cleanup the prefetch buffer also. */
      CSL_XMC_invalidatePrefetchBuffer();    

      CACHE_invL1d ((void *)dstBuf.buf,  PCIE_EXAMPLE_DSTBUF_BYTES, CACHE_FENCE_WAIT);
      CACHE_invL2  ((void *)dstBuf.buf,  PCIE_EXAMPLE_DSTBUF_BYTES, CACHE_FENCE_WAIT);

      /* Reenable Interrupts. */
      _restore_interrupts(key);

    } while(dstBuf.buf[PCIE_BUFSIZE_APP] != PCIE_EXAMPLE_BUF_FULL);


    /* check all the data */
    for (i=0; i<PCIE_BUFSIZE_APP; i++)
    {
      if(dstBuf.buf[i] != srcBuf[i])
      {
        System_printf ("Received data = %d\nTransmited data = %d\nIndex = %d.\n\nTest failed.\n", 
                        dstBuf.buf[i], srcBuf[i], i);
        exit(1);
      }
    }
    
    System_printf ("Root Complex received data.\nTest passed.\n");
  }
  else
  {
    /**********************************************************************/
    /* Wait for a single message from the RC then echo it back            */
    /**********************************************************************/

    /* EP waits for the data received from RC */
    do {
      unsigned int key;

      /* Disable Interrupts */
      key = _disable_interrupts();

      /*  Cleanup the prefetch buffer also. */
      CSL_XMC_invalidatePrefetchBuffer();    

      CACHE_invL1d ((void *)dstBuf.buf,  PCIE_EXAMPLE_DSTBUF_BYTES, CACHE_FENCE_WAIT);
      CACHE_invL2  ((void *)dstBuf.buf,  PCIE_EXAMPLE_DSTBUF_BYTES, CACHE_FENCE_WAIT);

      /* Reenable Interrupts. */
      _restore_interrupts(key);

    } while(dstBuf.buf[PCIE_BUFSIZE_APP] != PCIE_EXAMPLE_BUF_FULL);

    System_printf ("End Point received data.\n");

    /* Loopback to RC what was written in the DST buffer.
       Write from EP to RC */
    if ((retVal = Pcie_getMemSpaceRange (handle, &pcieBase, NULL)) != pcie_RET_OK) {
      System_printf ("getMemSpaceRange failed\n", (int)retVal);
      exit(1);
    }

    for (i=0; i<PCIE_BUFSIZE_APP; i++)
    {
      *((volatile uint32_t *)pcieBase + i) = dstBuf.buf[i];
    }
    
    /* Mark that the buffer is full, so RC can process it */
    *((volatile uint32_t *)pcieBase + PCIE_BUFSIZE_APP) = PCIE_EXAMPLE_BUF_FULL;

    /* Note on cache coherence: Write back is not necessary because pcieBase is in
       peripheral address space instead of physical memory*/

    System_printf ("End Point sent data to Root Complex, completing the loopback.\nEnd of Test.\n");
  }

  exit(0);
}

