/** @file
PCI Host Bridge Library instance for Marvell 70x0/80x0
Copyright (c) 2017, Linaro Ltd. All rights reserved.
Copyright (c) 2019 Marvell International Ltd. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "PciHostBridgeLibConstructor.h"
/**
This function configures PCIE controllers IATU windows.
@param [in] PcieDbiAddress PCIE controller base address.
@param [in] Index IATU window index.
@param [in] CpuBase Address from the CPU perspective.
@param [in] PciBase Target PCIE address.
@param [in] Size IATU window size.
@param [in] Type IATU window type.
@param [in] EnableFlags Extra configuration flags.
@retval none
**/
STATIC
VOID
ConfigureWindow (
IN EFI_PHYSICAL_ADDRESS PcieDbiAddress,
IN UINTN Index,
IN UINT64 CpuBase,
IN UINT64 PciBase,
IN UINT64 Size,
IN UINTN Type,
IN UINTN EnableFlags
)
{
ArmDataMemoryBarrier ();
MmioWrite32 (PcieDbiAddress + IATU_VIEWPORT_OFF,
IATU_VIEWPORT_OUTBOUND | IATU_VIEWPORT_REGION_INDEX (Index));
ArmDataMemoryBarrier ();
MmioWrite32 (PcieDbiAddress + IATU_LWR_BASE_ADDR_OFF_OUTBOUND_0,
(UINT32)(CpuBase & MAX_UINT32));
MmioWrite32 (PcieDbiAddress + IATU_UPPER_BASE_ADDR_OFF_OUTBOUND_0,
(UINT32)(CpuBase >> 32));
MmioWrite32 (PcieDbiAddress + IATU_LIMIT_ADDR_OFF_OUTBOUND_0,
(UINT32)(CpuBase + Size - 1));
MmioWrite32 (PcieDbiAddress + IATU_LWR_TARGET_ADDR_OFF_OUTBOUND_0,
(UINT32)(PciBase & MAX_UINT32));
MmioWrite32 (PcieDbiAddress + IATU_UPPER_TARGET_ADDR_OFF_OUTBOUND_0,
(UINT32)(PciBase >> 32));
MmioWrite32 (PcieDbiAddress + IATU_REGION_CTRL_1_OFF_OUTBOUND_0,
Type);
MmioWrite32 (PcieDbiAddress + IATU_REGION_CTRL_2_OFF_OUTBOUND_0,
IATU_REGION_CTRL_2_OFF_OUTBOUND_0_REGION_EN | EnableFlags);
}
/**
Perform PCIE slot reset using external GPIO pin.
@param [in] PcieDbiAddress PCIE controller base address.
@retval none
**/
STATIC
VOID
WaitForLink (
IN EFI_PHYSICAL_ADDRESS PcieDbiAddress
)
{
UINT32 Mask;
UINT32 Status;
UINT32 Timeout;
if (!(MmioRead32 (PcieDbiAddress + PCIE_PM_STATUS) & PCIE_PM_LTSSM_STAT_MASK)) {
DEBUG ((DEBUG_INIT, "%a: no PCIE device detected\n", __FUNCTION__));
return;
}
/* Wait for the link to establish itself. */
DEBUG ((DEBUG_INIT, "%a: waiting for PCIE link\n", __FUNCTION__));
Mask = PCIE_GLOBAL_STATUS_RDLH_LINK_UP | PCIE_GLOBAL_STATUS_PHY_LINK_UP;
Timeout = PCIE_LINK_UP_TIMEOUT_US / 10;
do {
Status = MmioRead32 (PcieDbiAddress + PCIE_GLOBAL_STATUS_REG);
if ((Status & Mask) == Mask) {
DEBUG ((DEBUG_ERROR, "pcie@0x%x link UP\n", PcieDbiAddress));
break;
}
/* Wait 10us between each link status check. */
gBS->Stall (10);
} while (Timeout--);
}
/**
Perform PCIE slot reset using external GPIO pin.
@param [in] *PcieResetGpio GPIO pin description.
@retval EFI_SUCEESS PCIE slot reset succeeded.
@retval Other Return error status.
**/
STATIC
EFI_STATUS
ResetPcieSlot (
IN MV_GPIO_PIN CONST *PcieResetGpio
)
{
EMBEDDED_GPIO_MODE Mode;
EMBEDDED_GPIO_PIN GpioPin;
EMBEDDED_GPIO *GpioProtocol;
EFI_STATUS Status;
/* Get GPIO protocol. */
Status = MvGpioGetProtocol (PcieResetGpio->ControllerType, &GpioProtocol);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Unable to find GPIO protocol\n", __FUNCTION__));
return Status;
}
GpioPin = GPIO (PcieResetGpio->ControllerId, PcieResetGpio->PinNumber),
/* Activate reset. */
Mode = PcieResetGpio->ActiveHigh ? GPIO_MODE_OUTPUT_1 : GPIO_MODE_OUTPUT_0;
Status = GpioProtocol->Set (GpioProtocol, GpioPin, Mode);
/*
* According to the PCIE specification, the reset signal must be active
* for minimum 100ms. To be on a safe side, use 150ms delay.
*/
MemoryFence ();
gBS->Stall (150 * 1000);
/* Dectivate reset. */
Mode = PcieResetGpio->ActiveHigh ? GPIO_MODE_OUTPUT_0 : GPIO_MODE_OUTPUT_1;
Status = GpioProtocol->Set (GpioProtocol, GpioPin, Mode);
/*
* The controller cannot be configured (e.g. the clocks have to establish)
* during 20ms period after the reset is deactivated.
*/
MemoryFence ();
gBS->Stall (20 * 1000);
return EFI_SUCCESS;
}
/**
Obtain resources and perform a low-level PCIE controllers
configuration.
@param [in] ImageHandle The image handle.
@param [in] *SystemTable The system table.
@retval EFI_SUCEESS PCIE configuration successful.
@retval Other Return error status.
**/
EFI_STATUS
EFIAPI
Armada7k8kPciHostBridgeLibConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
MV_BOARD_PCIE_DESCRIPTION CONST *BoardPcieDescription;
MARVELL_BOARD_DESC_PROTOCOL *BoardDescriptionProtocol;
MV_PCIE_CONTROLLER CONST *PcieController;
EFI_PHYSICAL_ADDRESS PcieDbiAddress;
EFI_STATUS Status;
UINTN Index;
/* Obtain list of available controllers */
Status = gBS->LocateProtocol (&gMarvellBoardDescProtocolGuid,
NULL,
(VOID **)&BoardDescriptionProtocol);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"%a: Cannot locate BoardDesc protocol\n",
__FUNCTION__));
return EFI_DEVICE_ERROR;
}
Status = BoardDescriptionProtocol->PcieDescriptionGet (
BoardDescriptionProtocol,
&BoardPcieDescription);
if (Status == EFI_NOT_FOUND) {
/* No controllers used on the platform, exit silently */
return EFI_SUCCESS;
} else if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"%a: Cannot get Pcie board desc from BoardDesc protocol\n",
__FUNCTION__));
return EFI_DEVICE_ERROR;
}
for (Index = 0; Index < BoardPcieDescription->PcieControllerCount; Index++) {
PcieController = &(BoardPcieDescription->PcieControllers[Index]);
ASSERT (PcieController->PcieBusMin == 0);
if (PcieController->HaveResetGpio == TRUE) {
/* Reset PCIE slot */
Status = ResetPcieSlot (&PcieController->PcieResetGpio);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"%a: Cannot reset Pcie Slot\n",
__FUNCTION__));
return EFI_DEVICE_ERROR;
}
}
/* Low level PCIE controller configuration */
PcieDbiAddress = PcieController->PcieDbiAddress;
MmioAndThenOr32 (PcieDbiAddress + PORT_LINK_CTRL_OFF,
~PORT_LINK_CTRL_OFF_LINK_CAPABLE_MASK,
PORT_LINK_CTRL_OFF_LINK_CAPABLE_x4);
MmioAndThenOr32 (PcieDbiAddress + GEN2_CTRL_OFF,
~GEN2_CTRL_OFF_NUM_OF_LANES_MASK,
GEN2_CTRL_OFF_NUM_OF_LANES(4) | GEN2_CTRL_OFF_DIRECT_SPEED_CHANGE);
MmioAndThenOr32 (PcieDbiAddress + PCIE_GLOBAL_CTRL_OFFSET,
~(PCIE_GLOBAL_CTRL_DEVICE_TYPE_MASK | PCIE_GLOBAL_APP_LTSSM_EN),
PCIE_GLOBAL_CTRL_DEVICE_TYPE_RC);
MmioWrite32 (PcieDbiAddress + PCIE_ARCACHE_TRC_REG,
ARCACHE_DEFAULT_VALUE);
MmioWrite32 (PcieDbiAddress + PCIE_AWCACHE_TRC_REG,
AWCACHE_DEFAULT_VALUE);
MmioAndThenOr32 (PcieDbiAddress + PCIE_ARUSER_REG,
~AX_USER_DOMAIN_MASK,
AX_USER_DOMAIN_OUTER_SHAREABLE);
MmioAndThenOr32 (PcieDbiAddress + PCIE_AWUSER_REG,
~AX_USER_DOMAIN_MASK,
AX_USER_DOMAIN_OUTER_SHAREABLE);
MmioAndThenOr32 (PcieDbiAddress + PCIE_LINK_CTL_2,
~TARGET_LINK_SPEED_MASK,
LINK_SPEED_GEN_3);
MmioAndThenOr32 (PcieDbiAddress + PCIE_LINK_CAPABILITY,
~TARGET_LINK_SPEED_MASK,
LINK_SPEED_GEN_3);
MmioOr32 (PcieDbiAddress + PCIE_GEN3_EQU_CTRL,
GEN3_EQU_EVAL_2MS_DISABLE);
MmioOr32 (PcieDbiAddress + PCIE_GLOBAL_CTRL_OFFSET,
PCIE_GLOBAL_APP_LTSSM_EN);
/* Region 0: MMIO32 range */
ConfigureWindow (PcieDbiAddress,
PcieController->PcieMmio32WinBase - PcieController->PcieMmio32Translation,
PcieController->PcieMmio32WinBase,
PcieController->PcieMmio32WinBase,
PcieController->PcieMmio32WinSize,
IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_MEM,
0);
/* Region 1: Type 0 config space */
ConfigureWindow (PcieDbiAddress,
1,
PcieController->ConfigSpaceAddress,
0x0,
SIZE_64KB,
IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_CFG0,
IATU_REGION_CTRL_2_OFF_OUTBOUND_0_CFG_SHIFT_MODE);
/* Region 2: Type 1 config space */
ConfigureWindow (PcieDbiAddress,
2,
PcieController->ConfigSpaceAddress + SIZE_64KB,
0x0,
PcieController->PcieBusMax * SIZE_1MB,
IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_CFG1,
IATU_REGION_CTRL_2_OFF_OUTBOUND_0_CFG_SHIFT_MODE);
/* Region 3: port I/O range */
ConfigureWindow (PcieDbiAddress,
3,
PcieController->PcieIoTranslation,
PcieController->PcieIoWinBase,
PcieController->PcieIoWinSize,
IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_IO,
0);
/* Region 4: MMIO64 range */
ConfigureWindow (PcieDbiAddress,
4,
PcieController->PcieMmio64WinBase,
PcieController->PcieMmio64WinBase,
PcieController->PcieMmio64WinSize,
IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_MEM,
0);
MmioOr32 (PcieDbiAddress + PCIE_GLOBAL_INT_MASK1_REG,
PCIE_INT_A_ASSERT_MASK |
PCIE_INT_B_ASSERT_MASK |
PCIE_INT_C_ASSERT_MASK |
PCIE_INT_D_ASSERT_MASK);
WaitForLink (PcieDbiAddress);
/* Enable the RC */
MmioOr32 (PcieDbiAddress + PCI_COMMAND_OFFSET,
EFI_PCI_COMMAND_IO_SPACE |
EFI_PCI_COMMAND_MEMORY_SPACE |
EFI_PCI_COMMAND_BUS_MASTER);
}
return EFI_SUCCESS;
}