AM64x MCU+ SDK  09.02.01
PCIE

PCIE is a peripheral used for high speed data transfer between devices. The PCIe driver provides API to perform initialization, configuration of End point (EP)and Root complex (RC) mode of operation, configuring and sending interrupts.

Features Supported

Note
PCIe makes use of 2 Ring Accelerators (Rings for IPC), one each for MSI and MSIx to route interrupts to the core.
The actual index for the Ring Accelerator, Global Event and Virtual Interrupt is depended on the Board Config for the soc and core.
  • EP and RC operation
  • Gen 1 and 2 operation speed
  • x1 lane support
  • Legacy interrupts
  • MSI (Message Signalled Interrupt)

SysConfig Features

Note
It is strongly recommend to use SysConfig where it is available instead of using direct SW API calls. This will help simplify the SW application and also catch common mistakes early in the development cycle.

SysConfig can be used to configure below parameters apart from common configuration like Clock,MPU,RAT and others.

  • PCIe operation mode - Root complex (RC) or End point (EP)
  • PCIe instances
  • Vendor ID, Device ID, Subsystem Vendor ID, Subsystem ID
  • Class Code, Sub-Class Code, Programming Interface, Revision ID
  • Operation speed (Gen1 or Gen2)
  • Number of lanes (only x1 supported)
  • Reference clock mode
    • Internal or external reference clock
    • Reference clock output enabled or disabled (for internal reference clock)
    • Optional spread spectrum clocking
  • SRIS (Separate reference, independent spread) reference clock configuration
  • Legacy interrupt pin to use
    • INTA-INTD or disabled
  • Number of MSI vectors requested by the device (multiple message capable)
  • Inbound Address Translation Unit (ATU) configuration
  • Outbound Address Translation Unit (ATU) configuration

Features NOT supported

  • Bridge mode of operation
  • x2 and x4 lanes

Usage Overview

Initializing PCIe driver

Pcie_init() must be called before any other PCIe APIs. This function iterates through the elements of the gPcieConfig[] array, initializing the driver. Please note that initializing of PCIe driver is taken care by the SysConfig generated code.

Opening PCIe driver

After initializing the driver by calling Pcie_init(), the application can open a PCIe instance by calling Pcie_open(). Please note that the opening of PCIe driver is taken care by the SysConfig generated code. This function takes and index into the gPcieConfig[] array, and the PCIe parameters data structure. The PCIe instance is specified by the index of the PCIe in the gPcieConfig[]. Calling Pcie_open() second time with same index, previously passed to Pcie_open() will result in an error.

Configuration of the PCIe driver

In case of a root complex, configuration of the RC is complete after the call to Pcie_open().

In case of an endpoint, only a minimum amount of configuration is performed by the PCIe driver as part of Pcie_open(), and configuration needs to be finalized with a call to Pcie_cfgEP().

The endpoint intially blocks configuration space accesses from the RC by responding with a Configuration Request Retry Status (CRS). This allows the EP to postpone enumeration by the RC until the EP had a chance to setup its configuration space. The EP can modify its own configuration space only when a PCIe reference clock is available.

For setups where the EP always has a reference clock available, an endpoint application can immediately follow-up with a call to Pcie_cfgEP(). For setups where the EP can't rely on the reference clock being continously available, the endpoint must wait for the reference clock before calling Pcie_cfgEP(). The endpoint can detect the presence of a Link via Pcie_isLinkUp() as an indication of whether the reference clock is available.

After a call to Pcie_cfgEP(), the EP responds to configuration space accesses.

Important Usage Guidelines

  • Care must be taken so that the ATU address regions does not overlap. The ATU configuration can be done using SysConfig.
  • The zeroth Outbound ATU region for RC device is used for configuration space access of the remote EP. This configuration is performed implicitly as part of Pcie_open().
  • The usage of Inbound ATU regions and Outbound ATU regions differs between RC and EP:
    • A RC may have at most two inbound ATU regions that can optionally be used to remap addresses from PCIe space to local SoC memory space, but these are optional.
    • An EP may have up six inbound ATU regions (actually BARs) that can each map a single contiguous memory region from SoC memory space to PCIe space. The number of BARs depends on their properties, as a 64-bit BAR (that can be located anywhere in the 64-bit PCIe memory space) takes up to consecutive BARs. Only BARs 0, 2 and 4 may be configured as 64-bit BARs.
    • An RC may statically configure its outbound ATU regions, because it might statically partition its view of PCIe space (e.g. 64 KB configuration space, 64 KB I/O space, 64 MB memory space), and the RC assigns addresses within the PCIe space to EPs at its own discretion.
    • An EP typically needs to configure its outbound ATU regions dynamically, because the PCIe addresses it needs to access are determined by the RC at runtime.
  • Address translation on these devices works by replacing a number of MSB bits when passing addresses from PCIe to the SoC and vice versa, while leaving a number of LSB bits unmodified. The number of MSB vs. LSB bits depends on the size of the memory region, e.g. a 64 KB region passes 16 LSB bits unmodified and replaces the 16 MSB bits. Based on this, memory region sizes need to be a power of two, and the addresses, both local and on the PCIe side, need to be aligned to the region's size. If you need to access some memory that isn't itself a power of two, or if the base address isn't aligned to the region's size, you need to
    • map a larger area that covers all of the memory of intereset, at the risk of mapping additional, unwanted regions as well
    • align the base address and size of your data structures to a power of two, e.g. by manually placing them in a linker script

Example Usage

Include the below file to access the APIs

#include <drivers/pcie.h>

Instance open Example

gPcieHandle = Pcie_open(CONFIG_PCIE0);
DebugP_assert(gPcieHandle != NULL);

Instance close Example

Pcie_close(gPcieHandle);

Outbound ATU config Example

int32_t status;
Pcie_AtuRegionParams regionParams;
uint32_t regionIndex = 1;
regionParams.tlpType = PCIE_TLP_TYPE_MEM;
regionParams.lowerBaseAddr = 0x68000000UL + 0x01000000U;
regionParams.upperBaseAddr = 0x0;
regionParams.regionWindowSize = 0xFFFU;
regionParams.lowerTargetAddr = 0x70000000U;
regionParams.upperTargetAddr = 0x0U;
status = Pcie_atuRegionConfig (gPcieHandle, PCIE_LOCATION_LOCAL, regionIndex, &regionParams);

Inbound ATU config Example

int32_t status;
Pcie_AtuRegionParams regionParams;
uint32_t regionIndex = 1;
regionParams.tlpType = PCIE_TLP_TYPE_MEM;
regionParams.lowerBaseAddr = 0x90000000U;
regionParams.upperBaseAddr = 0x0;
regionParams.regionWindowSize = 0xFFFU;
regionParams.lowerTargetAddr = (uint32_t)dst_buf;
regionParams.upperTargetAddr = 0x0U;
status = Pcie_atuRegionConfig (gPcieHandle, PCIE_LOCATION_LOCAL, regionIndex, &regionParams);

BAR Configuration for EP Example

Pcie_BarCfg barCfg;
int32_t status;
uint32_t regionIndex = 0;
barCfg.mode = PCIE_EP_MODE;
barCfg.barxc = PCIE_BARC_32B_MEM_BAR_NON_PREFETCH;
barCfg.barxa = PCIE_RCBARA_4K;
barCfg.idx = regionIndex;
status = Pcie_cfgBar (object->handle, &barCfg);

API

APIs for PCIE

PCIE_EP_MODE
@ PCIE_EP_MODE
Definition: pcie/pcie.h:95
Pcie_BarCfg::barxc
uint8_t barxc
Definition: pcie/pcie.h:579
Pcie_AtuRegionParams::lowerTargetAddr
uint32_t lowerTargetAddr
Definition: pcie/pcie.h:643
PCIE_TLP_TYPE_MEM
@ PCIE_TLP_TYPE_MEM
Definition: pcie/pcie.h:222
Pcie_close
void Pcie_close(Pcie_Handle handle)
Function to close PCIe peripheral specified by PCIe handle.
PCIE_ATU_REGION_DIR_OUTBOUND
@ PCIE_ATU_REGION_DIR_OUTBOUND
Definition: pcie/pcie.h:212
Pcie_BarCfg::barxa
uint8_t barxa
Definition: pcie/pcie.h:581
Pcie_AtuRegionParams::lowerBaseAddr
uint32_t lowerBaseAddr
Definition: pcie/pcie.h:628
pcie.h
Pcie_BarCfg
PCIe BAR configuration info.
Definition: pcie/pcie.h:563
Pcie_AtuRegionParams::tlpType
Pcie_TlpType tlpType
Definition: pcie/pcie.h:613
Pcie_open
Pcie_Handle Pcie_open(uint32_t index)
This function opens a given PCIe peripheral.
Pcie_AtuRegionParams
This Structure defines the ATU region parameters.
Definition: pcie/pcie.h:608
SystemP_SUCCESS
#define SystemP_SUCCESS
Return status when the API execution was successful.
Definition: SystemP.h:56
Pcie_AtuRegionParams::upperTargetAddr
uint32_t upperTargetAddr
Definition: pcie/pcie.h:648
Pcie_atuRegionConfig
int32_t Pcie_atuRegionConfig(Pcie_Handle handle, Pcie_Location location, uint32_t atuRegionIndex, const Pcie_AtuRegionParams *atuRegionParams)
Configure address translation registers.
Pcie_AtuRegionParams::regionDir
Pcie_AtuRegionDir regionDir
Definition: pcie/pcie.h:609
Pcie_cfgBar
int32_t Pcie_cfgBar(Pcie_Handle handle, const Pcie_BarCfg *barCfg)
Configure a BAR Register (32 bits)
PCIE_ATU_REGION_DIR_INBOUND
@ PCIE_ATU_REGION_DIR_INBOUND
Definition: pcie/pcie.h:213
Pcie_BarCfg::location
Pcie_Location location
Definition: pcie/pcie.h:565
Pcie_BarCfg::idx
uint8_t idx
Definition: pcie/pcie.h:577
PCIE_LOCATION_LOCAL
@ PCIE_LOCATION_LOCAL
Definition: pcie/pcie.h:507
Pcie_BarCfg::mode
Pcie_Mode mode
Definition: pcie/pcie.h:567
DebugP_assert
#define DebugP_assert(expression)
Function to call for assert check.
Definition: DebugP.h:177
Pcie_AtuRegionParams::upperBaseAddr
uint32_t upperBaseAddr
Definition: pcie/pcie.h:633
Pcie_AtuRegionParams::regionWindowSize
uint64_t regionWindowSize
Definition: pcie/pcie.h:638