From 682607fbd775e37fb5631508434dab9e60220c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20B=C4=83l=C4=83nic=C4=83?= Date: Wed, 6 Mar 2024 06:10:52 +0200 Subject: [PATCH] feat(rpi5): add PCI SMCCC support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BCM2712 changes: - support all 3 PCIe RCs / segments. - don't check for link up: the RC can now be configured to fabricate all-ones AXI OKAY responses, so no more Arm SErrors when the link is down (or other conditions). Also, limit bus 0 to devfn 0 as accesses beyond that may result in lock-ups. Change-Id: Ic64785cd68b22571c6638fc3f771703113bc76f6 Signed-off-by: Mario Bălănică --- .../rpi4_pci_svc.c => common/rpi_pci_svc.c} | 95 ++++++++++--------- plat/rpi/rpi4/include/rpi_hw.h | 7 ++ plat/rpi/rpi4/platform.mk | 2 +- plat/rpi/rpi5/include/rpi_hw.h | 7 ++ plat/rpi/rpi5/platform.mk | 8 ++ 5 files changed, 73 insertions(+), 46 deletions(-) rename plat/rpi/{rpi4/rpi4_pci_svc.c => common/rpi_pci_svc.c} (76%) diff --git a/plat/rpi/rpi4/rpi4_pci_svc.c b/plat/rpi/common/rpi_pci_svc.c similarity index 76% rename from plat/rpi/rpi4/rpi4_pci_svc.c rename to plat/rpi/common/rpi_pci_svc.c index e4ef5c1ae..c22f6d89a 100644 --- a/plat/rpi/rpi4/rpi4_pci_svc.c +++ b/plat/rpi/common/rpi_pci_svc.c @@ -1,9 +1,10 @@ /* - * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2024, Mario Bălănică * * SPDX-License-Identifier: BSD-3-Clause * - * The RPi4 has a single nonstandard PCI config region. It is broken into two + * The RPi has a single nonstandard PCI config region. It is broken into two * pieces, the root port config registers and a window to a single device's * config space which can move between devices. There isn't (yet) an * authoritative public document on this since the available BCM2711 reference @@ -29,62 +30,63 @@ #include -static spinlock_t pci_lock; - -#define PCIE_REG_BASE U(RPI_IO_BASE + 0x01500000) #define PCIE_MISC_PCIE_STATUS 0x4068 #define PCIE_EXT_CFG_INDEX 0x9000 -/* A small window pointing at the ECAM of the device selected by CFG_INDEX */ #define PCIE_EXT_CFG_DATA 0x8000 +#define PCIE_EXT_CFG_BDF_SHIFT 12 + #define INVALID_PCI_ADDR 0xFFFFFFFF -#define PCIE_EXT_BUS_SHIFT 20 -#define PCIE_EXT_DEV_SHIFT 15 -#define PCIE_EXT_FUN_SHIFT 12 +static spinlock_t pci_lock; +static uint64_t pcie_rc_bases[] = { RPI_PCIE_RC_BASES }; static uint64_t pci_segment_lib_get_base(uint32_t address, uint32_t offset) { - uint64_t base; - uint32_t bus, dev, fun; - uint32_t status; + uint64_t base; + uint32_t seg, bus, dev, fun; - base = PCIE_REG_BASE; + seg = PCI_ADDR_SEG(address); - offset &= PCI_OFFSET_MASK; /* Pick off the 4k register offset */ + if (seg >= ARRAY_SIZE(pcie_rc_bases)) { + return INVALID_PCI_ADDR; + } /* The root port is at the base of the PCIe register space */ - if (address != 0U) { - /* - * The current device must be at CFG_DATA, a 4K window mapped, - * via CFG_INDEX, to the device we are accessing. At the same - * time we must avoid accesses to certain areas of the cfg - * space via CFG_DATA. Detect those accesses and report that - * the address is invalid. - */ - base += PCIE_EXT_CFG_DATA; - bus = PCI_ADDR_BUS(address); - dev = PCI_ADDR_DEV(address); - fun = PCI_ADDR_FUN(address); - address = (bus << PCIE_EXT_BUS_SHIFT) | - (dev << PCIE_EXT_DEV_SHIFT) | - (fun << PCIE_EXT_FUN_SHIFT); - - /* Allow only dev = 0 on root port and bus 1 */ - if ((bus < 2U) && (dev > 0U)) { - return INVALID_PCI_ADDR; - } + base = pcie_rc_bases[seg]; + + bus = PCI_ADDR_BUS(address); + dev = PCI_ADDR_DEV(address); + fun = PCI_ADDR_FUN(address); - /* Assure link up before reading bus 1 */ - status = mmio_read_32(PCIE_REG_BASE + PCIE_MISC_PCIE_STATUS); - if ((status & 0x30) != 0x30) { + /* There can only be the root port on bus 0 */ + if ((bus == 0U) && ((dev > 0U) || (fun > 0U))) { + return INVALID_PCI_ADDR; + } + + /* There can only be one device on bus 1 */ + if ((bus == 1U) && (dev > 0U)) { + return INVALID_PCI_ADDR; + } + + if (bus > 0) { +#if RPI_PCIE_ECAM_SERROR_QUIRK + uint32_t status = mmio_read_32(base + PCIE_MISC_PCIE_STATUS); + + /* Assure link up before accessing downstream of root port */ + if ((status & 0x30) == 0U) { return INVALID_PCI_ADDR; } - - /* Adjust which device the CFG_DATA window is pointing at */ - mmio_write_32(PCIE_REG_BASE + PCIE_EXT_CFG_INDEX, address); +#endif + /* + * Device function is mapped at CFG_DATA, a 4 KB window + * movable by writing its B/D/F location to CFG_INDEX. + */ + mmio_write_32(base + PCIE_EXT_CFG_INDEX, address << PCIE_EXT_CFG_BDF_SHIFT); + base += PCIE_EXT_CFG_DATA; } - return base + offset; + + return base + (offset & PCI_OFFSET_MASK); } /** @@ -130,7 +132,7 @@ uint32_t pci_read_config(uint32_t addr, uint32_t off, uint32_t sz, uint32_t *val *val = mmio_read_32(base); break; default: /* should be unreachable */ - *val = 0; + *val = 0U; ret = SMC_PCI_CALL_INVAL_PARAM; } } @@ -204,9 +206,12 @@ uint32_t pci_write_config(uint32_t addr, uint32_t off, uint32_t sz, uint32_t val uint32_t pci_get_bus_for_seg(uint32_t seg, uint32_t *bus_range, uint32_t *nseg) { uint32_t ret = SMC_PCI_CALL_SUCCESS; - *nseg = 0U; /* only a single segment */ - if (seg == 0U) { - *bus_range = 0xFF00; /* start 0, end 255 */ + uint32_t rc_count = ARRAY_SIZE(pcie_rc_bases); + + *nseg = (seg < rc_count - 1U) ? seg + 1U : 0U; + + if (seg < rc_count) { + *bus_range = 0U + (0xFF << 8); /* start 0, end 255 */ } else { *bus_range = 0U; ret = SMC_PCI_CALL_NOT_IMPL; diff --git a/plat/rpi/rpi4/include/rpi_hw.h b/plat/rpi/rpi4/include/rpi_hw.h index 816249212..53ce0f8b1 100644 --- a/plat/rpi/rpi4/include/rpi_hw.h +++ b/plat/rpi/rpi4/include/rpi_hw.h @@ -69,4 +69,11 @@ #define RPI4_LOCAL_CONTROL_BASE_ADDRESS ULL(0xff800000) #define RPI4_LOCAL_CONTROL_PRESCALER ULL(0xff800008) +/* + * PCI Express + */ +#define RPI_PCIE_RC_BASES (RPI_IO_BASE + ULL(0x01500000)) + +#define RPI_PCIE_ECAM_SERROR_QUIRK 1 + #endif /* RPI_HW_H */ diff --git a/plat/rpi/rpi4/platform.mk b/plat/rpi/rpi4/platform.mk index f17911fec..cbfa6f2fd 100644 --- a/plat/rpi/rpi4/platform.mk +++ b/plat/rpi/rpi4/platform.mk @@ -113,5 +113,5 @@ PLAT_BL_COMMON_SOURCES += drivers/rpi3/rng/rpi3_rng.c \ endif ifeq ($(SMC_PCI_SUPPORT), 1) -BL31_SOURCES += plat/rpi/rpi4/rpi4_pci_svc.c +BL31_SOURCES += plat/rpi/common/rpi_pci_svc.c endif diff --git a/plat/rpi/rpi5/include/rpi_hw.h b/plat/rpi/rpi5/include/rpi_hw.h index 384542e66..a7376765e 100644 --- a/plat/rpi/rpi5/include/rpi_hw.h +++ b/plat/rpi/rpi5/include/rpi_hw.h @@ -48,4 +48,11 @@ #define RPI4_LOCAL_CONTROL_BASE_ADDRESS (RPI_IO_BASE + ULL(0x7c280000)) #define RPI4_LOCAL_CONTROL_PRESCALER (RPI_IO_BASE + ULL(0x7c280008)) +/* + * PCI Express + */ +#define RPI_PCIE_RC_BASES RPI_IO_BASE + ULL(0x00100000), \ + RPI_IO_BASE + ULL(0x00110000), \ + RPI_IO_BASE + ULL(0x00120000) + #endif /* RPI_HW_H */ diff --git a/plat/rpi/rpi5/platform.mk b/plat/rpi/rpi5/platform.mk index 81b7dedfb..70c5add25 100644 --- a/plat/rpi/rpi5/platform.mk +++ b/plat/rpi/rpi5/platform.mk @@ -86,6 +86,9 @@ RPI3_RUNTIME_UART := 0 # Use normal memory mapping for ROM, FIP, SRAM and DRAM RPI3_USE_UEFI_MAP := 0 +# SMCCC PCI support (should be enabled for ACPI builds) +SMC_PCI_SUPPORT := 0 + # Process platform flags # ---------------------- @@ -96,6 +99,7 @@ $(eval $(call add_define,RPI3_PRELOADED_DTB_BASE)) endif $(eval $(call add_define,RPI3_RUNTIME_UART)) $(eval $(call add_define,RPI3_USE_UEFI_MAP)) +$(eval $(call add_define,SMC_PCI_SUPPORT)) ifeq (${ARCH},aarch32) $(error Error: AArch32 not supported on rpi5) @@ -105,3 +109,7 @@ ifneq ($(ENABLE_STACK_PROTECTOR), 0) PLAT_BL_COMMON_SOURCES += drivers/rpi3/rng/rpi3_rng.c \ plat/rpi/common/rpi3_stack_protector.c endif + +ifeq ($(SMC_PCI_SUPPORT), 1) +BL31_SOURCES += plat/rpi/common/rpi_pci_svc.c +endif