/** @file
PCI Host Bridge Library instance for Socionext SynQuacer ARM SOC
Copyright (c) 2017, Linaro Ltd. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IATU_VIEWPORT_OFF 0x900
#define IATU_VIEWPORT_INBOUND BIT31
#define IATU_VIEWPORT_OUTBOUND 0
#define IATU_VIEWPORT_REGION_INDEX(Idx) ((Idx) & 7)
#define IATU_REGION_CTRL_1_OFF_OUTBOUND_0 0x904
#define IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_MEM 0x0
#define IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_IO 0x2
#define IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_CFG0 0x4
#define IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_CFG1 0x5
#define IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TH BIT12
#define IATU_REGION_CTRL_2_OFF_OUTBOUND_0 0x908
#define IATU_REGION_CTRL_2_OFF_OUTBOUND_0_REGION_EN BIT31
#define IATU_REGION_CTRL_2_OFF_OUTBOUND_0_CFG_SHIFT_MODE BIT28
#define IATU_REGION_CTRL_2_OFF_OUTBOUND_0_MSG_CODE_32BIT 0xF
#define IATU_REGION_CTRL_2_OFF_OUTBOUND_0_MSG_CODE_64BIT 0xFF
#define IATU_LWR_BASE_ADDR_OFF_OUTBOUND_0 0x90C
#define IATU_UPPER_BASE_ADDR_OFF_OUTBOUND_0 0x910
#define IATU_LIMIT_ADDR_OFF_OUTBOUND_0 0x914
#define IATU_LWR_TARGET_ADDR_OFF_OUTBOUND_0 0x918
#define IATU_UPPER_TARGET_ADDR_OFF_OUTBOUND_0 0x91C
#define CORE_CONTROL 0x000
#define APP_LTSSM_ENABLE BIT4
#define DEVICE_TYPE (BIT3 | BIT2 | BIT1 | BIT0)
#define AXI_CLK_STOP 0x004
#define DBI_ACLK_STOP BIT8
#define SLV_ACLK_STOP BIT4
#define MSTR_ACLK_STOP BIT0
#define DBI_CSYSREQ_REG BIT9
#define SLV_CSYSREQ_REG BIT5
#define MSTR_CSYSREQ_REG BIT1
#define RESET_CONTROL_1 0x00C
#define PERST_N_O_REG BIT5
#define PERST_N_I_REG BIT4
#define BUTTON_RST_N_REG BIT1
#define PWUP_RST_N_REG BIT0
#define RESET_CONTROL_2 0x010
#define RESET_SELECT_1 0x014
#define SQU_RST_SEL BIT29
#define PHY_RST_SEL BIT28
#define PWR_RST_SEL BIT24
#define STI_RST_SEL BIT20
#define N_STI_RST_SEL BIT16
#define CORE_RST_SEL BIT12
#define PERST_SEL BIT4
#define BUTTON_RST_SEL BIT1
#define PWUP_RST_SEL BIT0
#define RESET_SELECT_2 0x018
#define DBI_ARST_SEL BIT8
#define SLV_ARST_SEL BIT4
#define MSTR_ARST_SEL BIT0
#define EM_CONTROL 0x030
#define PRE_DET_STT_REG BIT4
#define EM_SELECT 0x034
#define PRE_DET_STT_SEL BIT4
#define PM_CONTROL_2 0x050
#define SYS_AUX_PWR_DET BIT8
#define PHY_CONFIG_COM_6 0x114
#define PIPE_PORT_SEL (BIT1 | BIT0)
#define LINK_MONITOR 0x210
#define SMLH_LINK_UP BIT0
#define LINK_CAPABILITIES_REG 0x07C
#define PCIE_CAP_MAX_LINK_WIDTH (BIT7 | BIT6 | BIT5 | BIT4)
#define PCIE_CAP_MAX_LINK_SPEED (BIT3 | BIT2 | BIT1 | BIT0)
#define LINK_CONTROL_LINK_STATUS_REG 0x080
#define PCIE_CAP_NEGO_LINK_WIDTH (BIT23 | BIT22 | BIT21 | BIT20)
#define PCIE_CAP_LINK_SPEED (BIT19 | BIT18 | BIT17 | BIT16)
#define TYPE1_CLASS_CODE_REV_ID_REG 0x008
#define BASE_CLASS_CODE 0xFF000000
#define BASE_CLASS_CODE_VALUE 0x06
#define SUBCLASS_CODE 0x00FF0000
#define SUBCLASS_CODE_VALUE 0x04
#define PROGRAM_INTERFACE 0x0000FF00
#define PROGRAM_INTERFACE_VALUE 0x00
#define GEN2_CONTROL_OFF 0x80c
#define DIRECT_SPEED_CHANGE BIT17
#define MISC_CONTROL_1_OFF 0x8BC
#define DBI_RO_WR_EN BIT0
extern PCI_ROOT_BRIDGE mPciRootBridges[];
STATIC
VOID
ConfigureWindow (
IN EFI_PHYSICAL_ADDRESS DbiBase,
IN UINTN Index,
IN UINT64 CpuBase,
IN UINT64 PciBase,
IN UINT64 Size,
IN UINTN Type,
IN UINTN EnableFlags
)
{
ArmDataMemoryBarrier ();
MmioWrite32 (DbiBase + IATU_VIEWPORT_OFF,
IATU_VIEWPORT_OUTBOUND | IATU_VIEWPORT_REGION_INDEX (Index));
ArmDataMemoryBarrier ();
MmioWrite32 (DbiBase + IATU_LWR_BASE_ADDR_OFF_OUTBOUND_0,
(UINT32)(CpuBase & 0xFFFFFFFF));
MmioWrite32 (DbiBase + IATU_UPPER_BASE_ADDR_OFF_OUTBOUND_0,
(UINT32)(CpuBase >> 32));
MmioWrite32 (DbiBase + IATU_LIMIT_ADDR_OFF_OUTBOUND_0,
(UINT32)(CpuBase + Size - 1));
MmioWrite32 (DbiBase + IATU_LWR_TARGET_ADDR_OFF_OUTBOUND_0,
(UINT32)(PciBase & 0xFFFFFFFF));
MmioWrite32 (DbiBase + IATU_UPPER_TARGET_ADDR_OFF_OUTBOUND_0,
(UINT32)(PciBase >> 32));
MmioWrite32 (DbiBase + IATU_REGION_CTRL_1_OFF_OUTBOUND_0,
Type);
MmioWrite32 (DbiBase + IATU_REGION_CTRL_2_OFF_OUTBOUND_0,
IATU_REGION_CTRL_2_OFF_OUTBOUND_0_REGION_EN | EnableFlags);
}
STATIC
VOID
SnPcieSetData (
EFI_PHYSICAL_ADDRESS Base,
UINT32 Offset,
UINT32 Mask,
UINT32 In
)
{
UINT32 Data;
UINT32 Shift;
Shift = 1;
if (In) {
while (!(Mask & Shift))
Shift <<= 1;
Data = (MmioRead32 (Base + Offset) & ~Mask) | (In * Shift);
} else {
Data = MmioRead32 (Base + Offset) & ~Mask;
}
MmioWrite32 (Base + Offset, Data);
ArmDataMemoryBarrier ();
}
STATIC
UINT32
SnPcieReadData (
EFI_PHYSICAL_ADDRESS Base,
UINT32 Offset,
UINT32 Mask
)
{
UINT32 Shift;
Shift = 0;
while (!(Mask & 1)) {
Mask >>= 1;
Shift++;
}
ArmDataMemoryBarrier ();
return (MmioRead32 (Base + Offset) >> Shift) & Mask;
}
STATIC
VOID
SnDbiRoWrEn (
IN EFI_PHYSICAL_ADDRESS DbiBase,
IN INTN MaxLinkWidth,
IN INTN MaxLinkSpeed
)
{
SnPcieSetData (DbiBase, MISC_CONTROL_1_OFF, DBI_RO_WR_EN, 1);
SnPcieSetData (DbiBase, LINK_CAPABILITIES_REG, PCIE_CAP_MAX_LINK_WIDTH, MaxLinkWidth);
SnPcieSetData (DbiBase, LINK_CAPABILITIES_REG, PCIE_CAP_MAX_LINK_SPEED, MaxLinkSpeed);
SnPcieSetData (DbiBase, TYPE1_CLASS_CODE_REV_ID_REG, BASE_CLASS_CODE, BASE_CLASS_CODE_VALUE);
SnPcieSetData (DbiBase, TYPE1_CLASS_CODE_REV_ID_REG, SUBCLASS_CODE, SUBCLASS_CODE_VALUE);
SnPcieSetData (DbiBase, TYPE1_CLASS_CODE_REV_ID_REG, PROGRAM_INTERFACE, PROGRAM_INTERFACE_VALUE);
SnPcieSetData (DbiBase, MISC_CONTROL_1_OFF, DBI_RO_WR_EN, 0);
}
STATIC
VOID
PciInitControllerPre (
IN EFI_PHYSICAL_ADDRESS ExsBase
)
{
SnPcieSetData (ExsBase, EM_SELECT, PRE_DET_STT_SEL, 0);
SnPcieSetData (ExsBase, EM_CONTROL, PRE_DET_STT_REG, 0);
SnPcieSetData (ExsBase, EM_CONTROL, PRE_DET_STT_REG, 1);
// 1: Assert all PHY / LINK resets
SnPcieSetData (ExsBase, RESET_SELECT_1 , PERST_SEL , 0);
SnPcieSetData (ExsBase, RESET_CONTROL_1, PERST_N_I_REG , 0);
SnPcieSetData (ExsBase, RESET_CONTROL_1, PERST_N_O_REG , 0);
// Device Reset(PERST#) is effective afrer Set device_type (RC)
SnPcieSetData (ExsBase, RESET_SELECT_1 , PWUP_RST_SEL , 0);
SnPcieSetData (ExsBase, RESET_CONTROL_1, PWUP_RST_N_REG, 0);
SnPcieSetData (ExsBase, RESET_SELECT_1 , BUTTON_RST_SEL , 0);
SnPcieSetData (ExsBase, RESET_CONTROL_1, BUTTON_RST_N_REG, 0);
SnPcieSetData (ExsBase, RESET_SELECT_1 , PWR_RST_SEL , 1);
SnPcieSetData (ExsBase, RESET_SELECT_2 , MSTR_ARST_SEL , 1);
SnPcieSetData (ExsBase, RESET_SELECT_2 , SLV_ARST_SEL , 1);
SnPcieSetData (ExsBase, RESET_SELECT_2 , DBI_ARST_SEL , 1);
SnPcieSetData (ExsBase, RESET_SELECT_1 , CORE_RST_SEL , 1);
SnPcieSetData (ExsBase, RESET_SELECT_1 , STI_RST_SEL , 1);
SnPcieSetData (ExsBase, RESET_SELECT_1 , N_STI_RST_SEL , 1);
SnPcieSetData (ExsBase, RESET_SELECT_1 , SQU_RST_SEL , 1);
SnPcieSetData (ExsBase, RESET_SELECT_1 , PHY_RST_SEL , 1);
// 2: Set P_app_ltssm_enable='0' for reprogramming before linkup.
SnPcieSetData (ExsBase, CORE_CONTROL, APP_LTSSM_ENABLE, 0);
// 3: Set device_type (RC)
SnPcieSetData (ExsBase, CORE_CONTROL, DEVICE_TYPE, 4);
}
STATIC
VOID
PciInitControllerPost (
IN EFI_PHYSICAL_ADDRESS ExsBase,
IN EFI_PHYSICAL_ADDRESS DbiBase,
IN EFI_PHYSICAL_ADDRESS ConfigBase,
IN EFI_PHYSICAL_ADDRESS IoMemBase,
IN CONST PCI_ROOT_BRIDGE *RootBridge,
IN BOOLEAN EnableGen2Speed
)
{
// 4: Set Bifurcation 1=disable 4=able
// 5: Supply Reference (It has executed)
// 6: Wait for 10usec (Reference Clocks is stable)
// 7 De assert PERST# */
SnPcieSetData (ExsBase, RESET_CONTROL_1, PERST_N_I_REG, 1);
SnPcieSetData (ExsBase, RESET_CONTROL_1, PERST_N_O_REG, 1);
// 8 Assert SYS_AUX_PWR_DET
SnPcieSetData(ExsBase, PM_CONTROL_2, SYS_AUX_PWR_DET, 1);
// 9 Supply following clocks
SnPcieSetData (ExsBase, AXI_CLK_STOP, MSTR_CSYSREQ_REG, 1);
SnPcieSetData (ExsBase, AXI_CLK_STOP, MSTR_ACLK_STOP, 0);
SnPcieSetData (ExsBase, AXI_CLK_STOP, SLV_CSYSREQ_REG, 1);
SnPcieSetData (ExsBase, AXI_CLK_STOP, SLV_ACLK_STOP, 0);
SnPcieSetData (ExsBase, AXI_CLK_STOP, DBI_CSYSREQ_REG, 1);
SnPcieSetData (ExsBase, AXI_CLK_STOP, DBI_ACLK_STOP, 0);
// 10 De assert PHY reset
// 11 De assert LINK's PMC reset
SnPcieSetData (ExsBase, RESET_CONTROL_1, PWUP_RST_N_REG, 1);
SnPcieSetData (ExsBase, RESET_CONTROL_1, BUTTON_RST_N_REG, 1);
// 12 PHY auto
// 13 Wrapper auto
// 14-17 PHY auto
// 18 Wrapper auto
// 19 Update registers through DBI AXI Slave interface
SnDbiRoWrEn (DbiBase, 4 /* lanes */, /* Gen */ 2);
//
// ECAM shift mode uses bits [27:12] of the untranslated address as
// B/D/F identifiers. This only works as expected if the base of the
// region is aligned to 256 MB, or the effective bus numbers will be
// out of sync with the bus base and limit values we chose.
//
ASSERT ((ConfigBase % SIZE_256MB) == RootBridge->Bus.Base * SIZE_1MB);
MmioOr32 (DbiBase + PCI_COMMAND_OFFSET, EFI_PCI_COMMAND_IO_SPACE |
EFI_PCI_COMMAND_MEMORY_SPACE |
EFI_PCI_COMMAND_BUS_MASTER);
if (EnableGen2Speed) {
// Force link speed change to Gen2 at link up
MmioOr32 (DbiBase + GEN2_CONTROL_OFF, DIRECT_SPEED_CHANGE);
}
// Region 0: MMIO32 range
ConfigureWindow (DbiBase, 0,
RootBridge->Mem.Base - RootBridge->Mem.Translation,
RootBridge->Mem.Base,
RootBridge->Mem.Limit - RootBridge->Mem.Base + 1,
IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_MEM |
IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TH,
IATU_REGION_CTRL_2_OFF_OUTBOUND_0_MSG_CODE_32BIT);
// Region 1: Type 0 config space
ConfigureWindow (DbiBase, 1,
ConfigBase + RootBridge->Bus.Base * SIZE_1MB,
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 (DbiBase, 2,
ConfigBase + RootBridge->Bus.Base * SIZE_1MB + SIZE_64KB,
0x0,
(RootBridge->Bus.Limit - RootBridge->Bus.Base + 1) * SIZE_1MB - SIZE_64KB,
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 (DbiBase, 3,
IoMemBase,
RootBridge->Io.Base,
RootBridge->Io.Limit - RootBridge->Io.Base + 1,
IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_IO,
0);
// Region 4: MMIO64 range
if (RootBridge->AllocationAttributes & EFI_PCI_HOST_BRIDGE_MEM64_DECODE) {
ConfigureWindow (DbiBase, 4,
RootBridge->MemAbove4G.Base,
RootBridge->MemAbove4G.Base,
RootBridge->MemAbove4G.Limit - RootBridge->MemAbove4G.Base + 1,
IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_MEM |
IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TH,
IATU_REGION_CTRL_2_OFF_OUTBOUND_0_MSG_CODE_32BIT);
}
// enable link
if (SnPcieReadData (ExsBase, CORE_CONTROL, APP_LTSSM_ENABLE) == 0) {
SnPcieSetData (ExsBase, CORE_CONTROL, APP_LTSSM_ENABLE, 1);
}
}
STATIC CONST struct {
EFI_PHYSICAL_ADDRESS DbiBase;
EFI_PHYSICAL_ADDRESS ExsBase;
EFI_PHYSICAL_ADDRESS ConfigBase;
EFI_PHYSICAL_ADDRESS IoMemBase;
} mBaseAddresses [] = {
{
SYNQUACER_PCI_SEG0_DBI_BASE,
SYNQUACER_PCI_SEG0_EXS_BASE,
SYNQUACER_PCI_SEG0_CONFIG_BASE,
SYNQUACER_PCI_SEG0_PORTIO_MEMBASE
},
{
SYNQUACER_PCI_SEG1_DBI_BASE,
SYNQUACER_PCI_SEG1_EXS_BASE,
SYNQUACER_PCI_SEG1_CONFIG_BASE,
SYNQUACER_PCI_SEG1_PORTIO_MEMBASE
},
};
EFI_STATUS
EFIAPI
SynQuacerPciHostBridgeLibConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
UINTN Idx;
UINT64 SettingsVal;
SYNQUACER_PLATFORM_VARSTORE_DATA *Settings;
UINT8 MaxSpeed;
SettingsVal = PcdGet64 (PcdPlatformSettings);
Settings = (SYNQUACER_PLATFORM_VARSTORE_DATA *)&SettingsVal;
for (Idx = 0; Idx < ARRAY_SIZE (mBaseAddresses); Idx++) {
if (PcdGet8 (PcdPcieEnableMask) & (1 << Idx)) {
PciInitControllerPre (mBaseAddresses[Idx].ExsBase);
}
}
//
// The PCIe spec requires that PERST# is asserted for at least 100 ms after
// the power and clocks have become stable. So let's give a bit or margin,
// and stall for 150 ms between asserting PERST# on both controllers and
// de-asserting it again.
//
gBS->Stall (150 * 1000);
for (Idx = 0; Idx < ARRAY_SIZE (mBaseAddresses); Idx++) {
//
// Check whether this root port is described by any of our 'slot'
// definitions, and get the maximum speed if this is the case.
//
switch (SYNQUACER_PCI_LOCATION (Idx, 0, 0)) {
case SYNQUACER_PCI_SLOT0_LOCATION:
MaxSpeed = Settings->PcieSlot0MaxSpeed;
break;
case SYNQUACER_PCI_SLOT1_LOCATION:
MaxSpeed = Settings->PcieSlot1MaxSpeed;
break;
case SYNQUACER_PCI_SLOT2_LOCATION:
MaxSpeed = Settings->PcieSlot2MaxSpeed;
break;
default:
MaxSpeed = PCIE_MAX_SPEED_UNLIMITED;
}
if (PcdGet8 (PcdPcieEnableMask) & (1 << Idx)) {
PciInitControllerPost (mBaseAddresses[Idx].ExsBase,
mBaseAddresses[Idx].DbiBase,
mBaseAddresses[Idx].ConfigBase,
mBaseAddresses[Idx].IoMemBase,
&mPciRootBridges[Idx],
(MaxSpeed != PCIE_MAX_SPEED_GEN1));
}
}
return EFI_SUCCESS;
}