diff options
Diffstat (limited to 'plat/marvell/armada/a3k/common')
19 files changed, 2549 insertions, 0 deletions
diff --git a/plat/marvell/armada/a3k/common/a3700_common.mk b/plat/marvell/armada/a3k/common/a3700_common.mk new file mode 100644 index 0000000..b9c28de --- /dev/null +++ b/plat/marvell/armada/a3k/common/a3700_common.mk @@ -0,0 +1,247 @@ +# +# Copyright (C) 2018-2021 Marvell International Ltd. +# +# SPDX-License-Identifier: BSD-3-Clause +# https://spdx.org/licenses +# + +MARVELL_PLAT_BASE := plat/marvell/armada +MARVELL_PLAT_INCLUDE_BASE := include/plat/marvell/armada +PLAT_FAMILY := a3k +PLAT_FAMILY_BASE := $(MARVELL_PLAT_BASE)/$(PLAT_FAMILY) +PLAT_INCLUDE_BASE := $(MARVELL_PLAT_INCLUDE_BASE)/$(PLAT_FAMILY) +PLAT_COMMON_BASE := $(PLAT_FAMILY_BASE)/common +MARVELL_DRV_BASE := drivers/marvell +MARVELL_COMMON_BASE := $(MARVELL_PLAT_BASE)/common +ERRATA_A53_1530924 := 1 + +include plat/marvell/marvell.mk + +#*********** A3700 ************* + +# GICV3 +$(eval $(call add_define,CONFIG_GICV3)) + +# CCI-400 +$(eval $(call add_define,USE_CCI)) + +# Include GICv3 driver files +include drivers/arm/gic/v3/gicv3.mk + +MARVELL_GIC_SOURCES := ${GICV3_SOURCES} \ + plat/common/plat_gicv3.c + +PLAT_INCLUDES := -I$(PLAT_FAMILY_BASE)/$(PLAT) \ + -I$(PLAT_COMMON_BASE)/include \ + -I$(PLAT_INCLUDE_BASE)/common \ + -I$(MARVELL_DRV_BASE) \ + -I$/drivers/arm/gic/common/ + +PLAT_BL_COMMON_SOURCES := $(PLAT_COMMON_BASE)/aarch64/a3700_common.c \ + $(PLAT_COMMON_BASE)/aarch64/a3700_clock.S \ + $(MARVELL_DRV_BASE)/uart/a3700_console.S + +BL1_SOURCES += $(PLAT_COMMON_BASE)/aarch64/plat_helpers.S \ + lib/cpus/aarch64/cortex_a53.S + +MARVELL_DRV := $(MARVELL_DRV_BASE)/comphy/phy-comphy-3700.c + +BL31_SOURCES += lib/cpus/aarch64/cortex_a53.S \ + $(PLAT_COMMON_BASE)/aarch64/plat_helpers.S \ + $(PLAT_COMMON_BASE)/plat_cci.c \ + $(PLAT_COMMON_BASE)/plat_pm.c \ + $(PLAT_COMMON_BASE)/dram_win.c \ + $(PLAT_COMMON_BASE)/io_addr_dec.c \ + $(PLAT_COMMON_BASE)/marvell_plat_config.c \ + $(PLAT_FAMILY_BASE)/$(PLAT)/plat_bl31_setup.c \ + $(MARVELL_COMMON_BASE)/marvell_cci.c \ + $(MARVELL_COMMON_BASE)/marvell_ddr_info.c \ + $(MARVELL_COMMON_BASE)/marvell_gicv3.c \ + $(MARVELL_GIC_SOURCES) \ + drivers/arm/cci/cci.c \ + $(PLAT_COMMON_BASE)/a3700_sip_svc.c \ + $(MARVELL_DRV) + +ifeq ($(HANDLE_EA_EL3_FIRST_NS),1) +BL31_SOURCES += $(PLAT_COMMON_BASE)/a3700_ea.c +endif + +ifeq ($(CM3_SYSTEM_RESET),1) +BL31_SOURCES += $(PLAT_COMMON_BASE)/cm3_system_reset.c +endif + +ifeq ($(A3720_DB_PM_WAKEUP_SRC),1) +BL31_SOURCES += $(PLAT_FAMILY_BASE)/$(PLAT)/board/pm_src.c +endif + +ifdef WTP + +# Do not remove! Following checks are required to ensure correct TF-A builds, removing these checks leads to broken TF-A builds +$(if $(wildcard $(value WTP)/*),,$(error "'WTP=$(value WTP)' was specified, but '$(value WTP)' directory does not exist")) +$(if $(shell git -C $(value WTP) rev-parse --show-cdup 2>&1),$(error "'WTP=$(value WTP)' was specified, but '$(value WTP)' does not contain valid A3700-utils-marvell git repository")) + +TBB := $(WTP)/wtptp/src/TBB_Linux/release/TBB_linux + +BUILD_UART := uart-images +UART_IMAGE := $(BUILD_UART).tgz.bin + +ifeq ($(MARVELL_SECURE_BOOT),1) +TIM_CFG := $(BUILD_PLAT)/atf-tim.txt +TIM_UART_CFG := $(BUILD_PLAT)/$(BUILD_UART)/atf-tim.txt +IMAGESPATH := $(WTP)/tim/trusted +TIMN_CFG := $(BUILD_PLAT)/atf-timN.txt +TIMN_UART_CFG := $(BUILD_PLAT)/$(BUILD_UART)/atf-timN.txt +TIMN_SIG := $(IMAGESPATH)/timnsign.txt +TIM2IMGARGS := -i $(TIM_CFG) -n $(TIMN_CFG) +TIMN_UART_IMAGE := $$(grep "Image Filename:" -m 1 $(TIMN_UART_CFG) | cut -c 17-) +else #MARVELL_SECURE_BOOT +TIM_CFG := $(BUILD_PLAT)/atf-ntim.txt +TIM_UART_CFG := $(BUILD_PLAT)/$(BUILD_UART)/atf-ntim.txt +IMAGESPATH := $(WTP)/tim/untrusted +TIM2IMGARGS := -i $(TIM_CFG) +endif #MARVELL_SECURE_BOOT + +TIM_UART_IMAGE := $$(grep "Image Filename:" -m 1 $(TIM_UART_CFG) | cut -c 17-) + +TIMBUILD := $(WTP)/script/buildtim.sh +TIM2IMG := $(WTP)/script/tim2img.pl +TIMDDRTOOL := $(WTP)/tim/ddr/ddr_tool + +$(TIMBUILD): $(TIMDDRTOOL) + +# WTMI_IMG is used to specify the customized RTOS image running over +# Service CPU (CM3 processor). By the default, it points to a +# baremetal binary of fuse programming in A3700_utils. +WTMI_IMG := $(WTP)/wtmi/fuse/build/fuse.bin + +# WTMI_MULTI_IMG is composed of CM3 RTOS image (WTMI_IMG) +# and sys-init image. +WTMI_MULTI_IMG := $(WTP)/wtmi/build/wtmi.bin + +WTMI_ENC_IMG := wtmi-enc.bin + +SRCPATH := $(dir $(BL33)) + +CLOCKSPRESET ?= CPU_800_DDR_800 + +DDR_TOPOLOGY ?= 0 + +BOOTDEV ?= SPINOR +PARTNUM ?= 0 + +TIMBLDARGS := $(MARVELL_SECURE_BOOT) $(BOOTDEV) $(IMAGESPATH) $(WTP) $(CLOCKSPRESET) \ + $(DDR_TOPOLOGY) $(PARTNUM) $(DEBUG) $(TIM_CFG) $(TIMN_CFG) $(TIMN_SIG) 1 +TIMBLDUARTARGS := $(MARVELL_SECURE_BOOT) UART $(IMAGESPATH) $(WTP) $(CLOCKSPRESET) \ + $(DDR_TOPOLOGY) 0 0 $(TIM_UART_CFG) $(TIMN_UART_CFG) $(TIMN_SIG) 0 + +UART_IMAGES := $(BUILD_UART)/$(TIM_UART_IMAGE) +ifeq ($(MARVELL_SECURE_BOOT),1) +UART_IMAGES += $(BUILD_UART)/$(TIMN_UART_IMAGE) +endif +UART_IMAGES += $(BUILD_UART)/wtmi_h.bin $(BUILD_UART)/boot-image_h.bin + +CRYPTOPP_LIBDIR ?= $(CRYPTOPP_PATH) +CRYPTOPP_INCDIR ?= $(CRYPTOPP_PATH) + +$(TBB): FORCE +# Do not remove! Following checks are required to ensure correct TF-A builds, removing these checks leads to broken TF-A builds + $(if $(CRYPTOPP_LIBDIR),,$(error "Platform '$(PLAT)' for WTP image tool requires CRYPTOPP_PATH or CRYPTOPP_LIBDIR. Please set CRYPTOPP_PATH or CRYPTOPP_LIBDIR to point to the right directory")) + $(if $(CRYPTOPP_INCDIR),,$(error "Platform '$(PLAT)' for WTP image tool requires CRYPTOPP_PATH or CRYPTOPP_INCDIR. Please set CRYPTOPP_PATH or CRYPTOPP_INCDIR to point to the right directory")) + $(if $(wildcard $(CRYPTOPP_LIBDIR)/*),,$(error "Either 'CRYPTOPP_PATH' or 'CRYPTOPP_LIB' was set to '$(CRYPTOPP_LIBDIR)', but '$(CRYPTOPP_LIBDIR)' does not exist")) + $(if $(wildcard $(CRYPTOPP_INCDIR)/*),,$(error "Either 'CRYPTOPP_PATH' or 'CRYPTOPP_INCDIR' was set to '$(CRYPTOPP_INCDIR)', but '$(CRYPTOPP_INCDIR)' does not exist")) +ifdef CRYPTOPP_PATH + $(Q)$(MAKE) --no-print-directory -C $(CRYPTOPP_PATH) -f GNUmakefile +endif + $(Q)$(MAKE) --no-print-directory -C $(WTP)/wtptp/src/TBB_Linux -f TBB_linux.mak LIBDIR=$(CRYPTOPP_LIBDIR) INCDIR=$(CRYPTOPP_INCDIR) + +$(WTMI_MULTI_IMG): FORCE + $(Q)$(MAKE) --no-print-directory -C $(WTP) WTMI_IMG=$(WTMI_IMG) DDR_TOPOLOGY=$(DDR_TOPOLOGY) CLOCKSPRESET=$(CLOCKSPRESET) WTMI + +$(BUILD_PLAT)/wtmi.bin: $(WTMI_MULTI_IMG) + $(Q)cp -a $(WTMI_MULTI_IMG) $(BUILD_PLAT)/wtmi.bin + +$(TIMDDRTOOL): FORCE +# Do not remove! Following checks are required to ensure correct TF-A builds, removing these checks leads to broken TF-A builds + $(if $(value MV_DDR_PATH),,$(error "Platform '${PLAT}' for ddr tool requires MV_DDR_PATH. Please set MV_DDR_PATH to point to the right directory")) + $(if $(wildcard $(value MV_DDR_PATH)/*),,$(error "'MV_DDR_PATH=$(value MV_DDR_PATH)' was specified, but '$(value MV_DDR_PATH)' directory does not exist")) + $(if $(shell git -C $(value MV_DDR_PATH) rev-parse --show-cdup 2>&1),$(error "'MV_DDR_PATH=$(value MV_DDR_PATH)' was specified, but '$(value MV_DDR_PATH)' does not contain valid mv-ddr-marvell git repository")) + $(Q)$(MAKE) --no-print-directory -C $(WTP) MV_DDR_PATH=$(MV_DDR_PATH) DDR_TOPOLOGY=$(DDR_TOPOLOGY) mv_ddr + +$(BUILD_PLAT)/$(UART_IMAGE): $(BUILD_PLAT)/$(BOOT_IMAGE) $(BUILD_PLAT)/wtmi.bin $(TBB) $(TIMBUILD) $(TIMDDRTOOL) + @$(ECHO_BLANK_LINE) + @echo "Building uart images" + $(Q)mkdir -p $(BUILD_PLAT)/$(BUILD_UART) + $(Q)cp -a $(BUILD_PLAT)/wtmi.bin $(BUILD_PLAT)/$(BUILD_UART)/wtmi.bin + $(Q)cp -a $(BUILD_PLAT)/$(BOOT_IMAGE) $(BUILD_PLAT)/$(BUILD_UART)/$(BOOT_IMAGE) + $(Q)cd $(BUILD_PLAT)/$(BUILD_UART) && $(TIMBUILD) $(TIMBLDUARTARGS) + $(Q)sed -i 's|WTMI_IMG|wtmi.bin|1' $(TIM_UART_CFG) + $(Q)sed -i 's|BOOT_IMAGE|$(BOOT_IMAGE)|1' $(TIM_UART_CFG) +ifeq ($(MARVELL_SECURE_BOOT),1) + $(Q)sed -i 's|WTMI_IMG|wtmi.bin|1' $(TIMN_UART_CFG) + $(Q)sed -i 's|BOOT_IMAGE|$(BOOT_IMAGE)|1' $(TIMN_UART_CFG) +endif + $(Q)cd $(BUILD_PLAT)/$(BUILD_UART) && $(TBB) -r $(TIM_UART_CFG) -v -D +ifeq ($(MARVELL_SECURE_BOOT),1) + $(Q)cd $(BUILD_PLAT)/$(BUILD_UART) && $(TBB) -r $(TIMN_UART_CFG) +endif + $(Q)tar czf $(BUILD_PLAT)/$(UART_IMAGE) -C $(BUILD_PLAT) $(UART_IMAGES) + @$(ECHO_BLANK_LINE) + @echo "Built $@ successfully" + @$(ECHO_BLANK_LINE) + +$(BUILD_PLAT)/$(FLASH_IMAGE): $(BUILD_PLAT)/$(BOOT_IMAGE) $(BUILD_PLAT)/wtmi.bin $(TBB) $(TIMBUILD) $(TIMDDRTOOL) $(TIM2IMG) + @$(ECHO_BLANK_LINE) + @echo "Building flash image" + $(Q)cd $(BUILD_PLAT) && $(TIMBUILD) $(TIMBLDARGS) + $(Q)sed -i 's|WTMI_IMG|wtmi.bin|1' $(TIM_CFG) + $(Q)sed -i 's|BOOT_IMAGE|$(BOOT_IMAGE)|1' $(TIM_CFG) +ifeq ($(MARVELL_SECURE_BOOT),1) + $(Q)sed -i 's|WTMI_IMG|wtmi.bin|1' $(TIMN_CFG) + $(Q)sed -i 's|BOOT_IMAGE|$(BOOT_IMAGE)|1' $(TIMN_CFG) + @$(ECHO_BLANK_LINE) + @echo "======================================================="; + @echo " Secure boot. Encrypting wtmi and boot-image"; + @echo "======================================================="; + @$(ECHO_BLANK_LINE) + $(Q)cp $(BUILD_PLAT)/wtmi.bin $(BUILD_PLAT)/wtmi-align.bin + $(Q)truncate -s %16 $(BUILD_PLAT)/wtmi-align.bin + $(Q)${OPENSSL_BIN_PATH}/openssl enc -aes-256-cbc -e -in $(BUILD_PLAT)/wtmi-align.bin \ + -out $(BUILD_PLAT)/$(WTMI_ENC_IMG) \ + -K `cat $(IMAGESPATH)/aes-256.txt` -nosalt \ + -iv `cat $(IMAGESPATH)/iv.txt` -p + $(Q)truncate -s %16 $(BUILD_PLAT)/$(BOOT_IMAGE); + $(Q)${OPENSSL_BIN_PATH}/openssl enc -aes-256-cbc -e -in $(BUILD_PLAT)/$(BOOT_IMAGE) \ + -out $(BUILD_PLAT)/$(BOOT_ENC_IMAGE) \ + -K `cat $(IMAGESPATH)/aes-256.txt` -nosalt \ + -iv `cat $(IMAGESPATH)/iv.txt` -p +endif + $(Q)cd $(BUILD_PLAT) && $(TBB) -r $(TIM_CFG) -v -D +ifeq ($(MARVELL_SECURE_BOOT),1) + $(Q)cd $(BUILD_PLAT) && $(TBB) -r $(TIMN_CFG) + $(Q)sed -i 's|wtmi.bin|$(WTMI_ENC_IMG)|1' $(TIMN_CFG) + $(Q)sed -i 's|$(BOOT_IMAGE)|$(BOOT_ENC_IMAGE)|1' $(TIMN_CFG) +endif + $(Q)cd $(BUILD_PLAT) && $(TIM2IMG) $(TIM2IMGARGS) -o $(BUILD_PLAT)/$(FLASH_IMAGE) + @$(ECHO_BLANK_LINE) + @echo "Built $@ successfully" + @$(ECHO_BLANK_LINE) + +clean realclean distclean: mrvl_clean + +.PHONY: mrvl_clean +mrvl_clean: + -$(Q)$(MAKE) --no-print-directory -C $(WTP) MV_DDR_PATH=$(MV_DDR_PATH) clean + -$(Q)$(MAKE) --no-print-directory -C $(WTP)/wtptp/src/TBB_Linux -f TBB_linux.mak clean +ifdef CRYPTOPP_PATH + -$(Q)$(MAKE) --no-print-directory -C $(CRYPTOPP_PATH) -f GNUmakefile clean +endif + +else # WTP + +$(BUILD_PLAT)/$(UART_IMAGE) $(BUILD_PLAT)/$(FLASH_IMAGE): + $(error "Platform '${PLAT}' for target '$@' requires WTP. Please set WTP to point to the right directory") + +endif # WTP + +.PHONY: mrvl_uart +mrvl_uart: $(BUILD_PLAT)/$(UART_IMAGE) diff --git a/plat/marvell/armada/a3k/common/a3700_ea.c b/plat/marvell/armada/a3k/common/a3700_ea.c new file mode 100644 index 0000000..5696b5c --- /dev/null +++ b/plat/marvell/armada/a3k/common/a3700_ea.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2019 Repk repk@triplefau.lt + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <inttypes.h> +#include <stdint.h> + +#include <common/bl_common.h> +#include <common/debug.h> +#include <arch_helpers.h> +#include <plat/common/platform.h> +#include <bl31/ea_handle.h> + +#define A53_SERR_INT_AXI_SLVERR_ON_EXTERNAL_ACCESS 0xbf000002 + +/* + * This source file with custom plat_ea_handler function is compiled only when + * building TF-A with compile option HANDLE_EA_EL3_FIRST_NS=1 + */ +void plat_ea_handler(unsigned int ea_reason, uint64_t syndrome, void *cookie, + void *handle, uint64_t flags) +{ + unsigned int level = (unsigned int)GET_EL(read_spsr_el3()); + + /* + * Asynchronous External Abort with syndrome 0xbf000002 on Cortex A53 + * core means SError interrupt caused by AXI SLVERR on external access. + * + * In most cases this indicates a bug in U-Boot or Linux kernel driver + * pci-aardvark.c which implements access to A3700 PCIe config space. + * Driver does not wait for PCIe PIO transfer completion and try to + * start a new PCIe PIO transfer while previous has not finished yet. + * A3700 PCIe controller in this case sends SLVERR via AXI which results + * in a fatal Asynchronous SError interrupt on Cortex A53 CPU. + * + * Following patches fix that bug in U-Boot and Linux kernel drivers: + * https://source.denx.de/u-boot/u-boot/-/commit/eccbd4ad8e4e182638eafbfb87ac139c04f24a01 + * https://git.kernel.org/stable/c/f18139966d072dab8e4398c95ce955a9742e04f7 + * + * As a hacky workaround for unpatched U-Boot and Linux kernel drivers + * ignore all asynchronous aborts with that syndrome value received on + * CPU from level lower than EL3. + * + * Because these aborts are delivered on CPU asynchronously, they are + * imprecise and we cannot check the real reason of abort and neither + * who and why sent this abort. We expect that on A3700 it is always + * PCIe controller. + * + * Hence ignoring all aborts with this syndrome value is just a giant + * hack that we need only because of bugs in old U-Boot and Linux kernel + * versions and because it was decided that TF-A would implement this + * hack for U-Boot and Linux kernel it in this way. New patched U-Boot + * and kernel versions do not need it anymore. + * + * Links to discussion about this workaround: + * https://lore.kernel.org/linux-pci/20190316161243.29517-1-repk@triplefau.lt/ + * https://lore.kernel.org/linux-pci/971be151d24312cc533989a64bd454b4@www.loen.fr/ + * https://review.trustedfirmware.org/c/TF-A/trusted-firmware-a/+/1541 + */ + if (level < MODE_EL3 && ea_reason == ERROR_EA_ASYNC && + syndrome == A53_SERR_INT_AXI_SLVERR_ON_EXTERNAL_ACCESS) { + ERROR_NL(); + ERROR("Ignoring Asynchronous External Abort with" + " syndrome 0x%" PRIx64 " received on 0x%lx from %s\n", + syndrome, read_mpidr_el1(), get_el_str(level)); + ERROR("SError interrupt: AXI SLVERR on external access\n"); + ERROR("This indicates a bug in pci-aardvark.c driver\n"); + ERROR("Please update U-Boot/Linux to the latest version\n"); + ERROR_NL(); + console_flush(); + return; + } + + plat_default_ea_handler(ea_reason, syndrome, cookie, handle, flags); +} diff --git a/plat/marvell/armada/a3k/common/a3700_sip_svc.c b/plat/marvell/armada/a3k/common/a3700_sip_svc.c new file mode 100644 index 0000000..e8ac5fc --- /dev/null +++ b/plat/marvell/armada/a3k/common/a3700_sip_svc.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <lib/smccc.h> + +#include <marvell_plat_priv.h> +#include <plat_marvell.h> + +#include "comphy/phy-comphy-3700.h" + +/* Comphy related FID's */ +#define MV_SIP_COMPHY_POWER_ON 0x82000001 +#define MV_SIP_COMPHY_POWER_OFF 0x82000002 +#define MV_SIP_COMPHY_PLL_LOCK 0x82000003 + +/* Miscellaneous FID's' */ +#define MV_SIP_DRAM_SIZE 0x82000010 + +/* This macro is used to identify COMPHY related calls from SMC function ID */ +#define is_comphy_fid(fid) \ + ((fid) >= MV_SIP_COMPHY_POWER_ON && (fid) <= MV_SIP_COMPHY_PLL_LOCK) + +uintptr_t mrvl_sip_smc_handler(uint32_t smc_fid, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + u_register_t ret; + + VERBOSE("%s: got SMC (0x%x) x1 0x%lx, x2 0x%lx\n", + __func__, smc_fid, x1, x2); + if (is_comphy_fid(smc_fid)) { + if (x1 >= MAX_LANE_NR) { + ERROR("%s: Wrong smc (0x%x) lane nr: %lx\n", + __func__, smc_fid, x2); + SMC_RET1(handle, SMC_UNK); + } + } + + switch (smc_fid) { + /* Comphy related FID's */ + case MV_SIP_COMPHY_POWER_ON: + /* x1: comphy_index, x2: comphy_mode */ + ret = mvebu_3700_comphy_power_on(x1, x2); + SMC_RET1(handle, ret); + case MV_SIP_COMPHY_POWER_OFF: + /* x1: comphy_index, x2: comphy_mode */ + ret = mvebu_3700_comphy_power_off(x1, x2); + SMC_RET1(handle, ret); + case MV_SIP_COMPHY_PLL_LOCK: + /* x1: comphy_index, x2: comphy_mode */ + ret = mvebu_3700_comphy_is_pll_locked(x1, x2); + SMC_RET1(handle, ret); + /* Miscellaneous FID's' */ + case MV_SIP_DRAM_SIZE: + /* x1: ap_base_addr */ + ret = mvebu_get_dram_size(MVEBU_REGS_BASE); + SMC_RET1(handle, ret); + + default: + ERROR("%s: unhandled SMC (0x%x)\n", __func__, smc_fid); + SMC_RET1(handle, SMC_UNK); + } +} + +/* Define a runtime service descriptor for fast SMC calls */ +DECLARE_RT_SVC( + marvell_sip_svc, + OEN_SIP_START, + OEN_SIP_END, + SMC_TYPE_FAST, + NULL, + mrvl_sip_smc_handler +); diff --git a/plat/marvell/armada/a3k/common/aarch64/a3700_clock.S b/plat/marvell/armada/a3k/common/aarch64/a3700_clock.S new file mode 100644 index 0000000..f79516f --- /dev/null +++ b/plat/marvell/armada/a3k/common/aarch64/a3700_clock.S @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <asm_macros.S> +#include <platform_def.h> + +/* + * Below address in used only for reading, therefore no problem with concurrent + * Linux access. + */ +#define MVEBU_TEST_PIN_LATCH_N (MVEBU_NB_GPIO_REG_BASE + 0x8) + #define MVEBU_XTAL_MODE_MASK BIT(9) + + /* ----------------------------------------------------- + * uint32_t get_ref_clk (void); + * + * returns reference clock in MHz (25 or 40) + * ----------------------------------------------------- + */ +.globl get_ref_clk +func get_ref_clk + mov_imm x0, MVEBU_TEST_PIN_LATCH_N + ldr w0, [x0] + tst w0, #MVEBU_XTAL_MODE_MASK + bne 40 + mov w0, #25 + ret +40: + mov w0, #40 + ret +endfunc get_ref_clk diff --git a/plat/marvell/armada/a3k/common/aarch64/a3700_common.c b/plat/marvell/armada/a3k/common/aarch64/a3700_common.c new file mode 100644 index 0000000..6351285 --- /dev/null +++ b/plat/marvell/armada/a3k/common/aarch64/a3700_common.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ +#include <plat_marvell.h> + +/* MMU entry for internal (register) space access */ +#define MAP_DEVICE0 MAP_REGION_FLAT(DEVICE0_BASE, \ + DEVICE0_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE) + +/* + * Table of regions for various BL stages to map using the MMU. + */ +#if IMAGE_BL1 +const mmap_region_t plat_marvell_mmap[] = { + MARVELL_MAP_SHARED_RAM, + MAP_DEVICE0, + {0} +}; +#endif +#if IMAGE_BL2 +const mmap_region_t plat_marvell_mmap[] = { + MARVELL_MAP_SHARED_RAM, + MAP_DEVICE0, + MARVELL_MAP_DRAM, + {0} +}; +#endif +#if IMAGE_BL2U +const mmap_region_t plat_marvell_mmap[] = { + MAP_DEVICE0, + {0} +}; +#endif +#if IMAGE_BL31 +const mmap_region_t plat_marvell_mmap[] = { + MARVELL_MAP_SHARED_RAM, + MAP_DEVICE0, + MARVELL_MAP_DRAM, + {0} +}; +#endif +#if IMAGE_BL32 +const mmap_region_t plat_marvell_mmap[] = { + MAP_DEVICE0, + {0} +}; +#endif + +MARVELL_CASSERT_MMAP; diff --git a/plat/marvell/armada/a3k/common/aarch64/plat_helpers.S b/plat/marvell/armada/a3k/common/aarch64/plat_helpers.S new file mode 100644 index 0000000..90d76f0 --- /dev/null +++ b/plat/marvell/armada/a3k/common/aarch64/plat_helpers.S @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <asm_macros.S> +#include <platform_def.h> + + .globl plat_secondary_cold_boot_setup + .globl plat_get_my_entrypoint + .globl plat_is_my_cpu_primary + + /* ----------------------------------------------------- + * void plat_secondary_cold_boot_setup (void); + * + * This function performs any platform specific actions + * needed for a secondary cpu after a cold reset. Right + * now this is a stub function. + * ----------------------------------------------------- + */ +func plat_secondary_cold_boot_setup + mov x0, #0 + ret +endfunc plat_secondary_cold_boot_setup + + /* --------------------------------------------------------------------- + * unsigned long plat_get_my_entrypoint (void); + * + * Main job of this routine is to distinguish between cold and warm boot + * For a cold boot, return 0. + * For a warm boot, read the mailbox and return the address it contains. + * A magic number is placed before entrypoint to avoid mistake caused by + * uninitialized mailbox data area. + * --------------------------------------------------------------------- + */ +func plat_get_my_entrypoint + /* Read first word and compare it with magic num */ + mov_imm x0, PLAT_MARVELL_MAILBOX_BASE + ldr x1, [x0] + mov_imm x2, PLAT_MARVELL_MAILBOX_MAGIC_NUM + cmp x1, x2 + /* If compare failed, return 0, i.e. cold boot */ + beq entrypoint + mov x0, #0 + ret +entrypoint: + /* Second word contains the jump address */ + add x0, x0, #8 + ldr x0, [x0] + ret +endfunc plat_get_my_entrypoint + + /* ----------------------------------------------------- + * unsigned int plat_is_my_cpu_primary (void); + * + * Find out whether the current cpu is the primary + * cpu. + * ----------------------------------------------------- + */ +func plat_is_my_cpu_primary + mrs x0, mpidr_el1 + and x0, x0, #(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK) + cmp x0, #MVEBU_PRIMARY_CPU + cset w0, eq + ret +endfunc plat_is_my_cpu_primary diff --git a/plat/marvell/armada/a3k/common/cm3_system_reset.c b/plat/marvell/armada/a3k/common/cm3_system_reset.c new file mode 100644 index 0000000..f105d59 --- /dev/null +++ b/plat/marvell/armada/a3k/common/cm3_system_reset.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2020 Marek Behun, CZ.NIC + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <stdbool.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <lib/mmio.h> + +#include <mvebu_def.h> + +/* Cortex-M3 Secure Processor Mailbox Registers */ +#define MVEBU_RWTM_PARAM0_REG (MVEBU_RWTM_REG_BASE) +#define MVEBU_RWTM_CMD_REG (MVEBU_RWTM_REG_BASE + 0x40) +#define MVEBU_RWTM_HOST_INT_RESET_REG (MVEBU_RWTM_REG_BASE + 0xC8) +#define MVEBU_RWTM_HOST_INT_MASK_REG (MVEBU_RWTM_REG_BASE + 0xCC) +#define MVEBU_RWTM_HOST_INT_SP_COMPLETE BIT(0) + +#define MVEBU_RWTM_REBOOT_CMD 0x0009 +#define MVEBU_RWTM_REBOOT_MAGIC 0xDEADBEEF + +static inline bool rwtm_completed(void) +{ + return (mmio_read_32(MVEBU_RWTM_HOST_INT_RESET_REG) & + MVEBU_RWTM_HOST_INT_SP_COMPLETE) != 0; +} + +static bool rwtm_wait(int ms) +{ + while (ms && !rwtm_completed()) { + mdelay(1); + --ms; + } + + return rwtm_completed(); +} + +void cm3_system_reset(void) +{ + int tries = 5; + + for (; tries > 0; --tries) { + mmio_clrbits_32(MVEBU_RWTM_HOST_INT_RESET_REG, + MVEBU_RWTM_HOST_INT_SP_COMPLETE); + + mmio_write_32(MVEBU_RWTM_PARAM0_REG, MVEBU_RWTM_REBOOT_MAGIC); + mmio_write_32(MVEBU_RWTM_CMD_REG, MVEBU_RWTM_REBOOT_CMD); + + if (rwtm_wait(10)) { + break; + } + + mdelay(100); + } + + /* If we reach here, the command is not implemented. */ + WARN("System reset command not implemented in WTMI firmware!\n"); +} diff --git a/plat/marvell/armada/a3k/common/dram_win.c b/plat/marvell/armada/a3k/common/dram_win.c new file mode 100644 index 0000000..9d7b3a9 --- /dev/null +++ b/plat/marvell/armada/a3k/common/dram_win.c @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2018-2021 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <string.h> + +#include <lib/mmio.h> + +#include <dram_win.h> +#include <marvell_plat_priv.h> +#include <mvebu.h> +#include <plat_marvell.h> + +/* Armada 3700 has 5 configurable windows */ +#define MV_CPU_WIN_NUM 5 + +#define CPU_WIN_DISABLED 0 +#define CPU_WIN_ENABLED 1 + +/* + * There are 2 different cpu decode window configuration cases: + * - DRAM size is not over 2GB; + * - DRAM size is 4GB. + */ +enum cpu_win_config_num { + CPU_WIN_CONFIG_DRAM_NOT_OVER_2GB = 0, + CPU_WIN_CONFIG_DRAM_4GB, + CPU_WIN_CONFIG_MAX +}; + +enum cpu_win_target { + CPU_WIN_TARGET_DRAM = 0, + CPU_WIN_TARGET_INTERNAL_REG, + CPU_WIN_TARGET_PCIE, + CPU_WIN_TARGET_PCIE_OVER_MCI, + CPU_WIN_TARGET_BOOT_ROM, + CPU_WIN_TARGET_MCI_EXTERNAL, + CPU_WIN_TARGET_RWTM_RAM = 7, + CPU_WIN_TARGET_CCI400_REG +}; + +struct cpu_win_configuration { + uint32_t enabled; + enum cpu_win_target target; + uint64_t base_addr; + uint64_t size; + uint64_t remap_addr; +}; + +struct cpu_win_configuration mv_cpu_wins[CPU_WIN_CONFIG_MAX][MV_CPU_WIN_NUM] = { + /* + * When total dram size is not over 2GB: + * DDR window 0 is configured in tim header, its size may be not 512MB, + * but the actual dram size, no need to configure it again; + * other cpu windows are kept as default. + */ + { + /* enabled + * target + * base + * size + * remap + */ + {CPU_WIN_ENABLED, + CPU_WIN_TARGET_DRAM, + 0x0, + 0x08000000, + 0x0}, + {CPU_WIN_ENABLED, + CPU_WIN_TARGET_MCI_EXTERNAL, + 0xe0000000, + 0x08000000, + 0xe0000000}, + {CPU_WIN_ENABLED, + CPU_WIN_TARGET_PCIE, + 0xe8000000, + 0x08000000, + 0xe8000000}, + {CPU_WIN_ENABLED, + CPU_WIN_TARGET_RWTM_RAM, + 0xf0000000, + 0x00020000, + 0x1fff0000}, + {CPU_WIN_ENABLED, + CPU_WIN_TARGET_PCIE_OVER_MCI, + 0x80000000, + 0x10000000, + 0x80000000}, + }, + + /* + * If total DRAM size is more than 2GB, now there is only one case: + * 4GB of DRAM; to better utilize address space (for maximization of + * DRAM usage), we will use the configuration of CPU windows below: + * - Internal Regs and Boot ROM windows are kept as default; + * - CCI-400 is moved from its default address to another address + * (this is actually done even if DRAM size is not more than 2 GB, + * because the firmware is compiled with that address as a + * constant); + * - PCIe window is moved to another address; + * - Use 4 CPU decode windows for DRAM, which cover 3.75GB DRAM; + * DDR window 0 is configured in tim header with 2G B size, no need + * to configure it again here; + * + * 0xFFFFFFFF ---> +-----------------------+ + * | Boot ROM | 1 MB + * | AP Boot ROM - 16 KB: | + * | 0xFFFF0000-0xFFFF4000 | + * 0xFFF00000 ---> +-----------------------+ + * : : + * 0xFE010000 ---> +-----------------------+ + * | CCI Regs | 64 KB + * 0xFE000000 ---> +-----------------------+ + * : : + * 0xFA000000 ---> +-----------------------+ + * | PCIE | 128 MB + * 0xF2000000 ---> +-----------------------+ + * | DDR window 3 | 512 MB + * 0xD2000000 ---> +-----------------------+ + * | Internal Regs | 32 MB + * 0xD0000000 ---> |-----------------------| + * | DDR window 2 | 256 MB + * 0xC0000000 ---> |-----------------------| + * | | + * | DDR window 1 | 1 GB + * | | + * 0x80000000 ---> |-----------------------| + * | | + * | | + * | DDR window 0 | 2 GB + * | | + * | | + * 0x00000000 ---> +-----------------------+ + */ + { + /* win_id + * target + * base + * size + * remap + */ + {CPU_WIN_ENABLED, + CPU_WIN_TARGET_DRAM, + 0x0, + 0x80000000, + 0x0}, + {CPU_WIN_ENABLED, + CPU_WIN_TARGET_DRAM, + 0x80000000, + 0x40000000, + 0x80000000}, + {CPU_WIN_ENABLED, + CPU_WIN_TARGET_DRAM, + 0xc0000000, + 0x10000000, + 0xc0000000}, + {CPU_WIN_ENABLED, + CPU_WIN_TARGET_DRAM, + 0xd2000000, + 0x20000000, + 0xd2000000}, + {CPU_WIN_ENABLED, + CPU_WIN_TARGET_PCIE, + 0xf2000000, + 0x08000000, + 0xf2000000}, + }, +}; + +/* + * dram_win_map_build + * + * This function builds cpu dram windows mapping + * which includes base address and window size by + * reading cpu dram decode windows registers. + * + * @input: N/A + * + * @output: + * - win_map: cpu dram windows mapping + * + * @return: N/A + */ +void dram_win_map_build(struct dram_win_map *win_map) +{ + int32_t win_id; + struct dram_win *win; + uint32_t base_reg, ctrl_reg, size_reg, enabled, target; + + memset(win_map, 0, sizeof(struct dram_win_map)); + for (win_id = 0; win_id < DRAM_WIN_MAP_NUM_MAX; win_id++) { + ctrl_reg = mmio_read_32(CPU_DEC_WIN_CTRL_REG(win_id)); + target = (ctrl_reg & CPU_DEC_CR_WIN_TARGET_MASK) >> + CPU_DEC_CR_WIN_TARGET_OFFS; + enabled = ctrl_reg & CPU_DEC_CR_WIN_ENABLE; + /* Ignore invalid and non-dram windows*/ + if ((enabled == 0) || (target != DRAM_CPU_DEC_TARGET_NUM)) + continue; + + win = win_map->dram_windows + win_map->dram_win_num; + base_reg = mmio_read_32(CPU_DEC_WIN_BASE_REG(win_id)); + size_reg = mmio_read_32(CPU_DEC_WIN_SIZE_REG(win_id)); + /* Base reg [15:0] corresponds to transaction address [39:16] */ + win->base_addr = (base_reg & CPU_DEC_BR_BASE_MASK) >> + CPU_DEC_BR_BASE_OFFS; + win->base_addr *= CPU_DEC_CR_WIN_SIZE_ALIGNMENT; + /* + * Size reg [15:0] is programmed from LSB to MSB as a sequence + * of 1s followed by a sequence of 0s and the number of 1s + * specifies the size of the window in 64 KB granularity, + * for example, a value of 00FFh specifies 256 x 64 KB = 16 MB + */ + win->win_size = (size_reg & CPU_DEC_CR_WIN_SIZE_MASK) >> + CPU_DEC_CR_WIN_SIZE_OFFS; + win->win_size = (win->win_size + 1) * + CPU_DEC_CR_WIN_SIZE_ALIGNMENT; + + win_map->dram_win_num++; + } +} + +static void cpu_win_set(uint32_t win_id, struct cpu_win_configuration *win_cfg) +{ + uint32_t base_reg, ctrl_reg, size_reg, remap_reg; + + /* Disable window */ + ctrl_reg = mmio_read_32(CPU_DEC_WIN_CTRL_REG(win_id)); + ctrl_reg &= ~CPU_DEC_CR_WIN_ENABLE; + mmio_write_32(CPU_DEC_WIN_CTRL_REG(win_id), ctrl_reg); + + /* For an disabled window, only disable it. */ + if (!win_cfg->enabled) + return; + + /* Set Base Register */ + base_reg = (uint32_t)(win_cfg->base_addr / + CPU_DEC_CR_WIN_SIZE_ALIGNMENT); + base_reg <<= CPU_DEC_BR_BASE_OFFS; + base_reg &= CPU_DEC_BR_BASE_MASK; + mmio_write_32(CPU_DEC_WIN_BASE_REG(win_id), base_reg); + + /* Set Remap Register with the same value + * as the <Base> field in Base Register + */ + remap_reg = (uint32_t)(win_cfg->remap_addr / + CPU_DEC_CR_WIN_SIZE_ALIGNMENT); + remap_reg <<= CPU_DEC_RLR_REMAP_LOW_OFFS; + remap_reg &= CPU_DEC_RLR_REMAP_LOW_MASK; + mmio_write_32(CPU_DEC_REMAP_LOW_REG(win_id), remap_reg); + + /* Set Size Register */ + size_reg = (win_cfg->size / CPU_DEC_CR_WIN_SIZE_ALIGNMENT) - 1; + size_reg <<= CPU_DEC_CR_WIN_SIZE_OFFS; + size_reg &= CPU_DEC_CR_WIN_SIZE_MASK; + mmio_write_32(CPU_DEC_WIN_SIZE_REG(win_id), size_reg); + + /* Set Control Register - set target id and enable window */ + ctrl_reg &= ~CPU_DEC_CR_WIN_TARGET_MASK; + ctrl_reg |= (win_cfg->target << CPU_DEC_CR_WIN_TARGET_OFFS); + ctrl_reg |= CPU_DEC_CR_WIN_ENABLE; + mmio_write_32(CPU_DEC_WIN_CTRL_REG(win_id), ctrl_reg); +} + +void cpu_wins_init(void) +{ + uint32_t cfg_idx, win_id; + + if (mvebu_get_dram_size(MVEBU_REGS_BASE) <= _2GB_) + cfg_idx = CPU_WIN_CONFIG_DRAM_NOT_OVER_2GB; + else + cfg_idx = CPU_WIN_CONFIG_DRAM_4GB; + + /* Window 0 is configured always for DRAM in tim header + * already, no need to configure it again here + */ + for (win_id = 1; win_id < MV_CPU_WIN_NUM; win_id++) + cpu_win_set(win_id, &mv_cpu_wins[cfg_idx][win_id]); +} + diff --git a/plat/marvell/armada/a3k/common/include/a3700_plat_def.h b/plat/marvell/armada/a3k/common/include/a3700_plat_def.h new file mode 100644 index 0000000..4d45e15 --- /dev/null +++ b/plat/marvell/armada/a3k/common/include/a3700_plat_def.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2018-2021 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#ifndef A3700_PLAT_DEF_H +#define A3700_PLAT_DEF_H + +#include <marvell_def.h> + + +#define MVEBU_MAX_CPUS_PER_CLUSTER 2 + +#define MVEBU_PRIMARY_CPU 0x0 + +/* + * The counter on A3700 is always fed from reference 25M clock (XTAL). + * However minimal CPU counter prescaler is 2, so the counter + * frequency will be divided by 2, the number is 12.5M + */ +#define COUNTER_FREQUENCY 12500000 + +#define MVEBU_REGS_BASE 0xD0000000 + +/***************************************************************************** + * MVEBU memory map related constants + ***************************************************************************** + */ +/* Aggregate of all devices in the first GB */ +#define DEVICE0_BASE MVEBU_REGS_BASE +#define DEVICE0_SIZE 0x10000000 + +/***************************************************************************** + * GIC-500 & interrupt handling related constants + ***************************************************************************** + */ +/* Base MVEBU compatible GIC memory map */ +#define MVEBU_GICD_BASE 0x1D00000 +#define MVEBU_GICR_BASE 0x1D40000 +#define MVEBU_GICC_BASE 0x1D80000 + +/* + * CCI-400 base address + * This address is absolute, not relative to MVEBU_REGS_BASE. + * This is not the default CCI base address (that would be 0xD8000000). + * Rather we remap CCI to this address to better utilize the address space. + * (The remapping is done in plat/marvell/armada/a3k/common/plat_cci.c) + */ +#define MVEBU_CCI_BASE 0xFE000000 + +/***************************************************************************** + * North and south bridge reset registers + ***************************************************************************** + */ +#define MVEBU_NB_RESET_REG (MVEBU_REGS_BASE + 0x12400) +#define MVEBU_NB_RESET_I2C1_N (1 << 0) +#define MVEBU_NB_RESET_1WIRE_N (1 << 1) +#define MVEBU_NB_RESET_SPI_N (1 << 2) +#define MVEBU_NB_RESET_UART_N (1 << 3) +#define MVEBU_NB_RESET_XTL_N (1 << 4) +#define MVEBU_NB_RESET_I2C2_N (1 << 5) +#define MVEBU_NB_RESET_UART2_N (1 << 6) +#define MVEBU_NB_RESET_AVS_N (1 << 7) +#define MVEBU_NB_RESET_DDR_N (1 << 10) +#define MVEBU_NB_RESET_SETM_N (1 << 11) +#define MVEBU_NB_RESET_DMA_N (1 << 12) +#define MVEBU_NB_RESET_TSECM_N (1 << 13) +#define MVEBU_NB_RESET_SDIO_N (1 << 14) +#define MVEBU_NB_RESET_SATA_N (1 << 15) +#define MVEBU_NB_RESET_PWRMGT_N (1 << 16) +#define MVEBU_NB_RESET_OTP_N (1 << 17) +#define MVEBU_NB_RESET_EIP_N (1 << 18) +#define MVEBU_SB_RESET_REG (MVEBU_REGS_BASE + 0x18600) +#define MVEBU_SB_RESET_MCIPHY (1 << 1) +#define MVEBU_SB_RESET_SDIO_N (1 << 2) +#define MVEBU_SB_RESET_PCIE_N (1 << 3) +#define MVEBU_SB_RESET_GBE1_N (1 << 4) +#define MVEBU_SB_RESET_GBE0_N (1 << 5) +#define MVEBU_SB_RESET_USB2PHY (1 << 6) +#define MVEBU_SB_RESET_USB2HPHY (1 << 7) +#define MVEBU_SB_RESET_MCI_N (1 << 8) +#define MVEBU_SB_RESET_PWRMGT_N (1 << 9) +#define MVEBU_SB_RESET_EBM_N (1 << 10) +#define MVEBU_SB_RESET_OTP_N (1 << 11) + +/***************************************************************************** + * North and south bridge register base + ***************************************************************************** + */ +#define MVEBU_NB_REGS_BASE (MVEBU_REGS_BASE + 0x13000) +#define MVEBU_SB_REGS_BASE (MVEBU_REGS_BASE + 0x18000) + +/***************************************************************************** + * GPIO registers related constants + ***************************************************************************** + */ +/* North and south bridge GPIO register base address */ +#define MVEBU_NB_GPIO_REG_BASE (MVEBU_NB_REGS_BASE + 0x800) +#define MVEBU_NB_GPIO_IRQ_REG_BASE (MVEBU_NB_REGS_BASE + 0xC00) +#define MVEBU_SB_GPIO_REG_BASE (MVEBU_SB_REGS_BASE + 0x800) +#define MVEBU_SB_GPIO_IRQ_REG_BASE (MVEBU_SB_REGS_BASE + 0xC00) +#define MVEBU_NB_SB_IRQ_REG_BASE (MVEBU_REGS_BASE + 0x8A00) + +/* North Bridge GPIO selection register */ +#define MVEBU_NB_GPIO_SEL_REG (MVEBU_NB_GPIO_REG_BASE + 0x30) +#define MVEBU_NB_GPIO_OUTPUT_EN_HIGH_REG (MVEBU_NB_GPIO_REG_BASE + 0x04) +/* I2C1 GPIO Enable bit offset */ +#define MVEBU_GPIO_TW1_GPIO_EN_OFF (10) +/* SPI pins mode bit offset */ +#define MVEBU_GPIO_NB_SPI_PIN_MODE_OFF (28) + +/***************************************************************************** + * DRAM registers related constants + ***************************************************************************** + */ +#define MVEBU_DRAM_REG_BASE (MVEBU_REGS_BASE) + +/***************************************************************************** + * SB wake-up registers related constants + ***************************************************************************** + */ +#define MVEBU_SB_WAKEUP_REG_BASE (MVEBU_REGS_BASE + 0x19000) + +/***************************************************************************** + * PMSU registers related constants + ***************************************************************************** + */ +#define MVEBU_PMSU_REG_BASE (MVEBU_REGS_BASE + 0x14000) + +/***************************************************************************** + * North Bridge Step-Down Registers + ***************************************************************************** + */ +#define MVEBU_NB_STEP_DOWN_REG_BASE (MVEBU_REGS_BASE + 0x12800) + +/***************************************************************************** + * DRAM CS memory map register base + ***************************************************************************** + */ +#define MVEBU_CS_MMAP_REG_BASE (MVEBU_REGS_BASE + 0x200) + +/***************************************************************************** + * CPU decoder window registers related constants + ***************************************************************************** + */ +#define MVEBU_CPU_DEC_WIN_REG_BASE (MVEBU_REGS_BASE + 0xCF00) + +/***************************************************************************** + * AVS registers related constants + ***************************************************************************** + */ +#define MVEBU_AVS_REG_BASE (MVEBU_REGS_BASE + 0x11500) + + +/***************************************************************************** + * AVS registers related constants + ***************************************************************************** + */ +#define MVEBU_COMPHY_REG_BASE (MVEBU_REGS_BASE + 0x18300) + +/***************************************************************************** + * Cortex-M3 Secure Processor Mailbox constants + ***************************************************************************** + */ +#define MVEBU_RWTM_REG_BASE (MVEBU_REGS_BASE + 0xB0000) + +#endif /* A3700_PLAT_DEF_H */ diff --git a/plat/marvell/armada/a3k/common/include/a3700_pm.h b/plat/marvell/armada/a3k/common/include/a3700_pm.h new file mode 100644 index 0000000..44dbb9f --- /dev/null +++ b/plat/marvell/armada/a3k/common/include/a3700_pm.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016-2020 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#ifndef A3700_PM_H +#define A3700_PM_H + +#include <stdint.h> + +/* supported wake up sources */ +enum pm_wake_up_src_type { + WAKE_UP_SRC_GPIO, + /* FOLLOWING SRC NOT SUPPORTED YET */ + WAKE_UP_SRC_TIMER, + WAKE_UP_SRC_UART0, + WAKE_UP_SRC_UART1, + WAKE_UP_SRC_MAX, +}; + +struct pm_gpio_data { + /* + * bank 0: North bridge GPIO + * bank 1: South bridge GPIO + */ + uint32_t bank_num; + uint32_t gpio_num; +}; + +union pm_wake_up_src_data { + struct pm_gpio_data gpio_data; + /* delay in seconds */ + uint32_t timer_delay; +}; + +struct pm_wake_up_src { + enum pm_wake_up_src_type wake_up_src_type; + + union pm_wake_up_src_data wake_up_data; +}; + +struct pm_wake_up_src_config { + uint32_t wake_up_src_num; + struct pm_wake_up_src wake_up_src[WAKE_UP_SRC_MAX]; +}; + +struct pm_wake_up_src_config *mv_wake_up_src_config_get(void); + +void cm3_system_reset(void); + +#endif /* A3700_PM_H */ diff --git a/plat/marvell/armada/a3k/common/include/ddr_info.h b/plat/marvell/armada/a3k/common/include/ddr_info.h new file mode 100644 index 0000000..254f78c --- /dev/null +++ b/plat/marvell/armada/a3k/common/include/ddr_info.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#ifndef DDR_INFO_H +#define DDR_INFO_H + +#define DRAM_MAX_IFACE 1 +#define DRAM_CH0_MMAP_LOW_OFFSET 0x200 + +#endif /* DDR_INFO_H */ diff --git a/plat/marvell/armada/a3k/common/include/dram_win.h b/plat/marvell/armada/a3k/common/include/dram_win.h new file mode 100644 index 0000000..26a0137 --- /dev/null +++ b/plat/marvell/armada/a3k/common/include/dram_win.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#ifndef DRAM_WIN_H +#define DRAM_WIN_H + +#include <common/bl_common.h> + +#include <io_addr_dec.h> + +void dram_win_map_build(struct dram_win_map *win_map); +void cpu_wins_init(void); + +#endif /* DRAM_WIN_H */ diff --git a/plat/marvell/armada/a3k/common/include/io_addr_dec.h b/plat/marvell/armada/a3k/common/include/io_addr_dec.h new file mode 100644 index 0000000..42ef30b --- /dev/null +++ b/plat/marvell/armada/a3k/common/include/io_addr_dec.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#ifndef IO_ADDR_DEC_H +#define IO_ADDR_DEC_H + +#include <stdint.h> + +/* There are 5 configurable cpu decoder windows. */ +#define DRAM_WIN_MAP_NUM_MAX 5 +/* Target number for dram in cpu decoder windows. */ +#define DRAM_CPU_DEC_TARGET_NUM 0 + +/* + * Not all configurable decode windows could be used for dram, some units have + * to reserve one decode window for other unit they have to communicate with; + * for example, DMA engineer has 3 configurable windows, but only two could be + * for dram while the last one has to be for pcie, so for DMA, its max_dram_win + * is 2. + */ +struct dec_win_config { + uint32_t dec_reg_base; /* IO address decoder register base address */ + uint32_t win_attr; /* IO address decoder windows attributes */ + /* How many configurable dram decoder windows that this unit has; */ + uint32_t max_dram_win; + /* The decoder windows number including remapping that this unit has */ + uint32_t max_remap; + /* The offset between continuous decode windows + * within the same unit, typically 0x10 + */ + uint32_t win_offset; +}; + +struct dram_win { + uintptr_t base_addr; + uintptr_t win_size; +}; + +struct dram_win_map { + int dram_win_num; + struct dram_win dram_windows[DRAM_WIN_MAP_NUM_MAX]; +}; + +/* + * init_io_addr_dec + * + * This function initializes io address decoder windows by + * cpu dram window mapping information + * + * @input: N/A + * - dram_wins_map: cpu dram windows mapping + * - io_dec_config: io address decoder windows configuration + * - io_unit_num: io address decoder unit number + * @output: N/A + * + * @return: 0 on success and others on failure + */ +int init_io_addr_dec(struct dram_win_map *dram_wins_map, + struct dec_win_config *io_dec_config, + uint32_t io_unit_num); + +#endif /* IO_ADDR_DEC_H */ diff --git a/plat/marvell/armada/a3k/common/include/plat_macros.S b/plat/marvell/armada/a3k/common/include/plat_macros.S new file mode 100644 index 0000000..f689b4f --- /dev/null +++ b/plat/marvell/armada/a3k/common/include/plat_macros.S @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#ifndef PLAT_MACROS_S +#define PLAT_MACROS_S + +#include <marvell_macros.S> + +/* --------------------------------------------- + * The below macro prints out relevant GIC and + * CCI registers registers whenever an unhandled + * exception is taken in BL31. + * --------------------------------------------- + */ +.macro plat_crash_print_regs + mov_imm x17, MVEBU_GICC_BASE + mov_imm x16, MVEBU_GICD_BASE + marvell_print_gic_regs + print_cci_regs +.endm + +#endif /* PLAT_MACROS_S */ diff --git a/plat/marvell/armada/a3k/common/include/platform_def.h b/plat/marvell/armada/a3k/common/include/platform_def.h new file mode 100644 index 0000000..f19d96b --- /dev/null +++ b/plat/marvell/armada/a3k/common/include/platform_def.h @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2016-2021 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#ifndef __ASSEMBLER__ +#include <stdio.h> +#endif /* __ASSEMBLER__ */ + +#include <board_marvell_def.h> +#include <mvebu_def.h> + +/* + * Most platform porting definitions provided by included headers + */ + +/* + * DRAM Memory layout: + * +-----------------------+ + * : : + * : Linux : + * 0x04X00000-->+-----------------------+ + * | BL3-3(u-boot) |>>}>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + * |-----------------------| } | + * | BL3-[0,1, 2] | }---------------------------------> | + * |-----------------------| } || | + * | BL2 | }->FIP (loaded by || | + * |-----------------------| } BootROM to DRAM) || | + * | FIP_TOC | } || | + * 0x04120000-->|-----------------------| || | + * | BL1 (RO) | || | + * 0x04100000-->+-----------------------+ || | + * : : || | + * : Trusted SRAM section : \/ | + * 0x04040000-->+-----------------------+ Replaced by BL2 +----------------+ | + * | BL1 (RW) | <<<<<<<<<<<<<<<< | BL3-1 NOBITS | | + * 0x04037000-->|-----------------------| <<<<<<<<<<<<<<<< |----------------| | + * | | <<<<<<<<<<<<<<<< | BL3-1 PROGBITS | | + * 0x04023000-->|-----------------------| +----------------+ | + * | BL2 | | + * |-----------------------| | + * | | | + * 0x04001000-->|-----------------------| | + * | Shared | | + * 0x04000000-->+-----------------------+ | + * : : | + * : Linux : | + * : : | + * |-----------------------| | + * | | U-Boot(BL3-3) Loaded by BL2 | + * | U-Boot | <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + * 0x00000000-->+-----------------------+ + * + * Trusted SRAM section 0x4000000..0x4200000: + * ---------------------------------------- + * SRAM_BASE = 0x4001000 + * BL2_BASE = 0x4006000 + * BL2_LIMIT = BL31_BASE + * BL31_BASE = 0x4023000 = (64MB + 256KB - 0x1D000) + * BL31_PROGBITS_LIMIT = BL1_RW_BASE + * BL1_RW_BASE = 0x4037000 = (64MB + 256KB - 0x9000) + * BL1_RW_LIMIT = BL31_LIMIT = 0x4040000 + * + * + * PLAT_MARVELL_FIP_BASE = 0x4120000 + */ + +/* + * Since BL33 is loaded by BL2 (and validated by BL31) to DRAM offset 0, + * it is allowed to load/copy images to 'NULL' pointers + */ +#if defined(IMAGE_BL2) || defined(IMAGE_BL31) +#define PLAT_ALLOW_ZERO_ADDR_COPY +#endif + +#define PLAT_MARVELL_ATF_BASE 0x4000000 +#define PLAT_MARVELL_ATF_LOAD_ADDR \ + (PLAT_MARVELL_ATF_BASE + 0x100000) + +#define PLAT_MARVELL_FIP_BASE \ + (PLAT_MARVELL_ATF_LOAD_ADDR + 0x20000) +#define PLAT_MARVELL_FIP_MAX_SIZE 0x4000000 + +#define PLAT_MARVELL_CLUSTER_CORE_COUNT U(2) +/* DRAM[2MB..66MB] is used as Trusted ROM */ +#define PLAT_MARVELL_TRUSTED_ROM_BASE PLAT_MARVELL_ATF_LOAD_ADDR +/* 4 MB for FIP image */ +#define PLAT_MARVELL_TRUSTED_ROM_SIZE 0x00400000 +/* Reserve 12M for SCP (Secure PayLoad) Trusted RAM + * OP-TEE SHMEM follows this region + */ +#define PLAT_MARVELL_TRUSTED_RAM_BASE 0x04400000 +#define PLAT_MARVELL_TRUSTED_RAM_SIZE 0x00C00000 /* 12 MB DRAM */ + +/* + * PLAT_ARM_MAX_BL1_RW_SIZE is calculated using the current BL1 RW debug size + * plus a little space for growth. + */ +#define PLAT_MARVELL_MAX_BL1_RW_SIZE 0xA000 + +/* + * PLAT_ARM_MAX_BL2_SIZE is calculated using the current BL2 debug size plus a + * little space for growth. + */ +#define PLAT_MARVELL_MAX_BL2_SIZE 0xF000 + +/* + * PLAT_ARM_MAX_BL31_SIZE is calculated using the current BL31 debug size plus a + * little space for growth. + */ +#define PLAT_MARVEL_MAX_BL31_SIZE 0x5D000 + +#define PLAT_MARVELL_CPU_ENTRY_ADDR BL1_RO_BASE + +/* GIC related definitions */ +#define PLAT_MARVELL_GICD_BASE (MVEBU_REGS_BASE + MVEBU_GICD_BASE) +#define PLAT_MARVELL_GICR_BASE (MVEBU_REGS_BASE + MVEBU_GICR_BASE) +#define PLAT_MARVELL_GICC_BASE (MVEBU_REGS_BASE + MVEBU_GICC_BASE) + +#define PLAT_MARVELL_G0_IRQ_PROPS(grp) \ + INTR_PROP_DESC(MARVELL_IRQ_SEC_SGI_0, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(MARVELL_IRQ_SEC_SGI_6, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL) + +#define PLAT_MARVELL_G1S_IRQ_PROPS(grp) \ + INTR_PROP_DESC(MARVELL_IRQ_SEC_PHY_TIMER, \ + GIC_HIGHEST_SEC_PRIORITY, grp, GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(MARVELL_IRQ_SEC_SGI_1, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(MARVELL_IRQ_SEC_SGI_2, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(MARVELL_IRQ_SEC_SGI_3, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(MARVELL_IRQ_SEC_SGI_4, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(MARVELL_IRQ_SEC_SGI_5, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(MARVELL_IRQ_SEC_SGI_7, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL) + + +#define PLAT_MARVELL_SHARED_RAM_CACHED 1 + +/* CCI related constants */ +#define PLAT_MARVELL_CCI_BASE MVEBU_CCI_BASE +#define PLAT_MARVELL_CCI_CLUSTER0_SL_IFACE_IX 3 +#define PLAT_MARVELL_CCI_CLUSTER1_SL_IFACE_IX 4 + +/* + * Load address of BL3-3 for this platform port + */ +#define PLAT_MARVELL_NS_IMAGE_OFFSET 0x0 + +/* System Reference Clock*/ +#define PLAT_REF_CLK_IN_HZ COUNTER_FREQUENCY + +/* + * PL011 related constants + */ +#define PLAT_MARVELL_UART_BASE (MVEBU_REGS_BASE + 0x12000) + +/* Required platform porting definitions */ +#define PLAT_MAX_PWR_LVL MPIDR_AFFLVL1 + +/* System timer related constants */ +#define PLAT_MARVELL_NSTIMER_FRAME_ID 1 + +/* Mailbox base address */ +#define PLAT_MARVELL_MAILBOX_BASE (MARVELL_SHARED_RAM_BASE + 0x400) +#define PLAT_MARVELL_MAILBOX_SIZE 0x100 +#define PLAT_MARVELL_MAILBOX_MAGIC_NUM 0x6D72766C /* mrvl */ + +/* DRAM CS memory map registers related constants */ +#define MVEBU_CS_MMAP_LOW(cs_num) \ + (MVEBU_CS_MMAP_REG_BASE + (cs_num) * 0x8) +#define MVEBU_CS_MMAP_ENABLE 0x1 +#define MVEBU_CS_MMAP_AREA_LEN_OFFS 16 +#define MVEBU_CS_MMAP_AREA_LEN_MASK \ + (0x1f << MVEBU_CS_MMAP_AREA_LEN_OFFS) +#define MVEBU_CS_MMAP_START_ADDR_LOW_OFFS 23 +#define MVEBU_CS_MMAP_START_ADDR_LOW_MASK \ + (0x1ff << MVEBU_CS_MMAP_START_ADDR_LOW_OFFS) + +#define MVEBU_CS_MMAP_HIGH(cs_num) \ + (MVEBU_CS_MMAP_REG_BASE + 0x4 + (cs_num) * 0x8) + +/* DRAM max CS number */ +#define MVEBU_MAX_CS_MMAP_NUM (2) + +/* CPU decoder window related constants */ +#define CPU_DEC_WIN_CTRL_REG(win_num) \ + (MVEBU_CPU_DEC_WIN_REG_BASE + (win_num) * 0x10) +#define CPU_DEC_CR_WIN_ENABLE 0x1 +#define CPU_DEC_CR_WIN_TARGET_OFFS 4 +#define CPU_DEC_CR_WIN_TARGET_MASK \ + (0xf << CPU_DEC_CR_WIN_TARGET_OFFS) + +#define CPU_DEC_WIN_SIZE_REG(win_num) \ + (MVEBU_CPU_DEC_WIN_REG_BASE + 0x4 + (win_num) * 0x10) +#define CPU_DEC_CR_WIN_SIZE_OFFS 0 +#define CPU_DEC_CR_WIN_SIZE_MASK \ + (0xffff << CPU_DEC_CR_WIN_SIZE_OFFS) +#define CPU_DEC_CR_WIN_SIZE_ALIGNMENT 0x10000 + +#define CPU_DEC_WIN_BASE_REG(win_num) \ + (MVEBU_CPU_DEC_WIN_REG_BASE + 0x8 + (win_num) * 0x10) +#define CPU_DEC_BR_BASE_OFFS 0 +#define CPU_DEC_BR_BASE_MASK \ + (0xffff << CPU_DEC_BR_BASE_OFFS) + +#define CPU_DEC_REMAP_LOW_REG(win_num) \ + (MVEBU_CPU_DEC_WIN_REG_BASE + 0xC + (win_num) * 0x10) +#define CPU_DEC_RLR_REMAP_LOW_OFFS 0 +#define CPU_DEC_RLR_REMAP_LOW_MASK \ + (0xffff << CPU_DEC_BR_BASE_OFFS) + +#define CPU_DEC_CCI_BASE_REG (MVEBU_CPU_DEC_WIN_REG_BASE + 0xe0) + +/* Securities */ +#define IRQ_SEC_OS_TICK_INT MARVELL_IRQ_SEC_PHY_TIMER + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/marvell/armada/a3k/common/io_addr_dec.c b/plat/marvell/armada/a3k/common/io_addr_dec.c new file mode 100644 index 0000000..fea7f81 --- /dev/null +++ b/plat/marvell/armada/a3k/common/io_addr_dec.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2016 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <common/debug.h> +#include <lib/mmio.h> + +#include <io_addr_dec.h> +#include <plat_marvell.h> + +#define MVEBU_DEC_WIN_CTRL_REG(base, win, off) (MVEBU_REGS_BASE + (base) + \ + (win) * (off)) +#define MVEBU_DEC_WIN_BASE_REG(base, win, off) (MVEBU_REGS_BASE + (base) + \ + (win) * (off) + 0x4) +#define MVEBU_DEC_WIN_REMAP_REG(base, win, off) (MVEBU_REGS_BASE + (base) + \ + (win) * (off) + 0x8) + +#define MVEBU_DEC_WIN_CTRL_SIZE_OFF (16) +#define MVEBU_DEC_WIN_ENABLE (0x1) +#define MVEBU_DEC_WIN_CTRL_ATTR_OFF (8) +#define MVEBU_DEC_WIN_CTRL_TARGET_OFF (4) +#define MVEBU_DEC_WIN_CTRL_EN_OFF (0) +#define MVEBU_DEC_WIN_BASE_OFF (16) + +#define MVEBU_WIN_BASE_SIZE_ALIGNMENT (0x10000) + +/* There are up to 14 IO unit which need address decode in Armada-3700 */ +#define IO_UNIT_NUM_MAX (14) + +#define MVEBU_MAX_ADDRSS_4GB (0x100000000ULL) + + +static void set_io_addr_dec_win(int win_id, uintptr_t base_addr, + uintptr_t win_size, + struct dec_win_config *dec_win) +{ + uint32_t ctrl = 0; + uint32_t base = 0; + + /* set size */ + ctrl = ((win_size / MVEBU_WIN_BASE_SIZE_ALIGNMENT) - 1) << + MVEBU_DEC_WIN_CTRL_SIZE_OFF; + /* set attr according to IO decode window */ + ctrl |= dec_win->win_attr << MVEBU_DEC_WIN_CTRL_ATTR_OFF; + /* set target */ + ctrl |= DRAM_CPU_DEC_TARGET_NUM << MVEBU_DEC_WIN_CTRL_TARGET_OFF; + /* set base */ + base = (base_addr / MVEBU_WIN_BASE_SIZE_ALIGNMENT) << + MVEBU_DEC_WIN_BASE_OFF; + + /* set base address*/ + mmio_write_32(MVEBU_DEC_WIN_BASE_REG(dec_win->dec_reg_base, + win_id, dec_win->win_offset), + base); + /* set remap window, some unit does not have remap window */ + if (win_id < dec_win->max_remap) + mmio_write_32(MVEBU_DEC_WIN_REMAP_REG(dec_win->dec_reg_base, + win_id, dec_win->win_offset), base); + /* set control register */ + mmio_write_32(MVEBU_DEC_WIN_CTRL_REG(dec_win->dec_reg_base, + win_id, dec_win->win_offset), ctrl); + /* enable the address decode window at last to make it effective */ + ctrl |= MVEBU_DEC_WIN_ENABLE << MVEBU_DEC_WIN_CTRL_EN_OFF; + mmio_write_32(MVEBU_DEC_WIN_CTRL_REG(dec_win->dec_reg_base, + win_id, dec_win->win_offset), ctrl); + + INFO("set_io_addr_dec %d result: ctrl(0x%x) base(0x%x) remap(0x%x)\n", + win_id, mmio_read_32(MVEBU_DEC_WIN_CTRL_REG(dec_win->dec_reg_base, + win_id, dec_win->win_offset)), + mmio_read_32(MVEBU_DEC_WIN_BASE_REG(dec_win->dec_reg_base, + win_id, dec_win->win_offset)), + (win_id < dec_win->max_remap) ? + mmio_read_32(MVEBU_DEC_WIN_REMAP_REG(dec_win->dec_reg_base, + win_id, dec_win->win_offset)) : 0); +} + +/* Set io decode window */ +static int set_io_addr_dec(struct dram_win_map *win_map, + struct dec_win_config *dec_win) +{ + struct dram_win *win; + int id; + + /* disable all windows first */ + for (id = 0; id < dec_win->max_dram_win; id++) + mmio_write_32(MVEBU_DEC_WIN_CTRL_REG(dec_win->dec_reg_base, id, + dec_win->win_offset), 0); + + /* configure IO decode windows for DRAM, inheritate DRAM size, + * base and target from CPU-DRAM decode window and others + * from hard coded IO decode window settings array. + */ + if (win_map->dram_win_num > dec_win->max_dram_win) { + /* + * If cpu dram windows number exceeds the io decode windows + * max number, then fill the first io decode window + * with base(0) and size(4GB). + */ + set_io_addr_dec_win(0, 0, MVEBU_MAX_ADDRSS_4GB, dec_win); + + return 0; + } + + for (id = 0; id < win_map->dram_win_num; id++, win++) { + win = &win_map->dram_windows[id]; + set_io_addr_dec_win(id, win->base_addr, win->win_size, dec_win); + } + + return 0; +} + +/* + * init_io_addr_dec + * + * This function initializes io address decoder windows by + * cpu dram window mapping information + * + * @input: N/A + * - dram_wins_map: cpu dram windows mapping + * - io_dec_config: io address decoder windows configuration + * - io_unit_num: io address decoder unit number + * @output: N/A + * + * @return: 0 on success and others on failure + */ +int init_io_addr_dec(struct dram_win_map *dram_wins_map, + struct dec_win_config *io_dec_config, uint32_t io_unit_num) +{ + int32_t index; + struct dec_win_config *io_dec_win; + int32_t ret; + + INFO("Initializing IO address decode windows\n"); + + if (io_dec_config == NULL || io_unit_num == 0) { + ERROR("No IO address decoder windows configurations!\n"); + return -1; + } + + if (io_unit_num > IO_UNIT_NUM_MAX) { + ERROR("IO address decoder windows number %d is over max %d\n", + io_unit_num, IO_UNIT_NUM_MAX); + return -1; + } + + if (dram_wins_map == NULL) { + ERROR("No cpu dram decoder windows map!\n"); + return -1; + } + + for (index = 0; index < dram_wins_map->dram_win_num; index++) + INFO("DRAM mapping %d base(0x%lx) size(0x%lx)\n", + index, dram_wins_map->dram_windows[index].base_addr, + dram_wins_map->dram_windows[index].win_size); + + /* Set address decode window for each IO */ + for (index = 0; index < io_unit_num; index++) { + io_dec_win = io_dec_config + index; + ret = set_io_addr_dec(dram_wins_map, io_dec_win); + if (ret) { + ERROR("Failed to set IO address decode\n"); + return -1; + } + INFO("Set IO decode window successfully, base(0x%x)" + " win_attr(%x) max_dram_win(%d) max_remap(%d)" + " win_offset(%d)\n", io_dec_win->dec_reg_base, + io_dec_win->win_attr, io_dec_win->max_dram_win, + io_dec_win->max_remap, io_dec_win->win_offset); + } + + return 0; +} diff --git a/plat/marvell/armada/a3k/common/marvell_plat_config.c b/plat/marvell/armada/a3k/common/marvell_plat_config.c new file mode 100644 index 0000000..3bf3d96 --- /dev/null +++ b/plat/marvell/armada/a3k/common/marvell_plat_config.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <common/bl_common.h> + +#include <io_addr_dec.h> +#include <mvebu_def.h> + +struct dec_win_config io_dec_win_conf[] = { + /* dec_reg_base win_attr max_dram_win max_remap win_offset */ + {0xc000, 0x3d, 2, 0, 0x08}, /* USB */ + {0xc100, 0x3d, 3, 0, 0x10}, /* USB3 */ + {0xc200, 0x3d, 2, 0, 0x10}, /* DMA */ + {0xc300, 0x3d, 2, 0, 0x10}, /* NETA0 */ + {0xc400, 0x3d, 2, 0, 0x10}, /* NETA1 */ + {0xc500, 0x3d, 2, 0, 0x10}, /* PCIe */ + {0xc800, 0x3d, 3, 0, 0x10}, /* SATA */ + {0xca00, 0x3d, 3, 0, 0x08}, /* SD */ + {0xcb00, 0x3d, 3, 0, 0x10}, /* eMMC */ + {0xce00, 0x3d, 2, 0, 0x08}, /* EIP97 */ +}; + +int marvell_get_io_dec_win_conf(struct dec_win_config **win, uint32_t *size) +{ + *win = io_dec_win_conf; + *size = sizeof(io_dec_win_conf)/sizeof(struct dec_win_config); + + return 0; +} + diff --git a/plat/marvell/armada/a3k/common/plat_cci.c b/plat/marvell/armada/a3k/common/plat_cci.c new file mode 100644 index 0000000..56f091f --- /dev/null +++ b/plat/marvell/armada/a3k/common/plat_cci.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Marek Behun <marek.behun@nic.cz> + * + * Based on plat/marvell/armada/common/marvell_cci.c + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <drivers/arm/cci.h> +#include <lib/mmio.h> + +#include <plat_marvell.h> + +static const int cci_map[] = { + PLAT_MARVELL_CCI_CLUSTER0_SL_IFACE_IX, + PLAT_MARVELL_CCI_CLUSTER1_SL_IFACE_IX +}; + +/* + * This redefines the weak definition in + * plat/marvell/armada/common/marvell_cci.c + */ +void plat_marvell_interconnect_init(void) +{ + /* + * To better utilize the address space, we remap CCI base address from + * the default (0xD8000000) to MVEBU_CCI_BASE. + * This has to be done here, rather than in cpu_wins_init(), because + * cpu_wins_init() is called later. + */ + mmio_write_32(CPU_DEC_CCI_BASE_REG, MVEBU_CCI_BASE >> 20); + + cci_init(PLAT_MARVELL_CCI_BASE, cci_map, ARRAY_SIZE(cci_map)); +} diff --git a/plat/marvell/armada/a3k/common/plat_pm.c b/plat/marvell/armada/a3k/common/plat_pm.c new file mode 100644 index 0000000..e2d15ab --- /dev/null +++ b/plat/marvell/armada/a3k/common/plat_pm.c @@ -0,0 +1,822 @@ +/* + * Copyright (C) 2018-2020 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <common/debug.h> +#ifdef USE_CCI +#include <drivers/arm/cci.h> +#endif +#include <lib/psci/psci.h> +#include <lib/mmio.h> +#include <plat/common/platform.h> + +#include <a3700_pm.h> +#include <arch_helpers.h> +#include <armada_common.h> +#include <dram_win.h> +#include <io_addr_dec.h> +#include <mvebu.h> +#include <mvebu_def.h> +#include <marvell_plat_priv.h> +#include <plat_marvell.h> + +/* Warm reset register */ +#define MVEBU_WARM_RESET_REG (MVEBU_NB_REGS_BASE + 0x840) +#define MVEBU_WARM_RESET_MAGIC 0x1D1E + +/* North Bridge GPIO1 SEL register */ +#define MVEBU_NB_GPIO1_SEL_REG (MVEBU_NB_REGS_BASE + 0x830) + #define MVEBU_NB_GPIO1_UART1_SEL BIT(19) + #define MVEBU_NB_GPIO1_GPIO_25_26_EN BIT(17) + #define MVEBU_NB_GPIO1_GPIO_19_EN BIT(14) + #define MVEBU_NB_GPIO1_GPIO_18_EN BIT(13) + +/* CPU 1 reset register */ +#define MVEBU_CPU_1_RESET_VECTOR (MVEBU_REGS_BASE + 0x14044) +#define MVEBU_CPU_1_RESET_REG (MVEBU_REGS_BASE + 0xD00C) +#define MVEBU_CPU_1_RESET_BIT 31 + +/* IRQ register */ +#define MVEBU_NB_IRQ_STATUS_1_REG (MVEBU_NB_SB_IRQ_REG_BASE) +#define MVEBU_NB_IRQ_STATUS_2_REG (MVEBU_NB_SB_IRQ_REG_BASE + \ + 0x10) +#define MVEBU_NB_IRQ_MASK_2_REG (MVEBU_NB_SB_IRQ_REG_BASE + \ + 0x18) +#define MVEBU_SB_IRQ_STATUS_1_REG (MVEBU_NB_SB_IRQ_REG_BASE + \ + 0x40) +#define MVEBU_SB_IRQ_STATUS_2_REG (MVEBU_NB_SB_IRQ_REG_BASE + \ + 0x50) +#define MVEBU_NB_GPIO_IRQ_MASK_1_REG (MVEBU_NB_SB_IRQ_REG_BASE + \ + 0xC8) +#define MVEBU_NB_GPIO_IRQ_MASK_2_REG (MVEBU_NB_SB_IRQ_REG_BASE + \ + 0xD8) +#define MVEBU_SB_GPIO_IRQ_MASK_REG (MVEBU_NB_SB_IRQ_REG_BASE + \ + 0xE8) +#define MVEBU_NB_GPIO_IRQ_EN_LOW_REG (MVEBU_NB_GPIO_IRQ_REG_BASE) +#define MVEBU_NB_GPIO_IRQ_EN_HIGH_REG (MVEBU_NB_GPIO_IRQ_REG_BASE + \ + 0x04) +#define MVEBU_NB_GPIO_IRQ_STATUS_LOW_REG (MVEBU_NB_GPIO_IRQ_REG_BASE + \ + 0x10) +#define MVEBU_NB_GPIO_IRQ_STATUS_HIGH_REG (MVEBU_NB_GPIO_IRQ_REG_BASE + \ + 0x14) +#define MVEBU_NB_GPIO_IRQ_WK_LOW_REG (MVEBU_NB_GPIO_IRQ_REG_BASE + \ + 0x18) +#define MVEBU_NB_GPIO_IRQ_WK_HIGH_REG (MVEBU_NB_GPIO_IRQ_REG_BASE + \ + 0x1C) +#define MVEBU_SB_GPIO_IRQ_EN_REG (MVEBU_SB_GPIO_IRQ_REG_BASE) +#define MVEBU_SB_GPIO_IRQ_STATUS_REG (MVEBU_SB_GPIO_IRQ_REG_BASE + \ + 0x10) +#define MVEBU_SB_GPIO_IRQ_WK_REG (MVEBU_SB_GPIO_IRQ_REG_BASE + \ + 0x18) + +/* PMU registers */ +#define MVEBU_PM_NB_PWR_CTRL_REG (MVEBU_PMSU_REG_BASE) + #define MVEBU_PM_PWR_DN_CNT_SEL BIT(28) + #define MVEBU_PM_SB_PWR_DWN BIT(4) + #define MVEBU_PM_INTERFACE_IDLE BIT(0) +#define MVEBU_PM_NB_CPU_PWR_CTRL_REG (MVEBU_PMSU_REG_BASE + 0x4) + #define MVEBU_PM_L2_FLUSH_EN BIT(22) +#define MVEBU_PM_NB_PWR_OPTION_REG (MVEBU_PMSU_REG_BASE + 0x8) + #define MVEBU_PM_DDR_SR_EN BIT(29) + #define MVEBU_PM_DDR_CLK_DIS_EN BIT(28) + #define MVEBU_PM_WARM_RESET_EN BIT(27) + #define MVEBU_PM_DDRPHY_PWRDWN_EN BIT(23) + #define MVEBU_PM_DDRPHY_PAD_PWRDWN_EN BIT(22) + #define MVEBU_PM_OSC_OFF_EN BIT(21) + #define MVEBU_PM_TBG_OFF_EN BIT(20) + #define MVEBU_PM_CPU_VDDV_OFF_EN BIT(19) + #define MVEBU_PM_AVS_DISABLE_MODE BIT(14) + #define MVEBU_PM_AVS_VDD2_MODE BIT(13) + #define MVEBU_PM_AVS_HOLD_MODE BIT(12) + #define MVEBU_PM_L2_SRAM_LKG_PD_EN BIT(8) + #define MVEBU_PM_EIP_SRAM_LKG_PD_EN BIT(7) + #define MVEBU_PM_DDRMC_SRAM_LKG_PD_EN BIT(6) + #define MVEBU_PM_MCI_SRAM_LKG_PD_EN BIT(5) + #define MVEBU_PM_MMC_SRAM_LKG_PD_EN BIT(4) + #define MVEBU_PM_SATA_SRAM_LKG_PD_EN BIT(3) + #define MVEBU_PM_DMA_SRAM_LKG_PD_EN BIT(2) + #define MVEBU_PM_SEC_SRAM_LKG_PD_EN BIT(1) + #define MVEBU_PM_CPU_SRAM_LKG_PD_EN BIT(0) + #define MVEBU_PM_NB_SRAM_LKG_PD_EN (MVEBU_PM_L2_SRAM_LKG_PD_EN |\ + MVEBU_PM_EIP_SRAM_LKG_PD_EN | MVEBU_PM_DDRMC_SRAM_LKG_PD_EN |\ + MVEBU_PM_MCI_SRAM_LKG_PD_EN | MVEBU_PM_MMC_SRAM_LKG_PD_EN |\ + MVEBU_PM_SATA_SRAM_LKG_PD_EN | MVEBU_PM_DMA_SRAM_LKG_PD_EN |\ + MVEBU_PM_SEC_SRAM_LKG_PD_EN | MVEBU_PM_CPU_SRAM_LKG_PD_EN) +#define MVEBU_PM_NB_PWR_DEBUG_REG (MVEBU_PMSU_REG_BASE + 0xC) + #define MVEBU_PM_NB_FORCE_CLK_ON BIT(30) + #define MVEBU_PM_IGNORE_CM3_SLEEP BIT(21) + #define MVEBU_PM_IGNORE_CM3_DEEP BIT(20) +#define MVEBU_PM_NB_WAKE_UP_EN_REG (MVEBU_PMSU_REG_BASE + 0x2C) + #define MVEBU_PM_SB_WKP_NB_EN BIT(31) + #define MVEBU_PM_NB_GPIO_WKP_EN BIT(27) + #define MVEBU_PM_SOC_TIMER_WKP_EN BIT(26) + #define MVEBU_PM_UART_WKP_EN BIT(25) + #define MVEBU_PM_UART2_WKP_EN BIT(19) + #define MVEBU_PM_CPU_TIMER_WKP_EN BIT(17) + #define MVEBU_PM_NB_WKP_EN BIT(16) + #define MVEBU_PM_CORE1_FIQ_IRQ_WKP_EN BIT(13) + #define MVEBU_PM_CORE0_FIQ_IRQ_WKP_EN BIT(12) +#define MVEBU_PM_CPU_0_PWR_CTRL_REG (MVEBU_PMSU_REG_BASE + 0x34) +#define MVEBU_PM_CPU_1_PWR_CTRL_REG (MVEBU_PMSU_REG_BASE + 0x38) + #define MVEBU_PM_CORE_SOC_PD BIT(2) + #define MVEBU_PM_CORE_PROC_PD BIT(1) + #define MVEBU_PM_CORE_PD BIT(0) +#define MVEBU_PM_CORE_1_RETURN_ADDR_REG (MVEBU_PMSU_REG_BASE + 0x44) +#define MVEBU_PM_CPU_VDD_OFF_INFO_1_REG (MVEBU_PMSU_REG_BASE + 0x48) +#define MVEBU_PM_CPU_VDD_OFF_INFO_2_REG (MVEBU_PMSU_REG_BASE + 0x4C) + #define MVEBU_PM_LOW_POWER_STATE BIT(0) +#define MVEBU_PM_CPU_WAKE_UP_CONF_REG (MVEBU_PMSU_REG_BASE + 0x54) + #define MVEBU_PM_CORE1_WAKEUP BIT(13) + #define MVEBU_PM_CORE0_WAKEUP BIT(12) +#define MVEBU_PM_WAIT_DDR_RDY_VALUE (0x15) +#define MVEBU_PM_SB_CPU_PWR_CTRL_REG (MVEBU_SB_WAKEUP_REG_BASE) + #define MVEBU_PM_SB_PM_START BIT(0) +#define MVEBU_PM_SB_PWR_OPTION_REG (MVEBU_SB_WAKEUP_REG_BASE + 0x4) + #define MVEBU_PM_SDIO_PHY_PDWN_EN BIT(17) + #define MVEBU_PM_SB_VDDV_OFF_EN BIT(16) + #define MVEBU_PM_EBM_SRAM_LKG_PD_EN BIT(11) + #define MVEBU_PM_PCIE_SRAM_LKG_PD_EN BIT(10) + #define MVEBU_PM_GBE1_TX_SRAM_LKG_PD_EN BIT(9) + #define MVEBU_PM_GBE1_RX_SRAM_LKG_PD_EN BIT(8) + #define MVEBU_PM_GBE1_MIB_SRAM_LKG_PD_EN BIT(7) + #define MVEBU_PM_GBE0_TX_SRAM_LKG_PD_EN BIT(6) + #define MVEBU_PM_GBE0_RX_SRAM_LKG_PD_EN BIT(5) + #define MVEBU_PM_GBE0_MIB_SRAM_LKG_PD_EN BIT(4) + #define MVEBU_PM_SDIO_SRAM_LKG_PD_EN BIT(3) + #define MVEBU_PM_USB2_SRAM_LKG_PD_EN BIT(2) + #define MVEBU_PM_USB3_H_SRAM_LKG_PD_EN BIT(1) + #define MVEBU_PM_SB_SRAM_LKG_PD_EN (MVEBU_PM_EBM_SRAM_LKG_PD_EN |\ + MVEBU_PM_PCIE_SRAM_LKG_PD_EN | MVEBU_PM_GBE1_TX_SRAM_LKG_PD_EN |\ + MVEBU_PM_GBE1_RX_SRAM_LKG_PD_EN | MVEBU_PM_GBE1_MIB_SRAM_LKG_PD_EN |\ + MVEBU_PM_GBE0_TX_SRAM_LKG_PD_EN | MVEBU_PM_GBE0_RX_SRAM_LKG_PD_EN |\ + MVEBU_PM_GBE0_MIB_SRAM_LKG_PD_EN | MVEBU_PM_SDIO_SRAM_LKG_PD_EN |\ + MVEBU_PM_USB2_SRAM_LKG_PD_EN | MVEBU_PM_USB3_H_SRAM_LKG_PD_EN) +#define MVEBU_PM_SB_WK_EN_REG (MVEBU_SB_WAKEUP_REG_BASE + 0x10) + #define MVEBU_PM_SB_GPIO_WKP_EN BIT(24) + #define MVEBU_PM_SB_WKP_EN BIT(20) + +/* DRAM registers */ +#define MVEBU_DRAM_STATS_CH0_REG (MVEBU_DRAM_REG_BASE + 0x4) + #define MVEBU_DRAM_WCP_EMPTY BIT(19) +#define MVEBU_DRAM_CMD_0_REG (MVEBU_DRAM_REG_BASE + 0x20) + #define MVEBU_DRAM_CH0_CMD0 BIT(28) + #define MVEBU_DRAM_CS_CMD0 BIT(24) + #define MVEBU_DRAM_WCB_DRAIN_REQ BIT(1) +#define MVEBU_DRAM_PWR_CTRL_REG (MVEBU_DRAM_REG_BASE + 0x54) + #define MVEBU_DRAM_PHY_CLK_GATING_EN BIT(1) + #define MVEBU_DRAM_PHY_AUTO_AC_OFF_EN BIT(0) + +/* AVS registers */ +#define MVEBU_AVS_CTRL_2_REG (MVEBU_AVS_REG_BASE + 0x8) + #define MVEBU_LOW_VDD_MODE_EN BIT(6) + +/* Clock registers */ +#define MVEBU_NB_CLOCK_SEL_REG (MVEBU_NB_REGS_BASE + 0x10) + #define MVEBU_A53_CPU_CLK_SEL BIT(15) + +/* North Bridge Step-Down Registers */ +#define MVEBU_NB_STEP_DOWN_INT_EN_REG MVEBU_NB_STEP_DOWN_REG_BASE + #define MVEBU_NB_GPIO_INT_WAKE_WCPU_CLK BIT(8) + +#define MVEBU_NB_GPIO_18 18 +#define MVEBU_NB_GPIO_19 19 +#define MVEBU_NB_GPIO_25 25 +#define MVEBU_NB_GPIO_26 26 + +typedef int (*wake_up_src_func)(union pm_wake_up_src_data *); + +struct wake_up_src_func_map { + enum pm_wake_up_src_type type; + wake_up_src_func func; +}; + +void marvell_psci_arch_init(int die_index) +{ +} + +static void a3700_pm_ack_irq(void) +{ + uint32_t reg; + + reg = mmio_read_32(MVEBU_NB_IRQ_STATUS_1_REG); + if (reg) + mmio_write_32(MVEBU_NB_IRQ_STATUS_1_REG, reg); + + reg = mmio_read_32(MVEBU_NB_IRQ_STATUS_2_REG); + if (reg) + mmio_write_32(MVEBU_NB_IRQ_STATUS_2_REG, reg); + + reg = mmio_read_32(MVEBU_SB_IRQ_STATUS_1_REG); + if (reg) + mmio_write_32(MVEBU_SB_IRQ_STATUS_1_REG, reg); + + reg = mmio_read_32(MVEBU_SB_IRQ_STATUS_2_REG); + if (reg) + mmio_write_32(MVEBU_SB_IRQ_STATUS_2_REG, reg); + + reg = mmio_read_32(MVEBU_NB_GPIO_IRQ_STATUS_LOW_REG); + if (reg) + mmio_write_32(MVEBU_NB_GPIO_IRQ_STATUS_LOW_REG, reg); + + reg = mmio_read_32(MVEBU_NB_GPIO_IRQ_STATUS_HIGH_REG); + if (reg) + mmio_write_32(MVEBU_NB_GPIO_IRQ_STATUS_HIGH_REG, reg); + + reg = mmio_read_32(MVEBU_SB_GPIO_IRQ_STATUS_REG); + if (reg) + mmio_write_32(MVEBU_SB_GPIO_IRQ_STATUS_REG, reg); +} + +/***************************************************************************** + * A3700 handler called to check the validity of the power state + * parameter. + ***************************************************************************** + */ +int a3700_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + ERROR("%s needs to be implemented\n", __func__); + panic(); +} + +/***************************************************************************** + * A3700 handler called when a CPU is about to enter standby. + ***************************************************************************** + */ +void a3700_cpu_standby(plat_local_state_t cpu_state) +{ + ERROR("%s needs to be implemented\n", __func__); + panic(); +} + +/***************************************************************************** + * A3700 handler called when a power domain is about to be turned on. The + * mpidr determines the CPU to be turned on. + ***************************************************************************** + */ +int a3700_pwr_domain_on(u_register_t mpidr) +{ + /* Set barrier */ + dsbsy(); + + /* Set the cpu start address to BL1 entry point */ + mmio_write_32(MVEBU_CPU_1_RESET_VECTOR, + PLAT_MARVELL_CPU_ENTRY_ADDR >> 2); + + /* Get the cpu out of reset */ + mmio_clrbits_32(MVEBU_CPU_1_RESET_REG, BIT(MVEBU_CPU_1_RESET_BIT)); + mmio_setbits_32(MVEBU_CPU_1_RESET_REG, BIT(MVEBU_CPU_1_RESET_BIT)); + + return 0; +} + +/***************************************************************************** + * A3700 handler called to validate the entry point. + ***************************************************************************** + */ +int a3700_validate_ns_entrypoint(uintptr_t entrypoint) +{ + return PSCI_E_SUCCESS; +} + +/***************************************************************************** + * A3700 handler called when a power domain is about to be turned off. The + * target_state encodes the power state that each level should transition to. + ***************************************************************************** + */ +void a3700_pwr_domain_off(const psci_power_state_t *target_state) +{ + /* Prevent interrupts from spuriously waking up this cpu */ + plat_marvell_gic_cpuif_disable(); + + /* Core can not be powered down with pending IRQ, + * acknowledge all the pending IRQ + */ + a3700_pm_ack_irq(); +} + +static void a3700_set_gen_pwr_off_option(void) +{ + /* Enable L2 flush -> processor state-machine option */ + mmio_setbits_32(MVEBU_PM_NB_CPU_PWR_CTRL_REG, MVEBU_PM_L2_FLUSH_EN); + + /* + * North bridge cannot be VDD off (always ON). + * The NB state machine support low power mode by its state machine. + * This bit MUST be set for north bridge power down, e.g., + * OSC input cutoff(NOT TEST), SRAM power down, PMIC, etc. + * It is not related to CPU VDD OFF!! + */ + mmio_clrbits_32(MVEBU_PM_NB_PWR_OPTION_REG, MVEBU_PM_CPU_VDDV_OFF_EN); + + /* + * MUST: Switch CPU/AXI clock to OSC + * NB state machine clock is always connected to OSC (slow clock). + * But Core0/1/processor state machine's clock are connected to AXI + * clock. Now, AXI clock takes the TBG as clock source. + * If using AXI clock, Core0/1/processor state machine may much faster + * than NB state machine. It will cause problem in this case if cores + * are released before north bridge gets ready. + */ + mmio_clrbits_32(MVEBU_NB_CLOCK_SEL_REG, MVEBU_A53_CPU_CLK_SEL); + + /* + * These register bits will trigger north bridge + * power-down state machine regardless CM3 status. + */ + mmio_setbits_32(MVEBU_PM_NB_PWR_DEBUG_REG, MVEBU_PM_IGNORE_CM3_SLEEP); + mmio_setbits_32(MVEBU_PM_NB_PWR_DEBUG_REG, MVEBU_PM_IGNORE_CM3_DEEP); + + /* + * SRAM => controlled by north bridge state machine. + * Core VDD OFF is not related to CPU SRAM power down. + */ + mmio_setbits_32(MVEBU_PM_NB_PWR_OPTION_REG, MVEBU_PM_NB_SRAM_LKG_PD_EN); + + /* + * Idle AXI interface in order to get L2_WFI + * L2 WFI is only asserted after CORE-0 and CORE-1 WFI asserted. + * (only both core-0/1in WFI, L2 WFI will be issued by CORE.) + * Once L2 WFI asserted, this bit is used for signalling assertion + * to AXI IO masters. + */ + mmio_setbits_32(MVEBU_PM_NB_PWR_CTRL_REG, MVEBU_PM_INTERFACE_IDLE); + + /* Enable core0 and core1 VDD_OFF */ + mmio_setbits_32(MVEBU_PM_CPU_0_PWR_CTRL_REG, MVEBU_PM_CORE_PD); + mmio_setbits_32(MVEBU_PM_CPU_1_PWR_CTRL_REG, MVEBU_PM_CORE_PD); + + /* Enable North bridge power down - + * Both Cores MUST enable this bit to power down north bridge! + */ + mmio_setbits_32(MVEBU_PM_CPU_0_PWR_CTRL_REG, MVEBU_PM_CORE_SOC_PD); + mmio_setbits_32(MVEBU_PM_CPU_1_PWR_CTRL_REG, MVEBU_PM_CORE_SOC_PD); + + /* CA53 (processor domain) power down */ + mmio_setbits_32(MVEBU_PM_CPU_0_PWR_CTRL_REG, MVEBU_PM_CORE_PROC_PD); + mmio_setbits_32(MVEBU_PM_CPU_1_PWR_CTRL_REG, MVEBU_PM_CORE_PROC_PD); +} + +static void a3700_en_ddr_self_refresh(void) +{ + /* + * Both count is 16 bits and configurable. By default, osc stb cnt + * is 0xFFF for lower 12 bits. + * Thus, powerdown count is smaller than osc count. + * This count is used for exiting DDR SR mode on wakeup event. + * The powerdown count also has impact on the following + * state changes: idle -> count-down -> ... (power-down, vdd off, etc) + * Here, make stable counter shorter + * Use power down count value instead of osc_stb_cnt to speed up + * DDR self refresh exit + */ + mmio_setbits_32(MVEBU_PM_NB_PWR_CTRL_REG, MVEBU_PM_PWR_DN_CNT_SEL); + + /* + * Enable DDR SR mode => controlled by north bridge state machine + * Therefore, we must powerdown north bridge to trigger the DDR SR + * mode switching. + */ + mmio_setbits_32(MVEBU_PM_NB_PWR_OPTION_REG, MVEBU_PM_DDR_SR_EN); + /* Disable DDR clock, otherwise DDR will not enter into SR mode. */ + mmio_setbits_32(MVEBU_PM_NB_PWR_OPTION_REG, MVEBU_PM_DDR_CLK_DIS_EN); + /* Power down DDR PHY (PAD) */ + mmio_setbits_32(MVEBU_PM_NB_PWR_OPTION_REG, MVEBU_PM_DDRPHY_PWRDWN_EN); + mmio_setbits_32(MVEBU_PM_NB_PWR_OPTION_REG, + MVEBU_PM_DDRPHY_PAD_PWRDWN_EN); + + /* Set wait time for DDR ready in ROM code */ + mmio_write_32(MVEBU_PM_CPU_VDD_OFF_INFO_1_REG, + MVEBU_PM_WAIT_DDR_RDY_VALUE); + + /* DDR flush write buffer - mandatory */ + mmio_write_32(MVEBU_DRAM_CMD_0_REG, MVEBU_DRAM_CH0_CMD0 | + MVEBU_DRAM_CS_CMD0 | MVEBU_DRAM_WCB_DRAIN_REQ); + while ((mmio_read_32(MVEBU_DRAM_STATS_CH0_REG) & + MVEBU_DRAM_WCP_EMPTY) != MVEBU_DRAM_WCP_EMPTY) + ; + + /* Trigger PHY reset after ddr out of self refresh => + * supply reset pulse for DDR phy after wake up + */ + mmio_setbits_32(MVEBU_DRAM_PWR_CTRL_REG, MVEBU_DRAM_PHY_CLK_GATING_EN | + MVEBU_DRAM_PHY_AUTO_AC_OFF_EN); +} + +static void a3700_pwr_dn_avs(void) +{ + /* + * AVS power down - controlled by north bridge statemachine + * Enable AVS power down by clear the AVS disable bit. + */ + mmio_clrbits_32(MVEBU_PM_NB_PWR_OPTION_REG, MVEBU_PM_AVS_DISABLE_MODE); + /* + * Should set BIT[12:13] to powerdown AVS. + * 1. Enable AVS VDD2 mode + * 2. After power down AVS, we must hold AVS output voltage. + * 3. We can choose the lower VDD for AVS power down. + */ + mmio_setbits_32(MVEBU_PM_NB_PWR_OPTION_REG, MVEBU_PM_AVS_VDD2_MODE); + mmio_setbits_32(MVEBU_PM_NB_PWR_OPTION_REG, MVEBU_PM_AVS_HOLD_MODE); + + /* Enable low VDD mode, AVS will set CPU to lowest core VDD 747mV */ + mmio_setbits_32(MVEBU_AVS_CTRL_2_REG, MVEBU_LOW_VDD_MODE_EN); +} + +static void a3700_pwr_dn_tbg(void) +{ + /* Power down TBG */ + mmio_setbits_32(MVEBU_PM_NB_PWR_OPTION_REG, MVEBU_PM_TBG_OFF_EN); +} + +static void a3700_pwr_dn_sb(void) +{ + /* Enable south bridge power down option */ + mmio_setbits_32(MVEBU_PM_NB_PWR_CTRL_REG, MVEBU_PM_SB_PWR_DWN); + + /* Enable SDIO_PHY_PWRDWN */ + mmio_setbits_32(MVEBU_PM_SB_PWR_OPTION_REG, MVEBU_PM_SDIO_PHY_PDWN_EN); + + /* Enable SRAM LRM on SB */ + mmio_setbits_32(MVEBU_PM_SB_PWR_OPTION_REG, MVEBU_PM_SB_SRAM_LKG_PD_EN); + + /* Enable SB Power Off */ + mmio_setbits_32(MVEBU_PM_SB_PWR_OPTION_REG, MVEBU_PM_SB_VDDV_OFF_EN); + + /* Kick off South Bridge Power Off */ + mmio_setbits_32(MVEBU_PM_SB_CPU_PWR_CTRL_REG, MVEBU_PM_SB_PM_START); +} + +static void a3700_set_pwr_off_option(void) +{ + /* Set general power off option */ + a3700_set_gen_pwr_off_option(); + + /* Enable DDR self refresh in low power mode */ + a3700_en_ddr_self_refresh(); + + /* Power down AVS */ + a3700_pwr_dn_avs(); + + /* Power down TBG */ + a3700_pwr_dn_tbg(); + + /* Power down south bridge, pay attention south bridge setting + * should be done before + */ + a3700_pwr_dn_sb(); +} + +static void a3700_set_wake_up_option(void) +{ + /* + * Enable the wakeup event for NB SOC => north-bridge + * state-machine enablement on wake-up event + */ + mmio_setbits_32(MVEBU_PM_NB_WAKE_UP_EN_REG, MVEBU_PM_NB_WKP_EN); + + /* Enable both core0 and core1 wakeup on demand */ + mmio_setbits_32(MVEBU_PM_CPU_WAKE_UP_CONF_REG, + MVEBU_PM_CORE1_WAKEUP | MVEBU_PM_CORE0_WAKEUP); + + /* Enable warm reset in low power mode */ + mmio_setbits_32(MVEBU_PM_NB_PWR_OPTION_REG, MVEBU_PM_WARM_RESET_EN); +} + +static void a3700_pm_en_nb_gpio(uint32_t gpio) +{ + /* For GPIO1 interrupt -- North bridge only */ + if (gpio >= 32) { + /* GPIO int mask */ + mmio_clrbits_32(MVEBU_NB_GPIO_IRQ_MASK_2_REG, BIT(gpio - 32)); + + /* NB_CPU_WAKE-up ENABLE GPIO int */ + mmio_setbits_32(MVEBU_NB_GPIO_IRQ_EN_HIGH_REG, BIT(gpio - 32)); + } else { + /* GPIO int mask */ + mmio_clrbits_32(MVEBU_NB_GPIO_IRQ_MASK_1_REG, BIT(gpio)); + + /* NB_CPU_WAKE-up ENABLE GPIO int */ + mmio_setbits_32(MVEBU_NB_GPIO_IRQ_EN_LOW_REG, BIT(gpio)); + } + + mmio_setbits_32(MVEBU_NB_STEP_DOWN_INT_EN_REG, + MVEBU_NB_GPIO_INT_WAKE_WCPU_CLK); + + /* Enable using GPIO as wakeup event + * (actually not only for north bridge) + */ + mmio_setbits_32(MVEBU_PM_NB_WAKE_UP_EN_REG, MVEBU_PM_NB_GPIO_WKP_EN | + MVEBU_PM_NB_WKP_EN | MVEBU_PM_CORE1_FIQ_IRQ_WKP_EN | + MVEBU_PM_CORE0_FIQ_IRQ_WKP_EN); +} + +static void a3700_pm_en_sb_gpio(uint32_t gpio) +{ + /* Enable using GPIO as wakeup event */ + mmio_setbits_32(MVEBU_PM_NB_WAKE_UP_EN_REG, MVEBU_PM_SB_WKP_NB_EN | + MVEBU_PM_NB_WKP_EN | MVEBU_PM_CORE1_FIQ_IRQ_WKP_EN | + MVEBU_PM_CORE0_FIQ_IRQ_WKP_EN); + + /* SB GPIO Wake UP | South Bridge Wake Up Enable */ + mmio_setbits_32(MVEBU_PM_SB_WK_EN_REG, MVEBU_PM_SB_GPIO_WKP_EN | + MVEBU_PM_SB_GPIO_WKP_EN); + + /* GPIO int mask */ + mmio_clrbits_32(MVEBU_SB_GPIO_IRQ_MASK_REG, BIT(gpio)); + + /* NB_CPU_WAKE-up ENABLE GPIO int */ + mmio_setbits_32(MVEBU_SB_GPIO_IRQ_EN_REG, BIT(gpio)); +} + +int a3700_pm_src_gpio(union pm_wake_up_src_data *src_data) +{ + if (src_data->gpio_data.bank_num == 0) + /* North Bridge GPIO */ + a3700_pm_en_nb_gpio(src_data->gpio_data.gpio_num); + else + a3700_pm_en_sb_gpio(src_data->gpio_data.gpio_num); + return 0; +} + +int a3700_pm_src_uart1(union pm_wake_up_src_data *src_data) +{ + /* Clear Uart1 select */ + mmio_clrbits_32(MVEBU_NB_GPIO1_SEL_REG, MVEBU_NB_GPIO1_UART1_SEL); + /* set pin 19 gpio usage*/ + mmio_setbits_32(MVEBU_NB_GPIO1_SEL_REG, MVEBU_NB_GPIO1_GPIO_19_EN); + /* Enable gpio wake-up*/ + a3700_pm_en_nb_gpio(MVEBU_NB_GPIO_19); + /* set pin 18 gpio usage*/ + mmio_setbits_32(MVEBU_NB_GPIO1_SEL_REG, MVEBU_NB_GPIO1_GPIO_18_EN); + /* Enable gpio wake-up*/ + a3700_pm_en_nb_gpio(MVEBU_NB_GPIO_18); + + return 0; +} + +int a3700_pm_src_uart0(union pm_wake_up_src_data *src_data) +{ + /* set pin 25/26 gpio usage*/ + mmio_setbits_32(MVEBU_NB_GPIO1_SEL_REG, MVEBU_NB_GPIO1_GPIO_25_26_EN); + /* Enable gpio wake-up*/ + a3700_pm_en_nb_gpio(MVEBU_NB_GPIO_25); + /* Enable gpio wake-up*/ + a3700_pm_en_nb_gpio(MVEBU_NB_GPIO_26); + + return 0; +} + +struct wake_up_src_func_map src_func_table[WAKE_UP_SRC_MAX] = { + {WAKE_UP_SRC_GPIO, a3700_pm_src_gpio}, + {WAKE_UP_SRC_UART1, a3700_pm_src_uart1}, + {WAKE_UP_SRC_UART0, a3700_pm_src_uart0}, + /* FOLLOWING SRC NOT SUPPORTED YET */ + {WAKE_UP_SRC_TIMER, NULL} +}; + +static wake_up_src_func a3700_get_wake_up_src_func( + enum pm_wake_up_src_type type) +{ + uint32_t loop; + + for (loop = 0; loop < WAKE_UP_SRC_MAX; loop++) { + if (src_func_table[loop].type == type) + return src_func_table[loop].func; + } + return NULL; +} + +#pragma weak mv_wake_up_src_config_get +struct pm_wake_up_src_config *mv_wake_up_src_config_get(void) +{ + static struct pm_wake_up_src_config wake_up_src_cfg = {}; + return &wake_up_src_cfg; +} + +static void a3700_set_wake_up_source(void) +{ + struct pm_wake_up_src_config *wake_up_src; + uint32_t loop; + wake_up_src_func src_func = NULL; + + wake_up_src = mv_wake_up_src_config_get(); + for (loop = 0; loop < wake_up_src->wake_up_src_num; loop++) { + src_func = a3700_get_wake_up_src_func( + wake_up_src->wake_up_src[loop].wake_up_src_type); + if (src_func) + src_func( + &(wake_up_src->wake_up_src[loop].wake_up_data)); + } +} + +static void a3700_pm_save_lp_flag(void) +{ + /* Save the flag for enter the low power mode */ + mmio_setbits_32(MVEBU_PM_CPU_VDD_OFF_INFO_2_REG, + MVEBU_PM_LOW_POWER_STATE); +} + +static void a3700_pm_clear_lp_flag(void) +{ + /* Clear the flag for enter the low power mode */ + mmio_clrbits_32(MVEBU_PM_CPU_VDD_OFF_INFO_2_REG, + MVEBU_PM_LOW_POWER_STATE); +} + +static uint32_t a3700_pm_get_lp_flag(void) +{ + /* Get the flag for enter the low power mode */ + return mmio_read_32(MVEBU_PM_CPU_VDD_OFF_INFO_2_REG) & + MVEBU_PM_LOW_POWER_STATE; +} + +/***************************************************************************** + * A3700 handler called when a power domain is about to be suspended. The + * target_state encodes the power state that each level should transition to. + ***************************************************************************** + */ +void a3700_pwr_domain_suspend(const psci_power_state_t *target_state) +{ + /* Prevent interrupts from spuriously waking up this cpu */ + plat_marvell_gic_cpuif_disable(); + + /* Save IRQ states */ + plat_marvell_gic_irq_save(); + + /* Set wake up options */ + a3700_set_wake_up_option(); + + /* Set wake up sources */ + a3700_set_wake_up_source(); + + /* SoC can not be powered down with pending IRQ, + * acknowledge all the pending IRQ + */ + a3700_pm_ack_irq(); + + /* Set power off options */ + a3700_set_pwr_off_option(); + + /* Save the flag for enter the low power mode */ + a3700_pm_save_lp_flag(); + + isb(); +} + +/***************************************************************************** + * A3700 handler called when a power domain has just been powered on after + * being turned off earlier. The target_state encodes the low power state that + * each level has woken up from. + ***************************************************************************** + */ +void a3700_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + /* arch specific configuration */ + marvell_psci_arch_init(0); + + /* Per-CPU interrupt initialization */ + plat_marvell_gic_pcpu_init(); + plat_marvell_gic_cpuif_enable(); + + /* Restore the per-cpu IRQ state */ + if (a3700_pm_get_lp_flag()) + plat_marvell_gic_irq_pcpu_restore(); +} + +/***************************************************************************** + * A3700 handler called when a power domain has just been powered on after + * having been suspended earlier. The target_state encodes the low power state + * that each level has woken up from. + * TODO: At the moment we reuse the on finisher and reinitialize the secure + * context. Need to implement a separate suspend finisher. + ***************************************************************************** + */ +void a3700_pwr_domain_suspend_finish(const psci_power_state_t *target_state) +{ + struct dec_win_config *io_dec_map; + uint32_t dec_win_num; + struct dram_win_map dram_wins_map; + + /* arch specific configuration */ + marvell_psci_arch_init(0); + + /* Interrupt initialization */ + plat_marvell_gic_init(); + + /* Restore IRQ states */ + plat_marvell_gic_irq_restore(); + + /* + * Initialize CCI for this cluster after resume from suspend state. + * No need for locks as no other CPU is active. + */ + plat_marvell_interconnect_init(); + /* + * Enable CCI coherency for the primary CPU's cluster. + * Platform specific PSCI code will enable coherency for other + * clusters. + */ + plat_marvell_interconnect_enter_coherency(); + + /* CPU address decoder windows initialization. */ + cpu_wins_init(); + + /* fetch CPU-DRAM window mapping information by reading + * CPU-DRAM decode windows (only the enabled ones) + */ + dram_win_map_build(&dram_wins_map); + + /* Get IO address decoder windows */ + if (marvell_get_io_dec_win_conf(&io_dec_map, &dec_win_num)) { + printf("No IO address decoder windows configurations found!\n"); + return; + } + + /* IO address decoder init */ + if (init_io_addr_dec(&dram_wins_map, io_dec_map, dec_win_num)) { + printf("IO address decoder windows initialization failed!\n"); + return; + } + + /* Clear low power mode flag */ + a3700_pm_clear_lp_flag(); +} + +/***************************************************************************** + * This handler is called by the PSCI implementation during the `SYSTEM_SUSPEND + * call to get the `power_state` parameter. This allows the platform to encode + * the appropriate State-ID field within the `power_state` parameter which can + * be utilized in `pwr_domain_suspend()` to suspend to system affinity level. + ***************************************************************************** + */ +void a3700_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + /* lower affinities use PLAT_MAX_OFF_STATE */ + for (int i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) + req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; +} + +/***************************************************************************** + * A3700 handlers to shutdown/reboot the system + ***************************************************************************** + */ +static void __dead2 a3700_system_off(void) +{ + ERROR("%s needs to be implemented\n", __func__); + panic(); +} + +#pragma weak cm3_system_reset +void cm3_system_reset(void) +{ +} + +/***************************************************************************** + * A3700 handlers to reset the system + ***************************************************************************** + */ +static void __dead2 a3700_system_reset(void) +{ + /* Clean the mailbox magic number to let it as act like cold boot */ + mmio_write_32(PLAT_MARVELL_MAILBOX_BASE, 0x0); + + dsbsy(); + + /* Flush data cache if the mail box shared RAM is cached */ +#if PLAT_MARVELL_SHARED_RAM_CACHED + flush_dcache_range((uintptr_t)PLAT_MARVELL_MAILBOX_BASE, + 2 * sizeof(uint64_t)); +#endif + + /* Use Cortex-M3 secure coprocessor for system reset */ + cm3_system_reset(); + + /* Trigger the warm reset */ + mmio_write_32(MVEBU_WARM_RESET_REG, MVEBU_WARM_RESET_MAGIC); + + /* Shouldn't get to this point */ + panic(); +} + +/***************************************************************************** + * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard + * platform layer will take care of registering the handlers with PSCI. + ***************************************************************************** + */ +const plat_psci_ops_t plat_arm_psci_pm_ops = { + .cpu_standby = a3700_cpu_standby, + .pwr_domain_on = a3700_pwr_domain_on, + .pwr_domain_off = a3700_pwr_domain_off, + .pwr_domain_suspend = a3700_pwr_domain_suspend, + .pwr_domain_on_finish = a3700_pwr_domain_on_finish, + .pwr_domain_suspend_finish = a3700_pwr_domain_suspend_finish, + .get_sys_suspend_power_state = a3700_get_sys_suspend_power_state, + .system_off = a3700_system_off, + .system_reset = a3700_system_reset, + .validate_power_state = a3700_validate_power_state, + .validate_ns_entrypoint = a3700_validate_ns_entrypoint +}; |