diff options
Diffstat (limited to 'plat/marvell/armada/a8k/common')
25 files changed, 3548 insertions, 0 deletions
diff --git a/plat/marvell/armada/a8k/common/a8k_common.mk b/plat/marvell/armada/a8k/common/a8k_common.mk new file mode 100644 index 0000000..4d8a87f --- /dev/null +++ b/plat/marvell/armada/a8k/common/a8k_common.mk @@ -0,0 +1,192 @@ +# +# Copyright (C) 2016 - 2020 Marvell International Ltd. +# +# SPDX-License-Identifier: BSD-3-Clause +# https://spdx.org/licenses + +PLAT_FAMILY := a8k +PLAT_INCLUDE_BASE := include/plat/marvell/armada/$(PLAT_FAMILY) +PLAT_COMMON_BASE := plat/marvell/armada/a8k/common +MARVELL_DRV_BASE := drivers/marvell +MARVELL_COMMON_BASE := plat/marvell/armada/common + +MARVELL_SVC_TEST := 0 +$(eval $(call add_define,MARVELL_SVC_TEST)) + +ERRATA_A72_859971 := 1 + +# Enable MSS support for a8k family +MSS_SUPPORT := 1 +$(eval $(call add_define,MSS_SUPPORT)) + +# Disable EL3 cache for power management +BL31_CACHE_DISABLE := 0 +$(eval $(call add_define,BL31_CACHE_DISABLE)) + +$(eval $(call add_define,PCI_EP_SUPPORT)) +$(eval $(call assert_boolean,PCI_EP_SUPPORT)) + +AP_NUM := 1 +$(eval $(call add_define,AP_NUM)) + +DOIMAGEPATH ?= tools/marvell/doimage +DOIMAGETOOL ?= ${DOIMAGEPATH}/doimage + +include plat/marvell/marvell.mk +include tools/marvell/doimage/doimage.mk + +ifeq (${MARVELL_SECURE_BOOT},1) +DOIMAGE_SEC_FLAGS := -c $(DOIMAGE_SEC) +DOIMAGE_LIBS_CHECK = \ + if ! [ -d "/usr/include/mbedtls" ]; then \ + echo "****************************************" >&2; \ + echo "Missing mbedTLS installation! " >&2; \ + echo "Please download it from \"tls.mbed.org\"" >&2; \ + echo "Alternatively on Debian/Ubuntu system install" >&2; \ + echo "\"libmbedtls-dev\" package" >&2; \ + echo "Make sure to use version 2.1.0 or later" >&2; \ + echo "****************************************" >&2; \ + exit 1; \ + else if ! [ -f "/usr/include/libconfig.h" ]; then \ + echo "********************************************************" >&2; \ + echo "Missing Libconfig installation!" >&2; \ + echo "Please download it from \"www.hyperrealm.com/libconfig/\"" >&2; \ + echo "Alternatively on Debian/Ubuntu system install packages" >&2; \ + echo "\"libconfig8\" and \"libconfig8-dev\"" >&2; \ + echo "********************************************************" >&2; \ + exit 1; \ + fi \ + fi +else #MARVELL_SECURE_BOOT +DOIMAGE_LIBS_CHECK = +DOIMAGE_SEC_FLAGS = +endif #MARVELL_SECURE_BOOT + +ROM_BIN_EXT ?= $(BUILD_PLAT)/ble.bin +DOIMAGE_FLAGS += -b $(ROM_BIN_EXT) $(NAND_DOIMAGE_FLAGS) $(DOIMAGE_SEC_FLAGS) + +# Check whether to build system_power.c for the platform +ifneq ("$(wildcard $(BOARD_DIR)/board/system_power.c)","") +SYSTEM_POWER_SUPPORT = 1 +else +SYSTEM_POWER_SUPPORT = 0 +endif + +# This define specifies DDR type for BLE +$(eval $(call add_define,CONFIG_DDR4)) + +# This define specifies DDR topology for BLE +DDR_TOPOLOGY ?= 0 +$(eval $(call add_define,DDR_TOPOLOGY)) + +MARVELL_GIC_SOURCES := drivers/arm/gic/common/gic_common.c \ + drivers/arm/gic/v2/gicv2_main.c \ + drivers/arm/gic/v2/gicv2_helpers.c \ + plat/common/plat_gicv2.c + +PLAT_INCLUDES += -I$(BOARD_DIR) \ + -I$(BOARD_DIR)/board \ + -I$(CURDIR)/drivers/marvell \ + -I$(PLAT_COMMON_BASE)/include \ + -I$(PLAT_INCLUDE_BASE)/common + +PLAT_BL_COMMON_SOURCES := $(PLAT_COMMON_BASE)/aarch64/a8k_common.c \ + drivers/ti/uart/aarch64/16550_console.S + +ifndef BLE_PORTING_SOURCES +BLE_PORTING_SOURCES := $(BOARD_DIR)/board/dram_port.c \ + $(BOARD_DIR)/board/marvell_plat_config.c +endif + +MARVELL_MOCHI_DRV += $(MARVELL_DRV_BASE)/mochi/cp110_setup.c + +BLE_SOURCES := drivers/mentor/i2c/mi2cv.c \ + $(PLAT_COMMON_BASE)/plat_ble_setup.c \ + $(MARVELL_MOCHI_DRV) \ + $(PLAT_COMMON_BASE)/plat_pm.c \ + $(MARVELL_DRV_BASE)/ap807_clocks_init.c \ + $(MARVELL_DRV_BASE)/thermal.c \ + $(PLAT_COMMON_BASE)/plat_thermal.c \ + $(BLE_PORTING_SOURCES) \ + $(MARVELL_DRV_BASE)/ccu.c \ + $(MARVELL_DRV_BASE)/io_win.c + +BL1_SOURCES += $(PLAT_COMMON_BASE)/aarch64/plat_helpers.S \ + lib/cpus/aarch64/cortex_a72.S + +MARVELL_DRV := $(MARVELL_DRV_BASE)/io_win.c \ + $(MARVELL_DRV_BASE)/iob.c \ + $(MARVELL_DRV_BASE)/mci.c \ + $(MARVELL_DRV_BASE)/amb_adec.c \ + $(MARVELL_DRV_BASE)/ccu.c \ + $(MARVELL_DRV_BASE)/cache_llc.c \ + $(MARVELL_DRV_BASE)/comphy/phy-comphy-cp110.c \ + $(MARVELL_DRV_BASE)/mc_trustzone/mc_trustzone.c \ + $(MARVELL_DRV_BASE)/secure_dfx_access/armada_thermal.c \ + $(MARVELL_DRV_BASE)/secure_dfx_access/misc_dfx.c \ + $(MARVELL_DRV_BASE)/ddr_phy_access.c \ + drivers/rambus/trng_ip_76.c + +ifeq (${MSS_SUPPORT}, 1) +MARVELL_DRV += $(MARVELL_DRV_BASE)/mg_conf_cm3/mg_conf_cm3.c +endif + +ifndef BL31_PORTING_SOURCES +BL31_PORTING_SOURCES := $(BOARD_DIR)/board/marvell_plat_config.c +endif + +ifeq ($(SYSTEM_POWER_SUPPORT),1) +BL31_PORTING_SOURCES += $(BOARD_DIR)/board/system_power.c +endif + +BL31_SOURCES += lib/cpus/aarch64/cortex_a72.S \ + $(PLAT_COMMON_BASE)/aarch64/plat_helpers.S \ + $(PLAT_COMMON_BASE)/aarch64/plat_arch_config.c \ + $(PLAT_COMMON_BASE)/plat_pm.c \ + $(PLAT_COMMON_BASE)/plat_bl31_setup.c \ + $(MARVELL_COMMON_BASE)/marvell_gicv2.c \ + $(MARVELL_COMMON_BASE)/mrvl_sip_svc.c \ + $(MARVELL_COMMON_BASE)/marvell_ddr_info.c \ + $(BL31_PORTING_SOURCES) \ + $(MARVELL_DRV) \ + $(MARVELL_MOCHI_DRV) \ + $(MARVELL_GIC_SOURCES) + +# Add trace functionality for PM +BL31_SOURCES += $(PLAT_COMMON_BASE)/plat_pm_trace.c + + +ifeq (${MSS_SUPPORT}, 1) +# Force builds with BL2 image on a80x0 platforms +ifndef SCP_BL2 + $(error "Error: SCP_BL2 image is mandatory for a8k family") +endif + +# MSS (SCP) build +include $(PLAT_COMMON_BASE)/mss/mss_a8k.mk +endif + +# BLE (ROM context execution code, AKA binary extension) +BLE_PATH ?= $(PLAT_COMMON_BASE)/ble + +include ${BLE_PATH}/ble.mk +$(eval $(call MAKE_BL,ble)) + +clean realclean distclean: mrvl_clean + +.PHONY: mrvl_clean +mrvl_clean: + @echo " Doimage CLEAN" + ${Q}${MAKE} PLAT=${PLAT} --no-print-directory -C ${DOIMAGEPATH} clean + +${DOIMAGETOOL}: FORCE + @$(DOIMAGE_LIBS_CHECK) + ${Q}${MAKE} --no-print-directory -C ${DOIMAGEPATH} + +${BUILD_PLAT}/${FLASH_IMAGE}: ${ROM_BIN_EXT} ${BUILD_PLAT}/${BOOT_IMAGE} ${DOIMAGETOOL} + @${ECHO_BLANK_LINE} + @echo "Building flash image" + ${Q}${DOIMAGETOOL} ${DOIMAGE_FLAGS} ${BUILD_PLAT}/${BOOT_IMAGE} ${BUILD_PLAT}/${FLASH_IMAGE} + @${ECHO_BLANK_LINE} + @echo "Built $@ successfully" + @${ECHO_BLANK_LINE} diff --git a/plat/marvell/armada/a8k/common/aarch64/a8k_common.c b/plat/marvell/armada/a8k/common/aarch64/a8k_common.c new file mode 100644 index 0000000..4332a76 --- /dev/null +++ b/plat/marvell/armada/a8k/common/aarch64/a8k_common.c @@ -0,0 +1,70 @@ +/* + * 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_SECURE_RAM, + MAP_DEVICE0, + {0} +}; +#endif +#if IMAGE_BL2 +const mmap_region_t plat_marvell_mmap[] = { + MARVELL_MAP_SECURE_RAM, + MAP_DEVICE0, + MARVELL_MAP_DRAM, +#ifdef SPD_opteed + MARVELL_MAP_OPTEE_CORE_MEM, + MARVELL_OPTEE_PAGEABLE_LOAD_MEM, +#endif + {0} +}; +#endif + +#if IMAGE_BL2U +const mmap_region_t plat_marvell_mmap[] = { + MARVELL_MAP_SECURE_RAM, + MAP_DEVICE0, + {0} +}; +#endif + +#if IMAGE_BLE +const mmap_region_t plat_marvell_mmap[] = { + MAP_DEVICE0, + {0} +}; +#endif + +#if IMAGE_BL31 +const mmap_region_t plat_marvell_mmap[] = { + MARVELL_MAP_SECURE_RAM, + MAP_DEVICE0, + MARVELL_MAP_DRAM, + {0} +}; +#endif +#if IMAGE_BL32 +const mmap_region_t plat_marvell_mmap[] = { + MARVELL_MAP_SECURE_RAM, + MAP_DEVICE0, + {0} +}; +#endif + +MARVELL_CASSERT_MMAP; diff --git a/plat/marvell/armada/a8k/common/aarch64/plat_arch_config.c b/plat/marvell/armada/a8k/common/aarch64/plat_arch_config.c new file mode 100644 index 0000000..d576514 --- /dev/null +++ b/plat/marvell/armada/a8k/common/aarch64/plat_arch_config.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/marvell/cache_llc.h> +#include <lib/mmio.h> +#include <plat/common/platform.h> + +#define CCU_HTC_ASET (MVEBU_CCU_BASE(MVEBU_AP0) + 0x264) +#define MVEBU_IO_AFFINITY (0xF00) +#define MVEBU_SF_REG (MVEBU_REGS_BASE + 0x40) +#define MVEBU_SF_EN BIT(8) +#define MVEBU_DFX_REG(cluster_id) (MVEBU_REGS_BASE + 0x6F82A0 + \ + (cluster_id) * 0x4) +#define MVEBU_DFX_CLK_EN_POS 0x3 +#define MVEBU_DFX_CL0_CLK_OFFS 16 +#define MVEBU_DFX_CL0_CLK_MASK (0xF << MVEBU_DFX_CL0_CLK_OFFS) +#define MVEBU_DFX_CL1_CLK_OFFS 8 +#define MVEBU_DFX_CL1_CLK_MASK (0xF << MVEBU_DFX_CL1_CLK_OFFS) + +#ifdef MVEBU_SOC_AP807 +static void plat_enable_snoop_filter(void) +{ + int cpu_id = plat_my_core_pos(); + + /* Snoop filter needs to be enabled once per cluster */ + if (cpu_id % 2) + return; + + mmio_setbits_32(MVEBU_SF_REG, MVEBU_SF_EN); +} +#endif + +#ifndef MVEBU_SOC_AP807 +static void plat_config_dfx_clock(void) +{ + int cluster_id = plat_my_core_pos(); + uint32_t val; + + /* DFX clock needs to be configured once per cluster */ + if ((cluster_id % PLAT_MAX_CPUS_PER_CLUSTER) != 0) { + return; + } + + val = mmio_read_32(MVEBU_DFX_REG(cluster_id / PLAT_MAX_CPUS_PER_CLUSTER)); + if (cluster_id == 0) { + val &= ~MVEBU_DFX_CL0_CLK_MASK; + val |= (MVEBU_DFX_CLK_EN_POS << MVEBU_DFX_CL0_CLK_OFFS); + } else { + val &= ~MVEBU_DFX_CL1_CLK_MASK; + val |= (MVEBU_DFX_CLK_EN_POS << MVEBU_DFX_CL1_CLK_OFFS); + } + mmio_write_32(MVEBU_DFX_REG(cluster_id / PLAT_MAX_CPUS_PER_CLUSTER), val); +} +#endif + +static void plat_enable_affinity(void) +{ + int cluster_id; + int affinity; + + /* set CPU Affinity */ + cluster_id = plat_my_core_pos() / PLAT_MARVELL_CLUSTER_CORE_COUNT; + affinity = (MVEBU_IO_AFFINITY | (1 << cluster_id)); + mmio_write_32(CCU_HTC_ASET, affinity); + + /* set barier */ + isb(); +} + +void marvell_psci_arch_init(int die_index) +{ +#if LLC_ENABLE + /* check if LLC is in exclusive mode + * as L2 is configured to UniqueClean eviction + * (in a8k reset handler) + */ + if (llc_is_exclusive(0) == 0) + ERROR("LLC should be configured to exclusice mode\n"); +#endif + + /* Enable Affinity */ + plat_enable_affinity(); + +#ifdef MVEBU_SOC_AP807 + plat_enable_snoop_filter(); +#else + plat_config_dfx_clock(); +#endif +} diff --git a/plat/marvell/armada/a8k/common/aarch64/plat_helpers.S b/plat/marvell/armada/a8k/common/aarch64/plat_helpers.S new file mode 100644 index 0000000..fadc4c2 --- /dev/null +++ b/plat/marvell/armada/a8k/common/aarch64/plat_helpers.S @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <asm_macros.S> +#include <platform_def.h> +#include <marvell_pm.h> + + .globl plat_secondary_cold_boot_setup + .globl plat_get_my_entrypoint + .globl plat_is_my_cpu_primary + .globl plat_reset_handler + + /* ----------------------------------------------------- + * 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 a cold and warm boot + * For a cold boot, return 0. + * For a warm boot, read the mailbox and return the address it contains. + * + * --------------------------------------------------------------------- + */ +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, MVEBU_MAILBOX_MAGIC_NUM + cmp x1, x2 + beq warm_boot /* If compare failed, return 0, i.e. cold boot */ + mov x0, #0 + ret +warm_boot: + mov_imm x1, MBOX_IDX_SEC_ADDR /* Get the jump address */ + subs x1, x1, #1 + mov x2, #(MBOX_IDX_SEC_ADDR * 8) + lsl x3, x2, x1 + add x0, x0, x3 + 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 + + /* ----------------------------------------------------- + * void plat_reset_handler (void); + * + * Platform specific configuration right after cpu is + * is our of reset. + * + * The plat_reset_handler can clobber x0 - x18, x30. + * ----------------------------------------------------- + */ +func plat_reset_handler + /* + * Note: the configurations below should be done before MMU, + * I Cache and L2are enabled. + * The reset handler is executed right after reset + * and before Caches are enabled. + */ + + /* Enable L1/L2 ECC and Parity */ + mrs x5, s3_1_c11_c0_2 /* L2 Ctrl */ + orr x5, x5, #(1 << 21) /* Enable L1/L2 cache ECC & Parity */ + msr s3_1_c11_c0_2, x5 /* L2 Ctrl */ + +#if LLC_ENABLE + /* + * Enable L2 UniqueClean evictions + * Note: this configuration assumes that LLC is configured + * in exclusive mode. + * Later on in the code this assumption will be validated + */ + mrs x5, s3_1_c15_c0_0 /* L2 Ctrl */ + orr x5, x5, #(1 << 14) /* Enable UniqueClean evictions with data */ + msr s3_1_c15_c0_0, x5 /* L2 Ctrl */ +#endif + + /* Instruction Barrier to allow msr command completion */ + isb + + ret +endfunc plat_reset_handler diff --git a/plat/marvell/armada/a8k/common/ble/ble.ld.S b/plat/marvell/armada/a8k/common/ble/ble.ld.S new file mode 100644 index 0000000..d7a0592 --- /dev/null +++ b/plat/marvell/armada/a8k/common/ble/ble.ld.S @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <platform_def.h> + +OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT) +OUTPUT_ARCH(PLATFORM_LINKER_ARCH) +ENTRY(ble_main) + +MEMORY { + RAM (rwx): ORIGIN = BLE_BASE, LENGTH = BLE_LIMIT - BLE_BASE +} + +SECTIONS +{ + . = BLE_BASE; + + ro . : { + __RO_START__ = .; + *ble_main.o(.entry*) + *(.text*) + *(.rodata*) + __RO_END_UNALIGNED__ = .; + __RO_END__ = .; + } >RAM + + /* + * Define a linker symbol to mark start of the RW memory area for this + * image. + */ + __RW_START__ = . ; + + .data . : { + __DATA_START__ = .; + *(.data*) + __DATA_END__ = .; + } >RAM + + stacks . (NOLOAD) : { + __STACKS_START__ = .; + *(tzfw_normal_stacks) + __STACKS_END__ = .; + } >RAM + + .bss : { + __BSS_START__ = .; + *(.bss*) + __BSS_END__ = .; + } >RAM + + /* + * Extend the BLE binary to the maximum size allocated for it in platform + * definition files and prevent overlapping between BLE BSS section and + * additional extensions that can follow the BLE in flash image preamble. + * This situation happens for instance when secure extension is added to + * the image preamble. + */ + .fill LOADADDR(.bss) + SIZEOF(.bss) : { + FILL(0xDEADC0DE); + . = ORIGIN(RAM) + LENGTH(RAM) - 1; + BYTE(0x00) + } >RAM + + /* + * Define a linker symbol to mark end of the RW memory area for this + * image. + */ + __RW_END__ = .; + __BLE_END__ = .; + + __BSS_SIZE__ = SIZEOF(.bss); +} diff --git a/plat/marvell/armada/a8k/common/ble/ble.mk b/plat/marvell/armada/a8k/common/ble/ble.mk new file mode 100644 index 0000000..160e98f --- /dev/null +++ b/plat/marvell/armada/a8k/common/ble/ble.mk @@ -0,0 +1,35 @@ +# Copyright (C) 2018 Marvell International Ltd. +# +# SPDX-License-Identifier: BSD-3-Clause +# https://spdx.org/licenses + +MV_DDR_LIB = $(BUILD_PLAT)/ble/mv_ddr_lib.a +LIBC_LIB = $(BUILD_PLAT)/lib/libc.a +BLE_LIBS = $(MV_DDR_LIB) $(LIBC_LIB) +PLAT_MARVELL = plat/marvell/armada + +BLE_SOURCES += $(BLE_PATH)/ble_main.c \ + $(BLE_PATH)/ble_mem.S \ + drivers/delay_timer/delay_timer.c \ + drivers/marvell/iob.c \ + $(PLAT_MARVELL)/common/aarch64/marvell_helpers.S \ + $(PLAT_MARVELL)/common/plat_delay_timer.c \ + $(PLAT_MARVELL)/common/marvell_console.c + +MV_DDR_INCLUDES := -I$(CURDIR)/include \ + -I$(CURDIR)/include/arch/aarch64 \ + -I$(CURDIR)/include/lib/libc \ + -I$(CURDIR)/include/lib/libc/aarch64 + +BLE_LINKERFILE := $(BLE_PATH)/ble.ld.S + +BLE_OBJS := $(addprefix $(BUILD_PLAT)/ble/,$(call SOURCES_TO_OBJS,$(BLE_SOURCES))) +$(BLE_OBJS): PLAT_INCLUDES += -I$(MV_DDR_PATH) +$(BLE_OBJS): $(MV_DDR_LIB) + +$(MV_DDR_LIB): 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 BLE 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")) + @+make -C $(MV_DDR_PATH) --no-print-directory PLAT_INCLUDES="$(MV_DDR_INCLUDES)" PLATFORM=$(PLAT) ARCH=AARCH64 OBJ_DIR=$(BUILD_PLAT)/ble diff --git a/plat/marvell/armada/a8k/common/ble/ble_main.c b/plat/marvell/armada/a8k/common/ble/ble_main.c new file mode 100644 index 0000000..5b3acec --- /dev/null +++ b/plat/marvell/armada/a8k/common/ble/ble_main.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <string.h> + +#include <platform_def.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/console.h> + +#include <marvell_plat_priv.h> +#include <marvell_pm.h> +#include <plat_marvell.h> + +#define BR_FLAG_SILENT 0x1 +#define SKIP_IMAGE_CODE 0xDEADB002 + +void mailbox_clean(void) +{ + uintptr_t *mailbox = (void *)PLAT_MARVELL_MAILBOX_BASE; + + memset(mailbox, 0, PLAT_MARVELL_MAILBOX_SIZE); +} + +int exec_ble_main(int bootrom_flags) +{ + int skip = 0; + uintptr_t *mailbox = (void *)PLAT_MARVELL_MAILBOX_BASE; + + /* + * In some situations, like boot from UART, bootrom will + * request to avoid printing to console. in that case don't + * initialize the console and prints will be ignored + */ + if ((bootrom_flags & BR_FLAG_SILENT) == 0) + marvell_console_boot_init(); + + NOTICE("Starting binary extension\n"); + + /* initialize time (for delay functionality) */ + plat_delay_timer_init(); + + ble_plat_setup(&skip); + + /* if there's skip image request, bootrom will load from the image + * saved on the next address of the flash + */ + if (skip) + return SKIP_IMAGE_CODE; + + /* + * Check if the mailbox magic number is stored at index MBOX_IDX_MAGIC + * and the suspend to RAM magic number at index MBOX_IDX_SUSPEND_MAGIC. + * If the above is true, this is the recovery from suspend to RAM state. + * In such case the mailbox should remain intact, since it stores the + * warm boot jump address to be used by the TF-A in BL31. + * Othervise the mailbox should be cleaned from a garbage data. + */ + if (mailbox[MBOX_IDX_MAGIC] != MVEBU_MAILBOX_MAGIC_NUM || + mailbox[MBOX_IDX_SUSPEND_MAGIC] != MVEBU_MAILBOX_SUSPEND_STATE) { + NOTICE("Cold boot\n"); + mailbox_clean(); + } else { + void (*bootrom_exit)(void) = + (void (*)(void))mailbox[MBOX_IDX_ROM_EXIT_ADDR]; + + INFO("Recovery...\n"); + /* + * If this is recovery from suspend, two things has to be done: + * 1. Define the DRAM region as executable memory for preparing + * jump to TF-A + * 2. Instead of returning control to the BootROM, invalidate + * and flush caches, and continue execution at address stored + * in the mailbox. + * This should be done until the BootROM have a native support + * for the system restore flow. + */ + marvell_ble_prepare_exit(); + bootrom_exit(); + } + + return 0; +} + +/* NOTE: don't notify this function, all code must be added to exec_ble_main + * in order to keep the end of ble_main as a fixed address. + */ +int __attribute__ ((section(".entry"))) ble_main(int bootrom_flags) +{ + volatile int ret; + + ret = exec_ble_main(bootrom_flags); + return ret; +} diff --git a/plat/marvell/armada/a8k/common/ble/ble_mem.S b/plat/marvell/armada/a8k/common/ble/ble_mem.S new file mode 100644 index 0000000..a48d546 --- /dev/null +++ b/plat/marvell/armada/a8k/common/ble/ble_mem.S @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <asm_macros.S> +#include <marvell_def.h> +#include <platform_def.h> + +#define PTE_NON_EXEC_OFF 54 /* XN - eXecute Never bit offset - see VMSAv8-64 */ + + .globl marvell_ble_prepare_exit + +func marvell_ble_prepare_exit + /* + * Read the page table base and set the first page to be executable. + * This is required for jumping to DRAM for further execution. + */ + mrs x0, ttbr0_el3 + ldr x1, [x0] + mov x2, #1 + bic x1, x1, x2, lsl #PTE_NON_EXEC_OFF + str x1, [x0] + tlbi alle3 + dsb sy + isb + ret +endfunc marvell_ble_prepare_exit diff --git a/plat/marvell/armada/a8k/common/include/a8k_plat_def.h b/plat/marvell/armada/a8k/common/include/a8k_plat_def.h new file mode 100644 index 0000000..3a0fd4b --- /dev/null +++ b/plat/marvell/armada/a8k/common/include/a8k_plat_def.h @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#ifndef A8K_PLAT_DEF_H +#define A8K_PLAT_DEF_H + +#include <marvell_def.h> + +#define MVEBU_PRIMARY_CPU 0x0 +#define MVEBU_AP0 0x0 + +/* APN806 revision ID */ +#define MVEBU_CSS_GWD_CTRL_IIDR2_REG (MVEBU_REGS_BASE + 0x610FCC) +#define GWD_IIDR2_REV_ID_OFFSET 12 +#define GWD_IIDR2_REV_ID_MASK 0xF +#define GWD_IIDR2_CHIP_ID_OFFSET 20 +#define GWD_IIDR2_CHIP_ID_MASK (0xFFFu << GWD_IIDR2_CHIP_ID_OFFSET) + +#define CHIP_ID_AP806 0x806 +#define CHIP_ID_AP807 0x807 + +#define COUNTER_FREQUENCY 25000000 + +#define MVEBU_REGS_BASE 0xF0000000 +#define MVEBU_REGS_BASE_MASK 0xF0000000 +#define MVEBU_REGS_BASE_AP(ap) MVEBU_REGS_BASE +#define MVEBU_AP_IO_BASE(ap) 0xF2000000 +#define MVEBU_CP_OFFSET 0x2000000 +#define MVEBU_CP_REGS_BASE(cp_index) (MVEBU_AP_IO_BASE(0) + \ + (cp_index) * MVEBU_CP_OFFSET) +#define MVEBU_RFU_BASE (MVEBU_REGS_BASE + 0x6F0000) +#define MVEBU_IO_WIN_BASE(ap_index) (MVEBU_RFU_BASE) +#define MVEBU_IO_WIN_GCR_OFFSET (0x70) +#define MVEBU_IO_WIN_MAX_WINS (7) + +/* Misc SoC configurations Base */ +#define MVEBU_MISC_SOC_BASE (MVEBU_REGS_BASE + 0x6F4300) + +#define MVEBU_CCU_BASE(ap_index) (MVEBU_REGS_BASE + 0x4000) +#define MVEBU_CCU_MAX_WINS (8) + +#define MVEBU_LLC_BASE(ap_index) (MVEBU_REGS_BASE + 0x8000) +#define MVEBU_DRAM_MAC_BASE (MVEBU_REGS_BASE + 0x20000) +#define MVEBU_DRAM_PHY_BASE (MVEBU_REGS_BASE + 0x20000) +#define MVEBU_SMMU_BASE (MVEBU_REGS_BASE + 0x100000) +#define MVEBU_CP_MPP_REGS(cp_index, n) (MVEBU_CP_REGS_BASE(cp_index) + \ + 0x440000 + ((n) << 2)) +#define MVEBU_PM_MPP_REGS(cp_index, n) (MVEBU_CP_REGS_BASE(cp_index) + \ + 0x440000 + ((n / 8) << 2)) +#define MVEBU_CP_GPIO_DATA_OUT(cp_index, n) \ + (MVEBU_CP_REGS_BASE(cp_index) + \ + 0x440100 + ((n > 31) ? 0x40 : 0x00)) +#define MVEBU_CP_GPIO_DATA_OUT_EN(cp_index, n) \ + (MVEBU_CP_REGS_BASE(cp_index) + \ + 0x440104 + ((n > 31) ? 0x40 : 0x00)) +#define MVEBU_CP_GPIO_DATA_IN(cp_index, n) (MVEBU_CP_REGS_BASE(cp_index) + \ + 0x440110 + ((n > 31) ? 0x40 : 0x00)) +#define MVEBU_AP_MPP_REGS(n) (MVEBU_RFU_BASE + 0x4000 + ((n) << 2)) +#define MVEBU_AP_GPIO_REGS (MVEBU_RFU_BASE + 0x5040) +#define MVEBU_AP_GPIO_DATA_IN (MVEBU_AP_GPIO_REGS + 0x10) +#define MVEBU_AP_I2C_BASE (MVEBU_REGS_BASE + 0x511000) +#define MVEBU_CP0_I2C_BASE (MVEBU_CP_REGS_BASE(0) + 0x701000) +#define MVEBU_AP_GEN_MGMT_BASE (MVEBU_RFU_BASE + 0x8000) +#define MVEBU_AP_EXT_TSEN_BASE (MVEBU_AP_GEN_MGMT_BASE + 0x84) + +#define MVEBU_AP_MC_TRUSTZONE_REG_LOW(ap, win) (MVEBU_REGS_BASE_AP(ap) + \ + 0x20080 + ((win) * 0x8)) +#define MVEBU_AP_MC_TRUSTZONE_REG_HIGH(ap, win) (MVEBU_REGS_BASE_AP(ap) + \ + 0x20084 + ((win) * 0x8)) + +/* MCI indirect access definitions */ +#define MCI_MAX_UNIT_ID 2 +/* SoC RFU / IHBx4 Control */ +#define MCIX4_REG_START_ADDRESS_REG(unit_id) (MVEBU_RFU_BASE + \ + 0x4218 + (unit_id * 0x20)) +#define MCI_REMAP_OFF_SHIFT 8 + +#define MVEBU_MCI_REG_BASE_REMAP(index) (0xFD000000 + \ + ((index) * 0x1000000)) + +#define MVEBU_PCIE_X4_MAC_BASE(x) (MVEBU_CP_REGS_BASE(x) + 0x600000) +#define MVEBU_COMPHY_BASE(x) (MVEBU_CP_REGS_BASE(x) + 0x441000) +#define MVEBU_HPIPE_BASE(x) (MVEBU_CP_REGS_BASE(x) + 0x120000) +#define MVEBU_CP_DFX_OFFSET (0x400200) + +/***************************************************************************** + * MVEBU memory map related constants + ***************************************************************************** + */ +/* Aggregate of all devices in the first GB */ +#define DEVICE0_BASE MVEBU_REGS_BASE +#define DEVICE0_SIZE 0x10000000 + +/***************************************************************************** + * GIC-400 & interrupt handling related constants + ***************************************************************************** + */ +/* Base MVEBU compatible GIC memory map */ +#define MVEBU_GICD_BASE 0x210000 +#define MVEBU_GICC_BASE 0x220000 + + +/***************************************************************************** + * AXI Configuration + ***************************************************************************** + */ +#define MVEBU_AXI_ATTR_ARCACHE_OFFSET 4 +#define MVEBU_AXI_ATTR_ARCACHE_MASK (0xF << \ + MVEBU_AXI_ATTR_ARCACHE_OFFSET) +#define MVEBU_AXI_ATTR_ARDOMAIN_OFFSET 12 +#define MVEBU_AXI_ATTR_ARDOMAIN_MASK (0x3 << \ + MVEBU_AXI_ATTR_ARDOMAIN_OFFSET) +#define MVEBU_AXI_ATTR_AWCACHE_OFFSET 20 +#define MVEBU_AXI_ATTR_AWCACHE_MASK (0xF << \ + MVEBU_AXI_ATTR_AWCACHE_OFFSET) +#define MVEBU_AXI_ATTR_AWDOMAIN_OFFSET 28 +#define MVEBU_AXI_ATTR_AWDOMAIN_MASK (0x3 << \ + MVEBU_AXI_ATTR_AWDOMAIN_OFFSET) + +/* SATA MBUS to AXI configuration */ +#define MVEBU_SATA_M2A_AXI_ARCACHE_OFFSET 1 +#define MVEBU_SATA_M2A_AXI_ARCACHE_MASK (0xF << \ + MVEBU_SATA_M2A_AXI_ARCACHE_OFFSET) +#define MVEBU_SATA_M2A_AXI_AWCACHE_OFFSET 5 +#define MVEBU_SATA_M2A_AXI_AWCACHE_MASK (0xF << \ + MVEBU_SATA_M2A_AXI_AWCACHE_OFFSET) + +/* ARM cache attributes */ +#define CACHE_ATTR_BUFFERABLE 0x1 +#define CACHE_ATTR_CACHEABLE 0x2 +#define CACHE_ATTR_READ_ALLOC 0x4 +#define CACHE_ATTR_WRITE_ALLOC 0x8 +/* Domain */ +#define DOMAIN_NON_SHAREABLE 0x0 +#define DOMAIN_INNER_SHAREABLE 0x1 +#define DOMAIN_OUTER_SHAREABLE 0x2 +#define DOMAIN_SYSTEM_SHAREABLE 0x3 + +/************************************************************************ + * Required platform porting definitions common to all + * Management Compute SubSystems (MSS) + ************************************************************************ + */ +/* + * Load address of SCP_BL2 + * SCP_BL2 is loaded to the same place as BL31. + * Once SCP_BL2 is transferred to the SCP, + * it is discarded and BL31 is loaded over the top. + */ +#ifdef SCP_IMAGE +#define SCP_BL2_BASE BL31_BASE +#define SCP_BL2_SIZE BL31_LIMIT +#endif + +#ifndef __ASSEMBLER__ +enum ap806_sar_target_dev { + SAR_PIDI_MCIX2 = 0x0, + SAR_MCIX4 = 0x1, + SAR_SPI = 0x2, + SAR_SD = 0x3, + SAR_PIDI_MCIX2_BD = 0x4, /* BootRom disabled */ + SAR_MCIX4_DB = 0x5, /* BootRom disabled */ + SAR_SPI_DB = 0x6, /* BootRom disabled */ + SAR_EMMC = 0x7 +}; + +enum io_win_target_ids { + MCI_0_TID = 0x0, + MCI_1_TID = 0x1, + MCI_2_TID = 0x2, + PIDI_TID = 0x3, + SPI_TID = 0x4, + STM_TID = 0x5, + BOOTROM_TID = 0x6, + IO_WIN_MAX_TID +}; + +enum ccu_target_ids { + IO_0_TID = 0x00, + DRAM_0_TID = 0x03, + IO_1_TID = 0x0F, + CFG_REG_TID = 0x10, + RAR_TID = 0x20, + SRAM_TID = 0x40, + DRAM_1_TID = 0xC0, + CCU_MAX_TID, + INVALID_TID = 0xFF +}; +#endif /* __ASSEMBLER__ */ + +#endif /* A8K_PLAT_DEF_H */ diff --git a/plat/marvell/armada/a8k/common/include/ddr_info.h b/plat/marvell/armada/a8k/common/include/ddr_info.h new file mode 100644 index 0000000..e19036a --- /dev/null +++ b/plat/marvell/armada/a8k/common/include/ddr_info.h @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#define DRAM_MAX_IFACE 1 +#define DRAM_CH0_MMAP_LOW_OFFSET 0x20200 diff --git a/plat/marvell/armada/a8k/common/include/mentor_i2c_plat.h b/plat/marvell/armada/a8k/common/include/mentor_i2c_plat.h new file mode 100644 index 0000000..e03c448 --- /dev/null +++ b/plat/marvell/armada/a8k/common/include/mentor_i2c_plat.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ +/* This driver provides I2C support for Marvell A8K and compatible SoCs */ + +#ifndef MENTOR_I2C_PLAT_H +#define MENTOR_I2C_PLAT_H + +#define CONFIG_SYS_TCLK 250000000 +#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C_SLAVE 0x0 + +#define I2C_CAN_UNSTUCK + +struct mentor_i2c_regs { + uint32_t slave_address; + uint32_t data; + uint32_t control; + union { + uint32_t status; /* when reading */ + uint32_t baudrate; /* when writing */ + }; + uint32_t xtnd_slave_addr; + uint32_t reserved[2]; + uint32_t soft_reset; + uint8_t reserved2[0xa0 - 0x20]; + uint32_t unstuck; +}; + +#endif /* MENTOR_I2C_PLAT_H */ diff --git a/plat/marvell/armada/a8k/common/include/plat_macros.S b/plat/marvell/armada/a8k/common/include/plat_macros.S new file mode 100644 index 0000000..8faccf0 --- /dev/null +++ b/plat/marvell/armada/a8k/common/include/plat_macros.S @@ -0,0 +1,20 @@ +/* + * 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> + +/* + * Required platform porting macros + * (Provided by included headers) + */ +.macro plat_crash_print_regs +.endm + +#endif /* PLAT_MACROS_S */ diff --git a/plat/marvell/armada/a8k/common/include/platform_def.h b/plat/marvell/armada/a8k/common/include/platform_def.h new file mode 100644 index 0000000..45860ba --- /dev/null +++ b/plat/marvell/armada/a8k/common/include/platform_def.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2018 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 <common/interrupt_props.h> +#include <drivers/arm/gic_common.h> + +#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 + */ + +#define PLAT_MARVELL_SRAM_BASE 0xFFE1C048 +#define PLAT_MARVELL_SRAM_END 0xFFE78000 + +#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_NORTHB_COUNT 1 + +#define PLAT_MARVELL_CLUSTER_COUNT U(2) +#define PLAT_MARVELL_CLUSTER_CORE_COUNT U(2) + +#define PLAT_MARVELL_CORE_COUNT (PLAT_MARVELL_CLUSTER_COUNT * \ + PLAT_MARVELL_CLUSTER_CORE_COUNT) + +#define PLAT_MAX_CPUS_PER_CLUSTER PLAT_MARVELL_CLUSTER_CORE_COUNT + +/* Part of DRAM that 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 12MB for SCP (Secure PayLoad) Trusted RAM + * OP-TEE 4MB SHMEM follows this region + */ +#define PLAT_MARVELL_TRUSTED_RAM_BASE 0x04400000 +#define PLAT_MARVELL_TRUSTED_RAM_SIZE 0x00C00000 /* 12 MB DRAM */ + +#define PLAT_MARVELL_LLC_SRAM_BASE 0x05400000 +#define PLAT_MARVELL_LLC_SRAM_SIZE 0x00100000 /* 1 MB SRAM */ + +/* + * 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_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), \ + INTR_PROP_DESC(MARVELL_IRQ_PIC0, 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 + +/* + * 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 + 0x512000) +#define PLAT_MARVELL_UART_CLK_IN_HZ 200000000 + +/* Recovery image enable */ +#define PLAT_RECOVERY_IMAGE_ENABLE 0 + +/* 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 (note the lower memory space + * is reserved for BLE data) + */ +#define PLAT_MARVELL_MAILBOX_BASE (MARVELL_SHARED_RAM_BASE \ + + 0x400) +#define PLAT_MARVELL_MAILBOX_SIZE 0x100 +#define PLAT_MARVELL_MAILBOX_MAGIC_NUM 0x6D72766C /* mrvl */ + +/* Securities */ +#define IRQ_SEC_OS_TICK_INT MARVELL_IRQ_SEC_PHY_TIMER + +#define MVEBU_PMU_IRQ_WA + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/marvell/armada/a8k/common/mss/mss_a8k.mk b/plat/marvell/armada/a8k/common/mss/mss_a8k.mk new file mode 100644 index 0000000..315fc87 --- /dev/null +++ b/plat/marvell/armada/a8k/common/mss/mss_a8k.mk @@ -0,0 +1,22 @@ +# +# Copyright (C) 2018 Marvell International Ltd. +# +# SPDX-License-Identifier: BSD-3-Clause +# https://spdx.org/licenses +# + +PLAT_MARVELL := plat/marvell/armada +A8K_MSS_SOURCE := $(PLAT_MARVELL)/a8k/common/mss + +BL2_SOURCES += $(A8K_MSS_SOURCE)/mss_bl2_setup.c \ + $(MARVELL_MOCHI_DRV) + +BL31_SOURCES += $(A8K_MSS_SOURCE)/mss_pm_ipc.c \ + $(A8K_MSS_SOURCE)/mss_bl31_setup.c + +PLAT_INCLUDES += -I$(A8K_MSS_SOURCE) + +ifneq (${SCP_BL2},) +# This define is used to inidcate the SCP image is present +$(eval $(call add_define,SCP_IMAGE)) +endif diff --git a/plat/marvell/armada/a8k/common/mss/mss_bl2_setup.c b/plat/marvell/armada/a8k/common/mss/mss_bl2_setup.c new file mode 100644 index 0000000..dee2d5b --- /dev/null +++ b/plat/marvell/armada/a8k/common/mss/mss_bl2_setup.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <platform_def.h> + +#include <common/bl_common.h> +#include <common/debug.h> +#include <drivers/marvell/ccu.h> +#include <drivers/marvell/mochi/ap_setup.h> +#include <drivers/marvell/mochi/cp110_setup.h> +#include <lib/mmio.h> + +#include <armada_common.h> +#include <marvell_plat_priv.h> /* timer functionality */ +#include "mss_defs.h" +#include "mss_scp_bootloader.h" + +/* MSS windows configuration */ +#define MSS_AEBR(base) (base + 0x160) +#define MSS_AIBR(base) (base + 0x164) +#define MSS_AEBR_MASK 0xFFF +#define MSS_AIBR_MASK 0xFFF + +#define MSS_EXTERNAL_SPACE 0x50000000 +#define MSS_EXTERNAL_ACCESS_BIT 28 +#define MSS_EXTERNAL_ADDR_MASK 0xfffffff +#define MSS_INTERNAL_ACCESS_BIT 28 + +struct addr_map_win ccu_mem_map[] = { + {MVEBU_CP_REGS_BASE(0), 0x4000000, IO_0_TID} +}; + +/* Since the scp_bl2 image can contain firmware for cp1 and cp0 coprocessors, + * the access to cp0 and cp1 need to be provided. More precisely it is + * required to: + * - get the information about device id which is stored in CP0 registers + * (to distinguish between cases where we have cp0 and cp1 or standalone cp0) + * - get the access to cp which is needed for loading fw for cp0/cp1 + * coprocessors + * This function configures ccu windows accordingly. + * + * Note: there is no need to restore previous ccu configuration, since in next + * phase (BL31) the init_ccu will be called (via apn806_init/ + * bl31_plat_arch_setu) and therefore the ccu configuration will be overwritten. + */ +static int bl2_plat_mmap_init(void) +{ + int cfg_num, win_id, cfg_idx, cp; + + cfg_num = ARRAY_SIZE(ccu_mem_map); + + /* CCU window-0 should not be counted - it's already used */ + if (cfg_num > (MVEBU_CCU_MAX_WINS - 1)) { + ERROR("BL2: %s: trying to open too many windows\n", __func__); + return -1; + } + + /* Enable required CCU windows + * Do not touch CCU window 0, + * it's used for the internal registers access + */ + for (cfg_idx = 0, win_id = 1; + (win_id < MVEBU_CCU_MAX_WINS) && (cfg_idx < cfg_num); win_id++) { + /* Skip already enabled CCU windows */ + if (ccu_is_win_enabled(MVEBU_AP0, win_id)) + continue; + /* Enable required CCU windows */ + ccu_win_check(&ccu_mem_map[cfg_idx]); + ccu_enable_win(MVEBU_AP0, &ccu_mem_map[cfg_idx], win_id); + cfg_idx++; + } + + /* Config address for each cp other than cp0 */ + for (cp = 1; cp < CP_COUNT; cp++) + update_cp110_default_win(cp); + + /* There is need to configure IO_WIN windows again to overwrite + * temporary configuration done during update_cp110_default_win + */ + init_io_win(MVEBU_AP0); + + /* Open AMB bridge required for MG access */ + for (cp = 0; cp < CP_COUNT; cp++) + cp110_amb_init(MVEBU_CP_REGS_BASE(cp)); + + return 0; +} + +/***************************************************************************** + * Transfer SCP_BL2 from Trusted RAM using the SCP Download protocol. + * Return 0 on success, -1 otherwise. + ***************************************************************************** + */ +int bl2_plat_handle_scp_bl2(image_info_t *scp_bl2_image_info) +{ + int ret; + + INFO("BL2: Initiating SCP_BL2 transfer to SCP\n"); + + /* initialize time (for delay functionality) */ + plat_delay_timer_init(); + + ret = bl2_plat_mmap_init(); + if (ret != 0) + return ret; + + ret = scp_bootloader_transfer((void *)scp_bl2_image_info->image_base, + scp_bl2_image_info->image_size); + + if (ret == 0) + INFO("BL2: SCP_BL2 transferred to SCP\n"); + else + ERROR("BL2: SCP_BL2 transfer failure\n"); + + return ret; +} + +uintptr_t bl2_plat_get_cp_mss_regs(int ap_idx, int cp_idx) +{ + return MVEBU_CP_REGS_BASE(cp_idx) + MSS_CP_REGS_OFFSET; +} + +uintptr_t bl2_plat_get_cp_mss_sram(int ap_idx, int cp_idx) +{ + return MVEBU_CP_REGS_BASE(cp_idx) + MSS_CP_SRAM_OFFSET; +} + +uintptr_t bl2_plat_get_ap_mss_regs(int ap_idx) +{ + return MVEBU_REGS_BASE + MSS_AP_REGS_OFFSET; +} + +uint32_t bl2_plat_get_cp_count(int ap_idx) +{ + uint32_t revision = cp110_device_id_get(MVEBU_CP_REGS_BASE(0)); + /* A8040: two CPs. + * A7040: one CP. + */ + if (revision == MVEBU_80X0_DEV_ID || + revision == MVEBU_80X0_CP115_DEV_ID) + return 2; + else if (revision == MVEBU_CN9130_DEV_ID) + return CP_COUNT; + else + return 1; +} + +uint32_t bl2_plat_get_ap_count(void) +{ + /* A8040 and A7040 have only one AP */ + return 1; +} + +void bl2_plat_configure_mss_windows(uintptr_t mss_regs) +{ + /* set AXI External and Internal Address Bus extension */ + mmio_write_32(MSS_AEBR(mss_regs), + ((0x0 >> MSS_EXTERNAL_ACCESS_BIT) & MSS_AEBR_MASK)); + mmio_write_32(MSS_AIBR(mss_regs), + ((mss_regs >> MSS_INTERNAL_ACCESS_BIT) & MSS_AIBR_MASK)); +} diff --git a/plat/marvell/armada/a8k/common/mss/mss_bl31_setup.c b/plat/marvell/armada/a8k/common/mss/mss_bl31_setup.c new file mode 100644 index 0000000..52a8929 --- /dev/null +++ b/plat/marvell/armada/a8k/common/mss/mss_bl31_setup.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <platform_def.h> + +#include <common/bl_common.h> +#include <common/debug.h> +#include <lib/mmio.h> + +#include <armada_common.h> + +#include "mss_defs.h" + +void mss_start_cp_cm3(int cp) +{ + uint32_t magic; + uintptr_t sram = MVEBU_CP_REGS_BASE(cp) + MSS_CP_SRAM_OFFSET; + uintptr_t regs = MVEBU_CP_REGS_BASE(cp) + MSS_CP_REGS_OFFSET; + + magic = mmio_read_32(sram); + + /* Make sure the FW was loaded */ + if (magic != MSS_FW_READY_MAGIC) { + return; + } + + NOTICE("Starting CP%d MSS CPU\n", cp); + /* remove the magic */ + mmio_write_32(sram, 0); + /* Release M3 from reset */ + mmio_write_32(MSS_M3_RSTCR(regs), + (MSS_M3_RSTCR_RST_OFF << MSS_M3_RSTCR_RST_OFFSET)); +} diff --git a/plat/marvell/armada/a8k/common/mss/mss_defs.h b/plat/marvell/armada/a8k/common/mss/mss_defs.h new file mode 100644 index 0000000..6956461 --- /dev/null +++ b/plat/marvell/armada/a8k/common/mss/mss_defs.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#ifndef MSS_DEFS_H +#define MSS_DEFS_H + +#define MSS_DMA_SRCBR(base) (base + 0xC0) +#define MSS_DMA_DSTBR(base) (base + 0xC4) +#define MSS_DMA_CTRLR(base) (base + 0xC8) +#define MSS_M3_RSTCR(base) (base + 0xFC) + +#define MSS_DMA_CTRLR_SIZE_OFFSET (0) +#define MSS_DMA_CTRLR_REQ_OFFSET (15) +#define MSS_DMA_CTRLR_REQ_SET (1) +#define MSS_DMA_CTRLR_ACK_OFFSET (12) +#define MSS_DMA_CTRLR_ACK_MASK (0x1) +#define MSS_DMA_CTRLR_ACK_READY (1) +#define MSS_M3_RSTCR_RST_OFFSET (0) +#define MSS_M3_RSTCR_RST_OFF (1) + +#define MSS_FW_READY_MAGIC 0x46575144 /* FWRD */ + +#define MSS_AP_REGS_OFFSET 0x00580000 +#define MSS_CP_SRAM_OFFSET 0x00220000 +#define MSS_CP_REGS_OFFSET 0x00280000 + +void mss_start_cp_cm3(int cp); + +#endif /* MSS_DEFS_H */ diff --git a/plat/marvell/armada/a8k/common/mss/mss_pm_ipc.c b/plat/marvell/armada/a8k/common/mss/mss_pm_ipc.c new file mode 100644 index 0000000..a070583 --- /dev/null +++ b/plat/marvell/armada/a8k/common/mss/mss_pm_ipc.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <string.h> + +#include <common/debug.h> +#include <lib/psci/psci.h> +#include <lib/mmio.h> + +#include <mss_pm_ipc.h> + +/* + * SISR is 32 bit interrupt register representing 32 interrupts + * + * +======+=============+=============+ + * + Bits + 31 + 30 - 00 + + * +======+=============+=============+ + * + Desc + MSS Msg Int + Reserved + + * +======+=============+=============+ + */ +#define MSS_SISR (MVEBU_REGS_BASE + 0x5800D0) +#define MSS_SISTR (MVEBU_REGS_BASE + 0x5800D8) + +#define MSS_MSG_INT_MASK (0x80000000) +#define MSS_TIMER_BASE (MVEBU_REGS_BASE_MASK + 0x580110) +#define MSS_TRIGGER_TIMEOUT (2000) + +/***************************************************************************** + * mss_pm_ipc_msg_send + * + * DESCRIPTION: create and transmit IPC message + ***************************************************************************** + */ +int mss_pm_ipc_msg_send(unsigned int channel_id, unsigned int msg_id, + const psci_power_state_t *target_state) +{ + /* Transmit IPC message */ +#ifndef DISABLE_CLUSTER_LEVEL + mv_pm_ipc_msg_tx(channel_id, msg_id, + (unsigned int)target_state->pwr_domain_state[ + MPIDR_AFFLVL1]); +#else + mv_pm_ipc_msg_tx(channel_id, msg_id, 0); +#endif + + return 0; +} + +/***************************************************************************** + * mss_pm_ipc_msg_trigger + * + * DESCRIPTION: Trigger IPC message interrupt to MSS + ***************************************************************************** + */ +int mss_pm_ipc_msg_trigger(void) +{ + unsigned int timeout; + unsigned int t_end; + unsigned int t_start = mmio_read_32(MSS_TIMER_BASE); + + mmio_write_32(MSS_SISR, MSS_MSG_INT_MASK); + + do { + /* wait while SCP process incoming interrupt */ + if (mmio_read_32(MSS_SISTR) != MSS_MSG_INT_MASK) + break; + + /* check timeout */ + t_end = mmio_read_32(MSS_TIMER_BASE); + + timeout = ((t_start > t_end) ? + (t_start - t_end) : (t_end - t_start)); + if (timeout > MSS_TRIGGER_TIMEOUT) { + ERROR("PM MSG Trigger Timeout\n"); + break; + } + + } while (1); + + return 0; +} diff --git a/plat/marvell/armada/a8k/common/mss/mss_pm_ipc.h b/plat/marvell/armada/a8k/common/mss/mss_pm_ipc.h new file mode 100644 index 0000000..1dfa9fa --- /dev/null +++ b/plat/marvell/armada/a8k/common/mss/mss_pm_ipc.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#ifndef MSS_PM_IPC_H +#define MSS_PM_IPC_H + +#include <mss_ipc_drv.h> + +/* Currently MSS does not support Cluster level Power Down */ +#define DISABLE_CLUSTER_LEVEL + + +/***************************************************************************** + * mss_pm_ipc_msg_send + * + * DESCRIPTION: create and transmit IPC message + ***************************************************************************** + */ +int mss_pm_ipc_msg_send(unsigned int channel_id, unsigned int msg_id, + const psci_power_state_t *target_state); + +/***************************************************************************** + * mss_pm_ipc_msg_trigger + * + * DESCRIPTION: Trigger IPC message interrupt to MSS + ***************************************************************************** + */ +int mss_pm_ipc_msg_trigger(void); + + +#endif /* MSS_PM_IPC_H */ diff --git a/plat/marvell/armada/a8k/common/plat_bl1_setup.c b/plat/marvell/armada/a8k/common/plat_bl1_setup.c new file mode 100644 index 0000000..f9521c8 --- /dev/null +++ b/plat/marvell/armada/a8k/common/plat_bl1_setup.c @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <lib/mmio.h> + +#include <plat_marvell.h> + +void marvell_bl1_setup_mpps(void) +{ + /* Enable UART MPPs. + ** In a normal system, this is done by Bootrom. + */ + mmio_write_32(MVEBU_AP_MPP_REGS(1), 0x3000); + mmio_write_32(MVEBU_AP_MPP_REGS(2), 0x3000); +} diff --git a/plat/marvell/armada/a8k/common/plat_bl31_setup.c b/plat/marvell/armada/a8k/common/plat_bl31_setup.c new file mode 100644 index 0000000..db85cce --- /dev/null +++ b/plat/marvell/armada/a8k/common/plat_bl31_setup.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <common/debug.h> +#include <drivers/marvell/mci.h> +#include <drivers/marvell/mochi/ap_setup.h> +#include <drivers/marvell/mochi/cp110_setup.h> +#include <lib/mmio.h> + +#include <armada_common.h> +#include <marvell_plat_priv.h> +#include <marvell_pm.h> +#include <mc_trustzone/mc_trustzone.h> +#include <plat_marvell.h> +#if MSS_SUPPORT +#include <mss_ipc_drv.h> +#include <mss_mem.h> +#include <mss_defs.h> +#endif + +/* In Armada-8k family AP806/AP807, CP0 connected to PIDI + * and CP1 connected to IHB via MCI #0 + */ +#define MVEBU_MCI0 0 + +static _Bool pm_fw_running; + +/* Set a weak stub for platforms that don't need to configure GPIO */ +#pragma weak marvell_gpio_config +int marvell_gpio_config(void) +{ + return 0; +} + +static void marvell_bl31_mpp_init(int cp) +{ + uint32_t reg; + + /* need to do for CP#0 only */ + if (cp) + return; + + + /* + * Enable CP0 I2C MPPs (MPP: 37-38) + * U-Boot rely on proper MPP settings for I2C EEPROM usage + * (only for CP0) + */ + reg = mmio_read_32(MVEBU_CP_MPP_REGS(0, 4)); + mmio_write_32(MVEBU_CP_MPP_REGS(0, 4), reg | 0x2200000); +} + +#if MSS_SUPPORT +void marvell_bl31_mss_init(void) +{ + struct mss_pm_ctrl_block *mss_pm_crtl = + (struct mss_pm_ctrl_block *)MSS_SRAM_PM_CONTROL_BASE; + + /* Check that the image was loaded successfully */ + if (mss_pm_crtl->handshake != HOST_ACKNOWLEDGMENT) { + NOTICE("MSS PM is not supported in this build\n"); + return; + } + + /* If we got here it means that the PM firmware is running */ + pm_fw_running = 1; + + INFO("MSS IPC init\n"); + + if (mss_pm_crtl->ipc_state == IPC_INITIALIZED) + mv_pm_ipc_init(mss_pm_crtl->ipc_base_address | MVEBU_REGS_BASE); +} +#endif + +_Bool is_pm_fw_running(void) +{ + return pm_fw_running; +} + +/* For TrusTzone we treat the "target" field of addr_map_win + * struct as attribute + */ +static const struct addr_map_win tz_map[] = { + {PLAT_MARVELL_ATF_BASE, 0x200000, TZ_PERM_ABORT} +}; + +/* Configure MC TrustZone regions */ +static void marvell_bl31_security_setup(void) +{ + int tz_nr, win_id; + + tz_nr = ARRAY_SIZE(tz_map); + + for (win_id = 0; win_id < tz_nr; win_id++) + tz_enable_win(MVEBU_AP0, tz_map, win_id); +} + +/* This function overruns the same function in marvell_bl31_setup.c */ +void bl31_plat_arch_setup(void) +{ + int cp; + uintptr_t *mailbox = (void *)PLAT_MARVELL_MAILBOX_BASE; + + /* initialize the timer for mdelay/udelay functionality */ + plat_delay_timer_init(); + + /* configure apn806 */ + ap_init(); + + /* In marvell_bl31_plat_arch_setup, el3 mmu is configured. + * el3 mmu configuration MUST be called after apn806_init, if not, + * this will cause an hang in init_io_win + * (after setting the IO windows GCR values). + */ + if (mailbox[MBOX_IDX_MAGIC] != MVEBU_MAILBOX_MAGIC_NUM || + mailbox[MBOX_IDX_SUSPEND_MAGIC] != MVEBU_MAILBOX_SUSPEND_STATE) + marvell_bl31_plat_arch_setup(); + + for (cp = 0; cp < CP_COUNT; cp++) { + cp110_init(MVEBU_CP_REGS_BASE(cp), + STREAM_ID_BASE + (cp * MAX_STREAM_ID_PER_CP)); + + marvell_bl31_mpp_init(cp); + +#if MSS_SUPPORT + /* Release CP MSS CPU from reset once the CP init is done */ + mss_start_cp_cm3(cp); +#endif + } + + for (cp = 1; cp < CP_COUNT; cp++) + mci_link_tune(cp - 1); + +#if MSS_SUPPORT + /* initialize IPC between MSS and ATF */ + if (mailbox[MBOX_IDX_MAGIC] != MVEBU_MAILBOX_MAGIC_NUM || + mailbox[MBOX_IDX_SUSPEND_MAGIC] != MVEBU_MAILBOX_SUSPEND_STATE) + marvell_bl31_mss_init(); +#endif + /* Configure GPIO */ + marvell_gpio_config(); + + marvell_bl31_security_setup(); +} diff --git a/plat/marvell/armada/a8k/common/plat_ble_setup.c b/plat/marvell/armada/a8k/common/plat_ble_setup.c new file mode 100644 index 0000000..9c5ee15 --- /dev/null +++ b/plat/marvell/armada/a8k/common/plat_ble_setup.c @@ -0,0 +1,765 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <common/debug.h> +#include <drivers/marvell/ap807_clocks_init.h> +#include <drivers/marvell/aro.h> +#include <drivers/marvell/ccu.h> +#include <drivers/marvell/io_win.h> +#include <drivers/marvell/mochi/ap_setup.h> +#include <drivers/marvell/mochi/cp110_setup.h> + +#include <armada_common.h> +#include <efuse_def.h> +#include <mv_ddr_if.h> +#include <mvebu_def.h> +#include <plat_marvell.h> + +/* Register for skip image use */ +#define SCRATCH_PAD_REG2 0xF06F00A8 +#define SCRATCH_PAD_SKIP_VAL 0x01 +#define NUM_OF_GPIO_PER_REG 32 + +#define MMAP_SAVE_AND_CONFIG 0 +#define MMAP_RESTORE_SAVED 1 + +/* SAR clock settings */ +#define MVEBU_AP_SAR_REG_BASE(r) (MVEBU_AP_GEN_MGMT_BASE + 0x200 +\ + ((r) << 2)) + +#define SAR_CLOCK_FREQ_MODE_OFFSET (0) +#define SAR_CLOCK_FREQ_MODE_MASK (0x1f << SAR_CLOCK_FREQ_MODE_OFFSET) +#define SAR_PIDI_LOW_SPEED_OFFSET (20) +#define SAR_PIDI_LOW_SPEED_MASK (1 << SAR_PIDI_LOW_SPEED_OFFSET) +#define SAR_PIDI_LOW_SPEED_SHIFT (15) +#define SAR_PIDI_LOW_SPEED_SET (1 << SAR_PIDI_LOW_SPEED_SHIFT) + +#define FREQ_MODE_AP_SAR_REG_NUM (0) +#define SAR_CLOCK_FREQ_MODE(v) (((v) & SAR_CLOCK_FREQ_MODE_MASK) >> \ + SAR_CLOCK_FREQ_MODE_OFFSET) + +#define AVS_I2C_EEPROM_ADDR 0x57 /* EEPROM */ +#define AVS_EN_CTRL_REG (MVEBU_AP_GEN_MGMT_BASE + 0x130) +#define AVS_ENABLE_OFFSET (0) +#define AVS_SOFT_RESET_OFFSET (2) +#define AVS_TARGET_DELTA_OFFSET (21) + +#ifndef MVEBU_SOC_AP807 + /* AP806 SVC bits */ + #define AVS_LOW_VDD_LIMIT_OFFSET (4) + #define AVS_HIGH_VDD_LIMIT_OFFSET (12) + #define AVS_VDD_LOW_LIMIT_MASK (0xFF << AVS_LOW_VDD_LIMIT_OFFSET) + #define AVS_VDD_HIGH_LIMIT_MASK (0xFF << AVS_HIGH_VDD_LIMIT_OFFSET) +#else + /* AP807 SVC bits */ + #define AVS_LOW_VDD_LIMIT_OFFSET (3) + #define AVS_HIGH_VDD_LIMIT_OFFSET (13) + #define AVS_VDD_LOW_LIMIT_MASK (0x3FF << AVS_LOW_VDD_LIMIT_OFFSET) + #define AVS_VDD_HIGH_LIMIT_MASK (0x3FF << AVS_HIGH_VDD_LIMIT_OFFSET) +#endif + +/* VDD limit is 0.9V for A70x0 @ CPU frequency < 1600MHz */ +#define AVS_A7K_LOW_CLK_VALUE ((0x80 << AVS_TARGET_DELTA_OFFSET) | \ + (0x1A << AVS_HIGH_VDD_LIMIT_OFFSET) | \ + (0x1A << AVS_LOW_VDD_LIMIT_OFFSET) | \ + (0x1 << AVS_SOFT_RESET_OFFSET) | \ + (0x1 << AVS_ENABLE_OFFSET)) +/* VDD limit is 1.0V for all A80x0 devices */ +#define AVS_A8K_CLK_VALUE ((0x80 << AVS_TARGET_DELTA_OFFSET) | \ + (0x24 << AVS_HIGH_VDD_LIMIT_OFFSET) | \ + (0x24 << AVS_LOW_VDD_LIMIT_OFFSET) | \ + (0x1 << AVS_SOFT_RESET_OFFSET) | \ + (0x1 << AVS_ENABLE_OFFSET)) + +/* VDD is 0.88V for 2GHz clock on CN913x devices */ +#define AVS_AP807_CLK_VALUE ((0x80UL << 24) | \ + (0x2dc << 13) | \ + (0x2dc << 3) | \ + (0x1 << AVS_SOFT_RESET_OFFSET) | \ + (0x1 << AVS_ENABLE_OFFSET)) + +/* + * - Identification information in the LD-0 eFuse: + * DRO: LD0[74:65] - Not used by the SW + * Revision: LD0[78:75] - Not used by the SW + * Bin: LD0[80:79] - Not used by the SW + * SW Revision: LD0[115:113] + * Cluster 1 PWR: LD0[193] - if set to 1, power down CPU Cluster-1 + * resulting in 2 CPUs active only (7020) + */ +/* Offsets for 2 efuse fields combined into single 64-bit value [125:63] */ +#define EFUSE_AP_LD0_DRO_OFFS 2 /* LD0[74:65] */ +#define EFUSE_AP_LD0_DRO_MASK 0x3FF +#define EFUSE_AP_LD0_REVID_OFFS 12 /* LD0[78:75] */ +#define EFUSE_AP_LD0_REVID_MASK 0xF +#define EFUSE_AP_LD0_BIN_OFFS 16 /* LD0[80:79] */ +#define EFUSE_AP_LD0_BIN_MASK 0x3 +#define EFUSE_AP_LD0_SWREV_MASK 0x7 + +#ifndef MVEBU_SOC_AP807 + /* AP806 AVS work points in the LD0 eFuse + * SVC1 work point: LD0[88:81] + * SVC2 work point: LD0[96:89] + * SVC3 work point: LD0[104:97] + * SVC4 work point: LD0[112:105] + */ + #define EFUSE_AP_LD0_SVC1_OFFS 18 /* LD0[88:81] */ + #define EFUSE_AP_LD0_SVC2_OFFS 26 /* LD0[96:89] */ + #define EFUSE_AP_LD0_SVC3_OFFS 34 /* LD0[104:97] */ + #define EFUSE_AP_LD0_WP_MASK 0xFF + #define EFUSE_AP_LD0_SWREV_OFFS 50 /* LD0[115:113] */ +#else + /* AP807 AVS work points in the LD0 eFuse + * SVC1 work point: LD0[91:81] + * SVC2 work point: LD0[102:92] + * SVC3 work point: LD0[113:103] + */ + #define EFUSE_AP_LD0_SVC1_OFFS 18 /* LD0[91:81] */ + #define EFUSE_AP_LD0_SVC2_OFFS 29 /* LD0[102:92] */ + #define EFUSE_AP_LD0_SVC3_OFFS 40 /* LD0[113:103] */ + #define EFUSE_AP_LD0_WP_MASK 0x7FF /* 10 data,1 parity */ + #define EFUSE_AP_LD0_SWREV_OFFS 51 /* LD0[116:114] */ +#endif + +#define EFUSE_AP_LD0_SVC4_OFFS 42 /* LD0[112:105] */ + +#define EFUSE_AP_LD0_CLUSTER_DOWN_OFFS 4 + +#if MARVELL_SVC_TEST +#define MVEBU_CP_MPP_CTRL37_OFFS 20 +#define MVEBU_CP_MPP_CTRL38_OFFS 24 +#define MVEBU_CP_MPP_I2C_FUNC 2 +#define MVEBU_MPP_CTRL_MASK 0xf +#endif + +/* Return the AP revision of the chip */ +static unsigned int ble_get_ap_type(void) +{ + unsigned int chip_rev_id; + + chip_rev_id = mmio_read_32(MVEBU_CSS_GWD_CTRL_IIDR2_REG); + chip_rev_id = ((chip_rev_id & GWD_IIDR2_CHIP_ID_MASK) >> + GWD_IIDR2_CHIP_ID_OFFSET); + + return chip_rev_id; +} + +/****************************************************************************** + * The routine allows to save the CCU and IO windows configuration during DRAM + * setup and restore them afterwards before exiting the BLE stage. + * Such window configuration is required since not all default settings coming + * from the HW and the BootROM allow access to peripherals connected to + * all available CPn components. + * For instance, when the boot device is located on CP0, the IO window to CP1 + * is not opened automatically by the HW and if the DRAM SPD is located on CP1 + * i2c channel, it cannot be read at BLE stage. + * Therefore the DRAM init procedure have to provide access to all available + * CPn peripherals during the BLE stage by setting the CCU IO window to all + * CPnph addresses and by enabling the IO windows accordingly. + * Additionally this function configures the CCU GCR to DRAM, which allows + * usage or more than 4GB DRAM as it configured by the default CCU DRAM window. + * + * IN: + * MMAP_SAVE_AND_CONFIG - save the existing configuration and update it + * MMAP_RESTORE_SAVED - restore saved configuration + * OUT: + * NONE + **************************************************************************** + */ +static void ble_plat_mmap_config(int restore) +{ + if (restore == MMAP_RESTORE_SAVED) { + /* Restore all orig. settings that were modified by BLE stage */ + ccu_restore_win_all(MVEBU_AP0); + /* Restore CCU */ + iow_restore_win_all(MVEBU_AP0); + return; + } + + /* Store original values */ + ccu_save_win_all(MVEBU_AP0); + /* Save CCU */ + iow_save_win_all(MVEBU_AP0); + + init_ccu(MVEBU_AP0); + /* The configuration saved, now all the changes can be done */ + init_io_win(MVEBU_AP0); +} + +/**************************************************************************** + * Setup Adaptive Voltage Switching - this is required for some platforms + **************************************************************************** + */ +#if !MARVELL_SVC_TEST +static void ble_plat_avs_config(void) +{ + uint32_t freq_mode, device_id; + uint32_t avs_val = 0; + + freq_mode = + SAR_CLOCK_FREQ_MODE(mmio_read_32(MVEBU_AP_SAR_REG_BASE( + FREQ_MODE_AP_SAR_REG_NUM))); + /* Check which SoC is running and act accordingly */ + if (ble_get_ap_type() == CHIP_ID_AP807) { + + avs_val = AVS_AP807_CLK_VALUE; + + } else { + /* Check which SoC is running and act accordingly */ + device_id = cp110_device_id_get(MVEBU_CP_REGS_BASE(0)); + switch (device_id) { + case MVEBU_80X0_DEV_ID: + case MVEBU_80X0_CP115_DEV_ID: + /* Always fix the default AVS value on A80x0 */ + avs_val = AVS_A8K_CLK_VALUE; + break; + case MVEBU_70X0_DEV_ID: + case MVEBU_70X0_CP115_DEV_ID: + /* Fix AVS for CPU clocks lower than 1600MHz on A70x0 */ + if ((freq_mode > CPU_1600_DDR_900_RCLK_900_2) && + (freq_mode < CPU_DDR_RCLK_INVALID)) + avs_val = AVS_A7K_LOW_CLK_VALUE; + break; + default: + ERROR("Unsupported Device ID 0x%x\n", device_id); + return; + } + } + + if (avs_val) { + VERBOSE("AVS: Setting AVS CTRL to 0x%x\n", avs_val); + mmio_write_32(AVS_EN_CTRL_REG, avs_val); + } +} +#endif +/****************************************************************************** + * Update or override current AVS work point value using data stored in EEPROM + * This is only required by QA/validation flows and activated by + * MARVELL_SVC_TEST flag. + * + * The function is expected to be called twice. + * + * First time with AVS value of 0 for testing if the EEPROM requests completely + * override the AVS value and bypass the eFuse test + * + * Second time - with non-zero AVS value obtained from eFuses as an input. + * In this case the EEPROM may contain AVS correction value (either positive + * or negative) that is added to the input AVS value and returned back for + * further processing. + ****************************************************************************** + */ +static uint32_t avs_update_from_eeprom(uint32_t avs_workpoint) +{ + uint32_t new_wp = avs_workpoint; +#if MARVELL_SVC_TEST + /* --------------------------------------------------------------------- + * EEPROM | Data description (avs_step) + * address | + * --------------------------------------------------------------------- + * 0x120 | AVS workpoint correction value + * | if not 0 and not 0xff, correct the AVS taken from eFuse + * | by the number of steps indicated by bit[6:0] + * | bit[7] defines correction direction. + * | If bit[7]=1, add the value from bit[6:0] to AVS workpoint, + * | othervise substruct this value from AVS workpoint. + * --------------------------------------------------------------------- + * 0x121 | AVS workpoint override value + * | Override the AVS workpoint with the value stored in this + * | byte. When running on AP806, the AVS workpoint is 7 bits + * | wide and override value is valid when bit[6:0] holds + * | value greater than zero and smaller than 0x33. + * | When running on AP807, the AVS workpoint is 10 bits wide. + * | Additional 2 MSB bits are supplied by EEPROM byte 0x122. + * | AVS override value is valid when byte @ 0x121 and bit[1:0] + * | of byte @ 0x122 combined have non-zero value. + * --------------------------------------------------------------------- + * 0x122 | Extended AVS workpoint override value + * | Valid only for AP807 platforms and must be less than 0x4 + * --------------------------------------------------------------------- + */ + static uint8_t avs_step[3] = {0}; + uintptr_t reg; + uint32_t val; + unsigned int ap_type = ble_get_ap_type(); + + /* Always happens on second call to this function */ + if (avs_workpoint != 0) { + /* Get correction steps from the EEPROM */ + if ((avs_step[0] != 0) && (avs_step[0] != 0xff)) { + NOTICE("AVS request to step %s by 0x%x from old 0x%x\n", + avs_step[0] & 0x80 ? "DOWN" : "UP", + avs_step[0] & 0x7f, new_wp); + if (avs_step[0] & 0x80) + new_wp -= avs_step[0] & 0x7f; + else + new_wp += avs_step[0] & 0x7f; + } + + return new_wp; + } + + /* AVS values are located in EEPROM + * at CP0 i2c bus #0, device 0x57 offset 0x120 + * The SDA and SCK pins of CP0 i2c-0: MPP[38:37], i2c function 0x2. + */ + reg = MVEBU_CP_MPP_REGS(0, 4); + val = mmio_read_32(reg); + val &= ~((MVEBU_MPP_CTRL_MASK << MVEBU_CP_MPP_CTRL37_OFFS) | + (MVEBU_MPP_CTRL_MASK << MVEBU_CP_MPP_CTRL38_OFFS)); + val |= (MVEBU_CP_MPP_I2C_FUNC << MVEBU_CP_MPP_CTRL37_OFFS) | + (MVEBU_CP_MPP_I2C_FUNC << MVEBU_CP_MPP_CTRL38_OFFS); + mmio_write_32(reg, val); + + /* Init CP0 i2c-0 */ + i2c_init((void *)(MVEBU_CP0_I2C_BASE)); + + /* Read EEPROM only once at the fist call! */ + i2c_read(AVS_I2C_EEPROM_ADDR, 0x120, 2, avs_step, 3); + NOTICE("== SVC test build ==\n"); + NOTICE("EEPROM holds values 0x%x, 0x%x and 0x%x\n", + avs_step[0], avs_step[1], avs_step[2]); + + /* Override the AVS value? */ + if ((ap_type != CHIP_ID_AP807) && (avs_step[1] < 0x33)) { + /* AP806 - AVS is 7 bits */ + new_wp = avs_step[1]; + + } else if (ap_type == CHIP_ID_AP807 && (avs_step[2] < 0x4)) { + /* AP807 - AVS is 10 bits */ + new_wp = avs_step[2]; + new_wp <<= 8; + new_wp |= avs_step[1]; + } + + if (new_wp == 0) + NOTICE("Ignore BAD AVS Override value in EEPROM!\n"); + else + NOTICE("Override AVS by EEPROM value 0x%x\n", new_wp); +#endif /* MARVELL_SVC_TEST */ + return new_wp; +} + +/**************************************************************************** + * SVC flow - v0.10 + * The feature is intended to configure AVS value according to eFuse values + * that are burned individually for each SoC during the test process. + * Primary AVS value is stored in HD efuse and processed on power on + * by the HW engine + * Secondary AVS value is located in LD efuse and contains 4 work points for + * various CPU frequencies. + * The Secondary AVS value is only taken into account if the SW Revision stored + * in the efuse is greater than 0 and the CPU is running in a certain speed. + **************************************************************************** + */ +static void ble_plat_svc_config(void) +{ + uint32_t reg_val, avs_workpoint, freq_pidi_mode; + uint64_t efuse; + uint32_t device_id, single_cluster; + uint16_t svc[4], perr[4], i, sw_ver; + uint8_t avs_data_bits, min_sw_ver, svc_fields; + unsigned int ap_type; + + /* Get test EERPOM data */ + avs_workpoint = avs_update_from_eeprom(0); + if (avs_workpoint) + goto set_aws_wp; + + /* Set access to LD0 */ + reg_val = mmio_read_32(MVEBU_AP_EFUSE_SRV_CTRL_REG); + reg_val &= ~EFUSE_SRV_CTRL_LD_SELECT_MASK; + mmio_write_32(MVEBU_AP_EFUSE_SRV_CTRL_REG, reg_val); + + /* Obtain the value of LD0[125:63] */ + efuse = mmio_read_32(MVEBU_AP_LDX_125_95_EFUSE_OFFS); + efuse <<= 32; + efuse |= mmio_read_32(MVEBU_AP_LDX_94_63_EFUSE_OFFS); + + /* SW Revision: + * Starting from SW revision 1 the SVC flow is supported. + * SW version 0 (efuse not programmed) should follow the + * regular AVS update flow. + */ + sw_ver = (efuse >> EFUSE_AP_LD0_SWREV_OFFS) & EFUSE_AP_LD0_SWREV_MASK; + if (sw_ver < 1) { + NOTICE("SVC: SW Revision 0x%x. SVC is not supported\n", sw_ver); +#if MARVELL_SVC_TEST + NOTICE("SVC_TEST: AVS bypassed\n"); + +#else + ble_plat_avs_config(); +#endif + return; + } + + /* Frequency mode from SAR */ + freq_pidi_mode = SAR_CLOCK_FREQ_MODE( + mmio_read_32( + MVEBU_AP_SAR_REG_BASE( + FREQ_MODE_AP_SAR_REG_NUM))); + + /* Decode all SVC work points */ + svc[0] = (efuse >> EFUSE_AP_LD0_SVC1_OFFS) & EFUSE_AP_LD0_WP_MASK; + svc[1] = (efuse >> EFUSE_AP_LD0_SVC2_OFFS) & EFUSE_AP_LD0_WP_MASK; + svc[2] = (efuse >> EFUSE_AP_LD0_SVC3_OFFS) & EFUSE_AP_LD0_WP_MASK; + + /* Fetch AP type to distinguish between AP806 and AP807 */ + ap_type = ble_get_ap_type(); + + if (ap_type != CHIP_ID_AP807) { + svc[3] = (efuse >> EFUSE_AP_LD0_SVC4_OFFS) + & EFUSE_AP_LD0_WP_MASK; + INFO("SVC: Efuse WP: [0]=0x%x, [1]=0x%x, [2]=0x%x, [3]=0x%x\n", + svc[0], svc[1], svc[2], svc[3]); + avs_data_bits = 7; + min_sw_ver = 2; /* parity check from sw revision 2 */ + svc_fields = 4; + } else { + INFO("SVC: Efuse WP: [0]=0x%x, [1]=0x%x, [2]=0x%x\n", + svc[0], svc[1], svc[2]); + avs_data_bits = 10; + min_sw_ver = 1; /* parity check required from sw revision 1 */ + svc_fields = 3; + } + + /* Validate parity of SVC workpoint values */ + for (i = 0; i < svc_fields; i++) { + uint8_t parity, bit; + perr[i] = 0; + + for (bit = 1, parity = (svc[i] & 1); bit < avs_data_bits; bit++) + parity ^= (svc[i] >> bit) & 1; + + /* From SW version 1 or 2 (AP806/AP807), check parity */ + if ((sw_ver >= min_sw_ver) && + (parity != ((svc[i] >> avs_data_bits) & 1))) + perr[i] = 1; /* register the error */ + } + + single_cluster = mmio_read_32(MVEBU_AP_LDX_220_189_EFUSE_OFFS); + single_cluster = (single_cluster >> EFUSE_AP_LD0_CLUSTER_DOWN_OFFS) & 1; + + device_id = cp110_device_id_get(MVEBU_CP_REGS_BASE(0)); + if (device_id == MVEBU_80X0_DEV_ID || + device_id == MVEBU_80X0_CP115_DEV_ID) { + /* A8040/A8020 */ + NOTICE("SVC: DEV ID: %s, FREQ Mode: 0x%x\n", + single_cluster == 0 ? "8040" : "8020", freq_pidi_mode); + switch (freq_pidi_mode) { + case CPU_1800_DDR_1050_RCLK_1050: + if (perr[1]) + goto perror; + avs_workpoint = svc[1]; + break; + case CPU_1600_DDR_1050_RCLK_1050: + case CPU_1600_DDR_900_RCLK_900_2: + if (perr[2]) + goto perror; + avs_workpoint = svc[2]; + break; + case CPU_1300_DDR_800_RCLK_800: + case CPU_1300_DDR_650_RCLK_650: + if (perr[3]) + goto perror; + avs_workpoint = svc[3]; + break; + case CPU_2000_DDR_1200_RCLK_1200: + case CPU_2000_DDR_1050_RCLK_1050: + default: + if (perr[0]) + goto perror; + avs_workpoint = svc[0]; + break; + } + } else if (device_id == MVEBU_70X0_DEV_ID || + device_id == MVEBU_70X0_CP115_DEV_ID) { + /* A7040/A7020/A6040 */ + NOTICE("SVC: DEV ID: %s, FREQ Mode: 0x%x\n", + single_cluster == 0 ? "7040" : "7020", freq_pidi_mode); + switch (freq_pidi_mode) { + case CPU_1400_DDR_800_RCLK_800: + if (single_cluster) {/* 7020 */ + if (perr[1]) + goto perror; + avs_workpoint = svc[1]; + } else { + if (perr[0]) + goto perror; + avs_workpoint = svc[0]; + } + break; + case CPU_1200_DDR_800_RCLK_800: + if (single_cluster) {/* 7020 */ + if (perr[2]) + goto perror; + avs_workpoint = svc[2]; + } else { + if (perr[1]) + goto perror; + avs_workpoint = svc[1]; + } + break; + case CPU_800_DDR_800_RCLK_800: + case CPU_1000_DDR_800_RCLK_800: + if (single_cluster) {/* 7020 */ + if (perr[3]) + goto perror; + avs_workpoint = svc[3]; + } else { + if (perr[2]) + goto perror; + avs_workpoint = svc[2]; + } + break; + case CPU_600_DDR_800_RCLK_800: + if (perr[3]) + goto perror; + avs_workpoint = svc[3]; /* Same for 6040 and 7020 */ + break; + case CPU_1600_DDR_800_RCLK_800: /* 7020 only */ + default: + if (single_cluster) {/* 7020 */ + if (perr[0]) + goto perror; + avs_workpoint = svc[0]; + } else { +#if MARVELL_SVC_TEST + reg_val = mmio_read_32(AVS_EN_CTRL_REG); + avs_workpoint = (reg_val & + AVS_VDD_LOW_LIMIT_MASK) >> + AVS_LOW_VDD_LIMIT_OFFSET; + NOTICE("7040 1600Mhz, avs = 0x%x\n", + avs_workpoint); +#else + NOTICE("SVC: AVS work point not changed\n"); + return; +#endif + } + break; + } + } else if (device_id == MVEBU_3900_DEV_ID) { + NOTICE("SVC: DEV ID: %s, FREQ Mode: 0x%x\n", + "3900", freq_pidi_mode); + switch (freq_pidi_mode) { + case CPU_1600_DDR_1200_RCLK_1200: + if (perr[0]) + goto perror; + avs_workpoint = svc[0]; + break; + case CPU_1300_DDR_800_RCLK_800: + if (perr[1]) + goto perror; + avs_workpoint = svc[1]; + break; + default: + if (perr[0]) + goto perror; + avs_workpoint = svc[0]; + break; + } + } else if (device_id == MVEBU_CN9130_DEV_ID) { + NOTICE("SVC: DEV ID: %s, FREQ Mode: 0x%x\n", + "CN913x", freq_pidi_mode); + switch (freq_pidi_mode) { + case CPU_2200_DDR_1200_RCLK_1200: + if (perr[0]) + goto perror; + avs_workpoint = svc[0]; + break; + case CPU_2000_DDR_1200_RCLK_1200: + if (perr[1]) + goto perror; + avs_workpoint = svc[1]; + break; + case CPU_1600_DDR_1200_RCLK_1200: + if (perr[2]) + goto perror; + avs_workpoint = svc[2]; + break; + default: + ERROR("SVC: Unsupported Frequency 0x%x\n", + freq_pidi_mode); + return; + + } + } else { + ERROR("SVC: Unsupported Device ID 0x%x\n", device_id); + return; + } + + /* Set AVS control if needed */ + if (avs_workpoint == 0) { + ERROR("SVC: You are using a frequency setup which is\n"); + ERROR("Not supported by this device\n"); + ERROR("This may result in malfunction of the device\n"); + return; + } + + /* Remove parity bit */ + if (ap_type != CHIP_ID_AP807) + avs_workpoint &= 0x7F; + else + avs_workpoint &= 0x3FF; + + /* Update WP from EEPROM if needed */ + avs_workpoint = avs_update_from_eeprom(avs_workpoint); + +set_aws_wp: + reg_val = mmio_read_32(AVS_EN_CTRL_REG); + NOTICE("SVC: AVS work point changed from 0x%x to 0x%x\n", + (reg_val & AVS_VDD_LOW_LIMIT_MASK) >> AVS_LOW_VDD_LIMIT_OFFSET, + avs_workpoint); + reg_val &= ~(AVS_VDD_LOW_LIMIT_MASK | AVS_VDD_HIGH_LIMIT_MASK); + reg_val |= 0x1 << AVS_ENABLE_OFFSET; + reg_val |= avs_workpoint << AVS_HIGH_VDD_LIMIT_OFFSET; + reg_val |= avs_workpoint << AVS_LOW_VDD_LIMIT_OFFSET; + mmio_write_32(AVS_EN_CTRL_REG, reg_val); + return; + +perror: + ERROR("Failed SVC WP[%d] parity check!\n", i); + ERROR("Ignoring the WP values\n"); +} + +#if PLAT_RECOVERY_IMAGE_ENABLE +static int ble_skip_image_i2c(struct skip_image *skip_im) +{ + ERROR("skipping image using i2c is not supported\n"); + /* not supported */ + return 0; +} + +static int ble_skip_image_other(struct skip_image *skip_im) +{ + ERROR("implementation missing for skip image request\n"); + /* not supported, make your own implementation */ + return 0; +} + +static int ble_skip_image_gpio(struct skip_image *skip_im) +{ + unsigned int val; + unsigned int mpp_address = 0; + unsigned int offset = 0; + + switch (skip_im->info.test.cp_ap) { + case(CP): + mpp_address = MVEBU_CP_GPIO_DATA_IN(skip_im->info.test.cp_index, + skip_im->info.gpio.num); + if (skip_im->info.gpio.num > NUM_OF_GPIO_PER_REG) + offset = skip_im->info.gpio.num - NUM_OF_GPIO_PER_REG; + else + offset = skip_im->info.gpio.num; + break; + case(AP): + mpp_address = MVEBU_AP_GPIO_DATA_IN; + offset = skip_im->info.gpio.num; + break; + } + + val = mmio_read_32(mpp_address); + val &= (1 << offset); + if ((!val && skip_im->info.gpio.button_state == HIGH) || + (val && skip_im->info.gpio.button_state == LOW)) { + mmio_write_32(SCRATCH_PAD_REG2, SCRATCH_PAD_SKIP_VAL); + return 1; + } + + return 0; +} + +/* + * This function checks if there's a skip image request: + * return values: + * 1: (true) images request been made. + * 0: (false) no image request been made. + */ +static int ble_skip_current_image(void) +{ + struct skip_image *skip_im; + + /*fetching skip image info*/ + skip_im = (struct skip_image *)plat_marvell_get_skip_image_data(); + + if (skip_im == NULL) + return 0; + + /* check if skipping image request has already been made */ + if (mmio_read_32(SCRATCH_PAD_REG2) == SCRATCH_PAD_SKIP_VAL) + return 0; + + switch (skip_im->detection_method) { + case GPIO: + return ble_skip_image_gpio(skip_im); + case I2C: + return ble_skip_image_i2c(skip_im); + case USER_DEFINED: + return ble_skip_image_other(skip_im); + } + + return 0; +} +#endif + + +int ble_plat_setup(int *skip) +{ + int ret, cp; + unsigned int freq_mode; + + /* Power down unused CPUs */ + plat_marvell_early_cpu_powerdown(); + + /* + * Save the current CCU configuration and make required changes: + * - Allow access to DRAM larger than 4GB + * - Open memory access to all CPn peripherals + */ + ble_plat_mmap_config(MMAP_SAVE_AND_CONFIG); + +#if PLAT_RECOVERY_IMAGE_ENABLE + /* Check if there's a skip request to bootRom recovery Image */ + if (ble_skip_current_image()) { + /* close memory access to all CPn peripherals. */ + ble_plat_mmap_config(MMAP_RESTORE_SAVED); + *skip = 1; + return 0; + } +#endif + /* Do required CP-110 setups for BLE stage */ + cp110_ble_init(MVEBU_CP_REGS_BASE(0)); + + /* Config address for each cp other than cp0 */ + for (cp = 1; cp < CP_COUNT; cp++) + update_cp110_default_win(cp); + + /* Setup AVS */ + ble_plat_svc_config(); + + /* read clk option from sampled-at-reset register */ + freq_mode = + SAR_CLOCK_FREQ_MODE(mmio_read_32(MVEBU_AP_SAR_REG_BASE( + FREQ_MODE_AP_SAR_REG_NUM))); + + /* work with PLL clock driver in AP807 */ + if (ble_get_ap_type() == CHIP_ID_AP807) + ap807_clocks_init(freq_mode); + + /* Do required AP setups for BLE stage */ + ap_ble_init(); + + /* Update DRAM topology (scan DIMM SPDs) */ + plat_marvell_dram_update_topology(); + + /* Kick it in */ + ret = dram_init(); + + /* Restore the original CCU configuration before exit from BLE */ + ble_plat_mmap_config(MMAP_RESTORE_SAVED); + + return ret; +} diff --git a/plat/marvell/armada/a8k/common/plat_pm.c b/plat/marvell/armada/a8k/common/plat_pm.c new file mode 100644 index 0000000..9ea9276 --- /dev/null +++ b/plat/marvell/armada/a8k/common/plat_pm.c @@ -0,0 +1,853 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <assert.h> + +#include <common/debug.h> +#include <drivers/arm/gicv2.h> +#include <drivers/console.h> +#include <drivers/delay_timer.h> +#include <drivers/marvell/cache_llc.h> +#include <lib/bakery_lock.h> +#include <lib/mmio.h> +#include <plat/common/platform.h> + +#include <armada_common.h> +#include <marvell_pm.h> +#if MSS_SUPPORT +#include <mss_pm_ipc.h> +#endif +#include <plat_marvell.h> +#include <plat_pm_trace.h> + +#define MVEBU_PRIVATE_UID_REG 0x30 +#define MVEBU_RFU_GLOBL_SW_RST 0x84 +#define MVEBU_CCU_RVBAR(cpu) (MVEBU_REGS_BASE + 0x640 + (cpu * 4)) +#define MVEBU_CCU_CPU_UN_RESET(cpu) (MVEBU_REGS_BASE + 0x650 + (cpu * 4)) + +#define MPIDR_CPU_GET(mpidr) ((mpidr) & MPIDR_CPU_MASK) +#define MPIDR_CLUSTER_GET(mpidr) MPIDR_AFFLVL1_VAL((mpidr)) + +#define MVEBU_GPIO_MASK(index) (1 << (index % 32)) +#define MVEBU_MPP_MASK(index) (0xF << (4 * (index % 8))) +#define MVEBU_GPIO_VALUE(index, value) (value << (index % 32)) + +#define MVEBU_USER_CMD_0_REG (MVEBU_DRAM_MAC_BASE + 0x20) +#define MVEBU_USER_CMD_CH0_OFFSET 28 +#define MVEBU_USER_CMD_CH0_MASK (1 << MVEBU_USER_CMD_CH0_OFFSET) +#define MVEBU_USER_CMD_CH0_EN (1 << MVEBU_USER_CMD_CH0_OFFSET) +#define MVEBU_USER_CMD_CS_OFFSET 24 +#define MVEBU_USER_CMD_CS_MASK (0xF << MVEBU_USER_CMD_CS_OFFSET) +#define MVEBU_USER_CMD_CS_ALL (0xF << MVEBU_USER_CMD_CS_OFFSET) +#define MVEBU_USER_CMD_SR_OFFSET 6 +#define MVEBU_USER_CMD_SR_MASK (0x3 << MVEBU_USER_CMD_SR_OFFSET) +#define MVEBU_USER_CMD_SR_ENTER (0x1 << MVEBU_USER_CMD_SR_OFFSET) +#define MVEBU_MC_PWR_CTRL_REG (MVEBU_DRAM_MAC_BASE + 0x54) +#define MVEBU_MC_AC_ON_DLY_OFFSET 8 +#define MVEBU_MC_AC_ON_DLY_MASK (0xF << MVEBU_MC_AC_ON_DLY_OFFSET) +#define MVEBU_MC_AC_ON_DLY_DEF_VAR (8 << MVEBU_MC_AC_ON_DLY_OFFSET) +#define MVEBU_MC_AC_OFF_DLY_OFFSET 4 +#define MVEBU_MC_AC_OFF_DLY_MASK (0xF << MVEBU_MC_AC_OFF_DLY_OFFSET) +#define MVEBU_MC_AC_OFF_DLY_DEF_VAR (0xC << MVEBU_MC_AC_OFF_DLY_OFFSET) +#define MVEBU_MC_PHY_AUTO_OFF_OFFSET 0 +#define MVEBU_MC_PHY_AUTO_OFF_MASK (1 << MVEBU_MC_PHY_AUTO_OFF_OFFSET) +#define MVEBU_MC_PHY_AUTO_OFF_EN (1 << MVEBU_MC_PHY_AUTO_OFF_OFFSET) + +/* this lock synchronize AP multiple cores execution with MSS */ +DEFINE_BAKERY_LOCK(pm_sys_lock); + +/* Weak definitions may be overridden in specific board */ +#pragma weak plat_marvell_get_pm_cfg + +/* AP806 CPU power down /power up definitions */ +enum CPU_ID { + CPU0, + CPU1, + CPU2, + CPU3 +}; + +#define REG_WR_VALIDATE_TIMEOUT (2000) + +#define FEATURE_DISABLE_STATUS_REG \ + (MVEBU_REGS_BASE + 0x6F8230) +#define FEATURE_DISABLE_STATUS_CPU_CLUSTER_OFFSET 4 +#define FEATURE_DISABLE_STATUS_CPU_CLUSTER_MASK \ + (0x1 << FEATURE_DISABLE_STATUS_CPU_CLUSTER_OFFSET) + +#ifdef MVEBU_SOC_AP807 + #define PWRC_CPUN_CR_PWR_DN_RQ_OFFSET 1 + #define PWRC_CPUN_CR_LDO_BYPASS_RDY_OFFSET 0 +#else + #define PWRC_CPUN_CR_PWR_DN_RQ_OFFSET 0 + #define PWRC_CPUN_CR_LDO_BYPASS_RDY_OFFSET 31 +#endif + +#define PWRC_CPUN_CR_REG(cpu_id) \ + (MVEBU_REGS_BASE + 0x680000 + (cpu_id * 0x10)) +#define PWRC_CPUN_CR_PWR_DN_RQ_MASK \ + (0x1 << PWRC_CPUN_CR_PWR_DN_RQ_OFFSET) +#define PWRC_CPUN_CR_ISO_ENABLE_OFFSET 16 +#define PWRC_CPUN_CR_ISO_ENABLE_MASK \ + (0x1 << PWRC_CPUN_CR_ISO_ENABLE_OFFSET) +#define PWRC_CPUN_CR_LDO_BYPASS_RDY_MASK \ + (0x1U << PWRC_CPUN_CR_LDO_BYPASS_RDY_OFFSET) + +#define CCU_B_PRCRN_REG(cpu_id) \ + (MVEBU_REGS_BASE + 0x1A50 + \ + ((cpu_id / 2) * (0x400)) + ((cpu_id % 2) * 4)) +#define CCU_B_PRCRN_CPUPORESET_STATIC_OFFSET 0 +#define CCU_B_PRCRN_CPUPORESET_STATIC_MASK \ + (0x1 << CCU_B_PRCRN_CPUPORESET_STATIC_OFFSET) + +/* power switch fingers */ +#define AP807_PWRC_LDO_CR0_REG \ + (MVEBU_REGS_BASE + 0x680000 + 0x100) +#define AP807_PWRC_LDO_CR0_OFFSET 16 +#define AP807_PWRC_LDO_CR0_MASK \ + (0xff << AP807_PWRC_LDO_CR0_OFFSET) +#define AP807_PWRC_LDO_CR0_VAL 0xfc + +/* + * Power down CPU: + * Used to reduce power consumption, and avoid SoC unnecessary temperature rise. + */ +static int plat_marvell_cpu_powerdown(int cpu_id) +{ + uint32_t reg_val; + int exit_loop = REG_WR_VALIDATE_TIMEOUT; + + INFO("Powering down CPU%d\n", cpu_id); + + /* 1. Isolation enable */ + reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id)); + reg_val |= 0x1 << PWRC_CPUN_CR_ISO_ENABLE_OFFSET; + mmio_write_32(PWRC_CPUN_CR_REG(cpu_id), reg_val); + + /* 2. Read and check Isolation enabled - verify bit set to 1 */ + do { + reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id)); + exit_loop--; + } while (!(reg_val & (0x1 << PWRC_CPUN_CR_ISO_ENABLE_OFFSET)) && + exit_loop > 0); + + /* 3. Switch off CPU power */ + reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id)); + reg_val &= ~PWRC_CPUN_CR_PWR_DN_RQ_MASK; + mmio_write_32(PWRC_CPUN_CR_REG(cpu_id), reg_val); + + /* 4. Read and check Switch Off - verify bit set to 0 */ + exit_loop = REG_WR_VALIDATE_TIMEOUT; + do { + reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id)); + exit_loop--; + } while (reg_val & PWRC_CPUN_CR_PWR_DN_RQ_MASK && exit_loop > 0); + + if (exit_loop <= 0) + goto cpu_poweroff_error; + + /* 5. De-Assert power ready */ + reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id)); + reg_val &= ~PWRC_CPUN_CR_LDO_BYPASS_RDY_MASK; + mmio_write_32(PWRC_CPUN_CR_REG(cpu_id), reg_val); + + /* 6. Assert CPU POR reset */ + reg_val = mmio_read_32(CCU_B_PRCRN_REG(cpu_id)); + reg_val &= ~CCU_B_PRCRN_CPUPORESET_STATIC_MASK; + mmio_write_32(CCU_B_PRCRN_REG(cpu_id), reg_val); + + /* 7. Read and poll on Validate the CPU is out of reset */ + exit_loop = REG_WR_VALIDATE_TIMEOUT; + do { + reg_val = mmio_read_32(CCU_B_PRCRN_REG(cpu_id)); + exit_loop--; + } while (reg_val & CCU_B_PRCRN_CPUPORESET_STATIC_MASK && exit_loop > 0); + + if (exit_loop <= 0) + goto cpu_poweroff_error; + + INFO("Successfully powered down CPU%d\n", cpu_id); + + return 0; + +cpu_poweroff_error: + ERROR("ERROR: Can't power down CPU%d\n", cpu_id); + return -1; +} + +/* + * Power down CPUs 1-3 at early boot stage, + * to reduce power consumption and SoC temperature. + * This is triggered by BLE prior to DDR initialization. + * + * Note: + * All CPUs will be powered up by plat_marvell_cpu_powerup on Linux boot stage, + * which is triggered by PSCI ops (pwr_domain_on). + */ +int plat_marvell_early_cpu_powerdown(void) +{ + uint32_t cpu_cluster_status = + mmio_read_32(FEATURE_DISABLE_STATUS_REG) & + FEATURE_DISABLE_STATUS_CPU_CLUSTER_MASK; + /* if cpu_cluster_status bit is set, + * that means we have only single cluster + */ + int cluster_count = cpu_cluster_status ? 1 : 2; + + INFO("Powering off unused CPUs\n"); + + /* CPU1 is in AP806 cluster-0, which always exists, so power it down */ + if (plat_marvell_cpu_powerdown(CPU1) == -1) + return -1; + + /* + * CPU2-3 are in AP806 2nd cluster (cluster-1), + * which doesn't exists in dual-core systems. + * so need to check if we have dual-core (single cluster) + * or quad-code (2 clusters) + */ + if (cluster_count == 2) { + /* CPU2-3 are part of 2nd cluster */ + if (plat_marvell_cpu_powerdown(CPU2) == -1) + return -1; + if (plat_marvell_cpu_powerdown(CPU3) == -1) + return -1; + } + + return 0; +} + +/* + * Power up CPU - part of Linux boot stage + */ +static int plat_marvell_cpu_powerup(u_register_t mpidr) +{ + uint32_t reg_val; + int cpu_id = MPIDR_CPU_GET(mpidr), + cluster = MPIDR_CLUSTER_GET(mpidr); + int exit_loop = REG_WR_VALIDATE_TIMEOUT; + + /* calculate absolute CPU ID */ + cpu_id = cluster * PLAT_MARVELL_CLUSTER_CORE_COUNT + cpu_id; + + INFO("Powering on CPU%d\n", cpu_id); + +#ifdef MVEBU_SOC_AP807 + /* Activate 2 power switch fingers */ + reg_val = mmio_read_32(AP807_PWRC_LDO_CR0_REG); + reg_val &= ~(AP807_PWRC_LDO_CR0_MASK); + reg_val |= (AP807_PWRC_LDO_CR0_VAL << AP807_PWRC_LDO_CR0_OFFSET); + mmio_write_32(AP807_PWRC_LDO_CR0_REG, reg_val); + udelay(100); +#endif + + /* 1. Switch CPU power ON */ + reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id)); + reg_val |= 0x1 << PWRC_CPUN_CR_PWR_DN_RQ_OFFSET; + mmio_write_32(PWRC_CPUN_CR_REG(cpu_id), reg_val); + + /* 2. Wait for CPU on, up to 100 uSec: */ + udelay(100); + + /* 3. Assert power ready */ + reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id)); + reg_val |= 0x1U << PWRC_CPUN_CR_LDO_BYPASS_RDY_OFFSET; + mmio_write_32(PWRC_CPUN_CR_REG(cpu_id), reg_val); + + /* 4. Read & Validate power ready + * used in order to generate 16 Host CPU cycles + */ + do { + reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id)); + exit_loop--; + } while (!(reg_val & (0x1U << PWRC_CPUN_CR_LDO_BYPASS_RDY_OFFSET)) && + exit_loop > 0); + + if (exit_loop <= 0) + goto cpu_poweron_error; + + /* 5. Isolation disable */ + reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id)); + reg_val &= ~PWRC_CPUN_CR_ISO_ENABLE_MASK; + mmio_write_32(PWRC_CPUN_CR_REG(cpu_id), reg_val); + + /* 6. Read and check Isolation enabled - verify bit set to 1 */ + exit_loop = REG_WR_VALIDATE_TIMEOUT; + do { + reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id)); + exit_loop--; + } while ((reg_val & (0x1 << PWRC_CPUN_CR_ISO_ENABLE_OFFSET)) && + exit_loop > 0); + + /* 7. De Assert CPU POR reset & Core reset */ + reg_val = mmio_read_32(CCU_B_PRCRN_REG(cpu_id)); + reg_val |= 0x1 << CCU_B_PRCRN_CPUPORESET_STATIC_OFFSET; + mmio_write_32(CCU_B_PRCRN_REG(cpu_id), reg_val); + + /* 8. Read & Validate CPU POR reset */ + exit_loop = REG_WR_VALIDATE_TIMEOUT; + do { + reg_val = mmio_read_32(CCU_B_PRCRN_REG(cpu_id)); + exit_loop--; + } while (!(reg_val & (0x1 << CCU_B_PRCRN_CPUPORESET_STATIC_OFFSET)) && + exit_loop > 0); + + if (exit_loop <= 0) + goto cpu_poweron_error; + + INFO("Successfully powered on CPU%d\n", cpu_id); + + return 0; + +cpu_poweron_error: + ERROR("ERROR: Can't power up CPU%d\n", cpu_id); + return -1; +} + +static int plat_marvell_cpu_on(u_register_t mpidr) +{ + int cpu_id; + int cluster; + + /* Set barierr */ + dsbsy(); + + /* Get cpu number - use CPU ID */ + cpu_id = MPIDR_CPU_GET(mpidr); + + /* Get cluster number - use affinity level 1 */ + cluster = MPIDR_CLUSTER_GET(mpidr); + + /* Set CPU private UID */ + mmio_write_32(MVEBU_REGS_BASE + MVEBU_PRIVATE_UID_REG, cluster + 0x4); + + /* Set the cpu start address to BL1 entry point (align to 0x10000) */ + mmio_write_32(MVEBU_CCU_RVBAR(cpu_id), + PLAT_MARVELL_CPU_ENTRY_ADDR >> 16); + + /* Get the cpu out of reset */ + mmio_write_32(MVEBU_CCU_CPU_UN_RESET(cpu_id), 0x10001); + + return 0; +} + +/***************************************************************************** + * A8K handler called to check the validity of the power state + * parameter. + ***************************************************************************** + */ +static int a8k_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + int pstate = psci_get_pstate_type(power_state); + int pwr_lvl = psci_get_pstate_pwrlvl(power_state); + int i; + + if (pwr_lvl > PLAT_MAX_PWR_LVL) + return PSCI_E_INVALID_PARAMS; + + /* Sanity check the requested state */ + if (pstate == PSTATE_TYPE_STANDBY) { + /* + * It's possible to enter standby only on power level 0 + * Ignore any other power level. + */ + if (pwr_lvl != MARVELL_PWR_LVL0) + return PSCI_E_INVALID_PARAMS; + + req_state->pwr_domain_state[MARVELL_PWR_LVL0] = + MARVELL_LOCAL_STATE_RET; + } else { + for (i = MARVELL_PWR_LVL0; i <= pwr_lvl; i++) + req_state->pwr_domain_state[i] = + MARVELL_LOCAL_STATE_OFF; + } + + /* + * We expect the 'state id' to be zero. + */ + if (psci_get_pstate_id(power_state)) + return PSCI_E_INVALID_PARAMS; + + return PSCI_E_SUCCESS; +} + +/***************************************************************************** + * A8K handler called when a CPU is about to enter standby. + ***************************************************************************** + */ +static void a8k_cpu_standby(plat_local_state_t cpu_state) +{ + if (!is_pm_fw_running()) { + ERROR("%s: needs to be implemented\n", __func__); + panic(); + } +} + +/***************************************************************************** + * A8K handler called when a power domain is about to be turned on. The + * mpidr determines the CPU to be turned on. + ***************************************************************************** + */ +static int a8k_pwr_domain_on(u_register_t mpidr) +{ + /* Power up CPU (CPUs 1-3 are powered off at start of BLE) */ + plat_marvell_cpu_powerup(mpidr); + +#if MSS_SUPPORT + if (is_pm_fw_running()) { + unsigned int target = + ((mpidr & 0xFF) + (((mpidr >> 8) & 0xFF) * 2)); + + /* + * pm system synchronization - used to synchronize + * multiple core access to MSS + */ + bakery_lock_get(&pm_sys_lock); + + /* send CPU ON IPC Message to MSS */ + mss_pm_ipc_msg_send(target, PM_IPC_MSG_CPU_ON, 0); + + /* trigger IPC message to MSS */ + mss_pm_ipc_msg_trigger(); + + /* pm system synchronization */ + bakery_lock_release(&pm_sys_lock); + + /* trace message */ + PM_TRACE(TRACE_PWR_DOMAIN_ON | target); + } else +#endif + { + /* proprietary CPU ON exection flow */ + plat_marvell_cpu_on(mpidr); + } + return 0; +} + +/***************************************************************************** + * A8K handler called to validate the entry point. + ***************************************************************************** + */ +static int a8k_validate_ns_entrypoint(uintptr_t entrypoint) +{ + return PSCI_E_SUCCESS; +} + +/***************************************************************************** + * A8K 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. + ***************************************************************************** + */ +static void a8k_pwr_domain_off(const psci_power_state_t *target_state) +{ +#if MSS_SUPPORT + if (is_pm_fw_running()) { + unsigned int idx = plat_my_core_pos(); + + /* Prevent interrupts from spuriously waking up this cpu */ + gicv2_cpuif_disable(); + + /* pm system synchronization - used to synchronize multiple + * core access to MSS + */ + bakery_lock_get(&pm_sys_lock); + + /* send CPU OFF IPC Message to MSS */ + mss_pm_ipc_msg_send(idx, PM_IPC_MSG_CPU_OFF, target_state); + + /* trigger IPC message to MSS */ + mss_pm_ipc_msg_trigger(); + + /* pm system synchronization */ + bakery_lock_release(&pm_sys_lock); + + /* trace message */ + PM_TRACE(TRACE_PWR_DOMAIN_OFF); + } else { + INFO("%s: is not supported without SCP\n", __func__); + } +#endif +} + +/* Get PM config to power off the SoC */ +void *plat_marvell_get_pm_cfg(void) +{ + return NULL; +} + +/* + * This function should be called on restore from + * "suspend to RAM" state when the execution flow + * has to bypass BootROM image to RAM copy and speed up + * the system recovery + * + */ +static void plat_marvell_exit_bootrom(void) +{ + marvell_exit_bootrom(PLAT_MARVELL_TRUSTED_ROM_BASE); +} + +/* + * Prepare for the power off of the system via GPIO + */ +static void plat_marvell_power_off_gpio(struct power_off_method *pm_cfg, + register_t *gpio_addr, + register_t *gpio_data) +{ + unsigned int gpio; + unsigned int idx; + unsigned int shift; + unsigned int reg; + unsigned int addr; + gpio_info_t *info; + unsigned int tog_bits; + + assert((pm_cfg->cfg.gpio.pin_count < PMIC_GPIO_MAX_NUMBER) && + (pm_cfg->cfg.gpio.step_count < PMIC_GPIO_MAX_TOGGLE_STEP)); + + /* Prepare GPIOs for PMIC */ + for (gpio = 0; gpio < pm_cfg->cfg.gpio.pin_count; gpio++) { + info = &pm_cfg->cfg.gpio.info[gpio]; + /* Set PMIC GPIO to output mode */ + reg = mmio_read_32(MVEBU_CP_GPIO_DATA_OUT_EN( + info->cp_index, info->gpio_index)); + mmio_write_32(MVEBU_CP_GPIO_DATA_OUT_EN( + info->cp_index, info->gpio_index), + reg & ~MVEBU_GPIO_MASK(info->gpio_index)); + + /* Set the appropriate MPP to GPIO mode */ + reg = mmio_read_32(MVEBU_PM_MPP_REGS(info->cp_index, + info->gpio_index)); + mmio_write_32(MVEBU_PM_MPP_REGS(info->cp_index, + info->gpio_index), + reg & ~MVEBU_MPP_MASK(info->gpio_index)); + } + + /* Wait for MPP & GPIO pre-configurations done */ + mdelay(pm_cfg->cfg.gpio.delay_ms); + + /* Toggle the GPIO values, and leave final step to be triggered + * after DDR self-refresh is enabled + */ + for (idx = 0; idx < pm_cfg->cfg.gpio.step_count; idx++) { + tog_bits = pm_cfg->cfg.gpio.seq[idx]; + + /* The GPIOs must be within same GPIO register, + * thus could get the original value by first GPIO + */ + info = &pm_cfg->cfg.gpio.info[0]; + reg = mmio_read_32(MVEBU_CP_GPIO_DATA_OUT( + info->cp_index, info->gpio_index)); + addr = MVEBU_CP_GPIO_DATA_OUT(info->cp_index, info->gpio_index); + + for (gpio = 0; gpio < pm_cfg->cfg.gpio.pin_count; gpio++) { + shift = pm_cfg->cfg.gpio.info[gpio].gpio_index % 32; + if (GPIO_LOW == (tog_bits & (1 << gpio))) + reg &= ~(1 << shift); + else + reg |= (1 << shift); + } + + /* Set the GPIO register, for last step just store + * register address and values to system registers + */ + if (idx < pm_cfg->cfg.gpio.step_count - 1) { + mmio_write_32(MVEBU_CP_GPIO_DATA_OUT( + info->cp_index, info->gpio_index), reg); + mdelay(pm_cfg->cfg.gpio.delay_ms); + } else { + /* Save GPIO register and address values for + * finishing the power down operation later + */ + *gpio_addr = addr; + *gpio_data = reg; + } + } +} + +/* + * Prepare for the power off of the system + */ +static void plat_marvell_power_off_prepare(struct power_off_method *pm_cfg, + register_t *addr, register_t *data) +{ + switch (pm_cfg->type) { + case PMIC_GPIO: + plat_marvell_power_off_gpio(pm_cfg, addr, data); + break; + default: + break; + } +} + +/***************************************************************************** + * A8K handler called when a power domain is about to be suspended. The + * target_state encodes the power state that each level should transition to. + ***************************************************************************** + */ +static void a8k_pwr_domain_suspend(const psci_power_state_t *target_state) +{ +#if MSS_SUPPORT + if (is_pm_fw_running()) { + unsigned int idx; + + /* Prevent interrupts from spuriously waking up this cpu */ + gicv2_cpuif_disable(); + + idx = plat_my_core_pos(); + + /* pm system synchronization - used to synchronize multiple + * core access to MSS + */ + bakery_lock_get(&pm_sys_lock); + + /* send CPU Suspend IPC Message to MSS */ + mss_pm_ipc_msg_send(idx, PM_IPC_MSG_CPU_SUSPEND, target_state); + + /* trigger IPC message to MSS */ + mss_pm_ipc_msg_trigger(); + + /* pm system synchronization */ + bakery_lock_release(&pm_sys_lock); + + /* trace message */ + PM_TRACE(TRACE_PWR_DOMAIN_SUSPEND); + } else +#endif + { + uintptr_t *mailbox = (void *)PLAT_MARVELL_MAILBOX_BASE; + + INFO("Suspending to RAM\n"); + + marvell_console_runtime_end(); + + /* Prevent interrupts from spuriously waking up this cpu */ + gicv2_cpuif_disable(); + + mailbox[MBOX_IDX_SUSPEND_MAGIC] = MVEBU_MAILBOX_SUSPEND_STATE; + mailbox[MBOX_IDX_ROM_EXIT_ADDR] = (uintptr_t)&plat_marvell_exit_bootrom; + +#if PLAT_MARVELL_SHARED_RAM_CACHED + flush_dcache_range(PLAT_MARVELL_MAILBOX_BASE + + MBOX_IDX_SUSPEND_MAGIC * sizeof(uintptr_t), + 2 * sizeof(uintptr_t)); +#endif + /* Flush and disable LLC before going off-power */ + llc_disable(0); + + isb(); + /* + * Do not halt here! + * The function must return for allowing the caller function + * psci_power_up_finish() to do the proper context saving and + * to release the CPU lock. + */ + } +} + +/***************************************************************************** + * A8K 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. + ***************************************************************************** + */ +static void a8k_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + /* arch specific configuration */ + marvell_psci_arch_init(0); + + /* Interrupt initialization */ + gicv2_pcpu_distif_init(); + gicv2_cpuif_enable(); + + if (is_pm_fw_running()) { + /* trace message */ + PM_TRACE(TRACE_PWR_DOMAIN_ON_FINISH); + } +} + +/***************************************************************************** + * A8K 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. + ***************************************************************************** + */ +static void a8k_pwr_domain_suspend_finish( + const psci_power_state_t *target_state) +{ + if (is_pm_fw_running()) { + /* arch specific configuration */ + marvell_psci_arch_init(0); + + /* Interrupt initialization */ + gicv2_cpuif_enable(); + + /* trace message */ + PM_TRACE(TRACE_PWR_DOMAIN_SUSPEND_FINISH); + } else { + uintptr_t *mailbox = (void *)PLAT_MARVELL_MAILBOX_BASE; + + /* Only primary CPU requres platform init */ + if (!plat_my_core_pos()) { + /* Initialize the console to provide + * early debug support + */ + marvell_console_runtime_init(); + + bl31_plat_arch_setup(); + marvell_bl31_platform_setup(); + /* + * Remove suspend to RAM marker from the mailbox + * for treating a regular reset as a cold boot + */ + mailbox[MBOX_IDX_SUSPEND_MAGIC] = 0; + mailbox[MBOX_IDX_ROM_EXIT_ADDR] = 0; +#if PLAT_MARVELL_SHARED_RAM_CACHED + flush_dcache_range(PLAT_MARVELL_MAILBOX_BASE + + MBOX_IDX_SUSPEND_MAGIC * sizeof(uintptr_t), + 2 * sizeof(uintptr_t)); +#endif + } + } +} + +/***************************************************************************** + * 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. + ***************************************************************************** + */ +static void a8k_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; +} + +static void +__dead2 a8k_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) +{ + struct power_off_method *pm_cfg; + unsigned int srcmd; + unsigned int sdram_reg; + register_t gpio_data = 0, gpio_addr = 0; + + if (is_pm_fw_running()) { + psci_power_down_wfi(); + panic(); + } + + pm_cfg = (struct power_off_method *)plat_marvell_get_pm_cfg(); + + /* Prepare for power off */ + plat_marvell_power_off_prepare(pm_cfg, &gpio_addr, &gpio_data); + + /* First step to enable DDR self-refresh + * to keep the data during suspend + */ + mmio_write_32(MVEBU_MC_PWR_CTRL_REG, 0x8C1); + + /* Save DDR self-refresh second step register + * and value to be issued later + */ + sdram_reg = MVEBU_USER_CMD_0_REG; + srcmd = mmio_read_32(sdram_reg); + srcmd &= ~(MVEBU_USER_CMD_CH0_MASK | MVEBU_USER_CMD_CS_MASK | + MVEBU_USER_CMD_SR_MASK); + srcmd |= (MVEBU_USER_CMD_CH0_EN | MVEBU_USER_CMD_CS_ALL | + MVEBU_USER_CMD_SR_ENTER); + + /* + * Wait for DRAM is done using registers access only. + * At this stage any access to DRAM (procedure call) will + * release it from the self-refresh mode + */ + __asm__ volatile ( + /* Align to a cache line */ + " .balign 64\n\t" + + /* Enter self refresh */ + " str %[srcmd], [%[sdram_reg]]\n\t" + + /* + * Wait 100 cycles for DDR to enter self refresh, by + * doing 50 times two instructions. + */ + " mov x1, #50\n\t" + "1: subs x1, x1, #1\n\t" + " bne 1b\n\t" + + /* Issue the command to trigger the SoC power off */ + " str %[gpio_data], [%[gpio_addr]]\n\t" + + /* Trap the processor */ + " b .\n\t" + : : [srcmd] "r" (srcmd), [sdram_reg] "r" (sdram_reg), + [gpio_addr] "r" (gpio_addr), [gpio_data] "r" (gpio_data) + : "x1"); + + panic(); +} + +/***************************************************************************** + * A8K handlers to shutdown/reboot the system + ***************************************************************************** + */ + +/* Set a weak stub for platforms that don't configure system power off */ +#pragma weak system_power_off +int system_power_off(void) +{ + return 0; +} + +static void __dead2 a8k_system_off(void) +{ + /* Call the platform specific system power off function */ + system_power_off(); + + /* board doesn't have a system off implementation */ + ERROR("%s: needs to be implemented\n", __func__); + panic(); +} + +void plat_marvell_system_reset(void) +{ + mmio_write_32(MVEBU_RFU_BASE + MVEBU_RFU_GLOBL_SW_RST, 0x0); +} + +static void __dead2 a8k_system_reset(void) +{ + plat_marvell_system_reset(); + + /* we 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 = a8k_cpu_standby, + .pwr_domain_on = a8k_pwr_domain_on, + .pwr_domain_off = a8k_pwr_domain_off, + .pwr_domain_suspend = a8k_pwr_domain_suspend, + .pwr_domain_on_finish = a8k_pwr_domain_on_finish, + .get_sys_suspend_power_state = a8k_get_sys_suspend_power_state, + .pwr_domain_suspend_finish = a8k_pwr_domain_suspend_finish, + .pwr_domain_pwr_down_wfi = a8k_pwr_domain_pwr_down_wfi, + .system_off = a8k_system_off, + .system_reset = a8k_system_reset, + .validate_power_state = a8k_validate_power_state, + .validate_ns_entrypoint = a8k_validate_ns_entrypoint +}; diff --git a/plat/marvell/armada/a8k/common/plat_pm_trace.c b/plat/marvell/armada/a8k/common/plat_pm_trace.c new file mode 100644 index 0000000..e02a893 --- /dev/null +++ b/plat/marvell/armada/a8k/common/plat_pm_trace.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <lib/mmio.h> +#include <plat/common/platform.h> + +#if MSS_SUPPORT +#include <mss_mem.h> + +#ifdef PM_TRACE_ENABLE +#include <plat_pm_trace.h> + +/* core trace APIs */ +core_trace_func funcTbl[PLATFORM_CORE_COUNT] = { + pm_core_0_trace, + pm_core_1_trace, + pm_core_2_trace, + pm_core_3_trace}; + +/***************************************************************************** + * pm_core0_trace + * pm_core1_trace + * pm_core2_trace + * pm_core_3trace + * + * This functions set trace info into core cyclic trace queue in MSS SRAM + * memory space + ***************************************************************************** + */ +void pm_core_0_trace(unsigned int trace) +{ + unsigned int current_position_core_0 = + mmio_read_32(AP_MSS_ATF_CORE_0_CTRL_BASE); + mmio_write_32((AP_MSS_ATF_CORE_0_INFO_BASE + + (current_position_core_0 * AP_MSS_ATF_CORE_ENTRY_SIZE)), + mmio_read_32(AP_MSS_TIMER_BASE)); + mmio_write_32((AP_MSS_ATF_CORE_0_INFO_TRACE + + (current_position_core_0 * AP_MSS_ATF_CORE_ENTRY_SIZE)), + trace); + mmio_write_32(AP_MSS_ATF_CORE_0_CTRL_BASE, + ((current_position_core_0 + 1) & + AP_MSS_ATF_TRACE_SIZE_MASK)); +} + +void pm_core_1_trace(unsigned int trace) +{ + unsigned int current_position_core_1 = + mmio_read_32(AP_MSS_ATF_CORE_1_CTRL_BASE); + mmio_write_32((AP_MSS_ATF_CORE_1_INFO_BASE + + (current_position_core_1 * AP_MSS_ATF_CORE_ENTRY_SIZE)), + mmio_read_32(AP_MSS_TIMER_BASE)); + mmio_write_32((AP_MSS_ATF_CORE_1_INFO_TRACE + + (current_position_core_1 * AP_MSS_ATF_CORE_ENTRY_SIZE)), + trace); + mmio_write_32(AP_MSS_ATF_CORE_1_CTRL_BASE, + ((current_position_core_1 + 1) & + AP_MSS_ATF_TRACE_SIZE_MASK)); +} + +void pm_core_2_trace(unsigned int trace) +{ + unsigned int current_position_core_2 = + mmio_read_32(AP_MSS_ATF_CORE_2_CTRL_BASE); + mmio_write_32((AP_MSS_ATF_CORE_2_INFO_BASE + + (current_position_core_2 * AP_MSS_ATF_CORE_ENTRY_SIZE)), + mmio_read_32(AP_MSS_TIMER_BASE)); + mmio_write_32((AP_MSS_ATF_CORE_2_INFO_TRACE + + (current_position_core_2 * AP_MSS_ATF_CORE_ENTRY_SIZE)), + trace); + mmio_write_32(AP_MSS_ATF_CORE_2_CTRL_BASE, + ((current_position_core_2 + 1) & + AP_MSS_ATF_TRACE_SIZE_MASK)); +} + +void pm_core_3_trace(unsigned int trace) +{ + unsigned int current_position_core_3 = + mmio_read_32(AP_MSS_ATF_CORE_3_CTRL_BASE); + mmio_write_32((AP_MSS_ATF_CORE_3_INFO_BASE + + (current_position_core_3 * AP_MSS_ATF_CORE_ENTRY_SIZE)), + mmio_read_32(AP_MSS_TIMER_BASE)); + mmio_write_32((AP_MSS_ATF_CORE_3_INFO_TRACE + + (current_position_core_3 * AP_MSS_ATF_CORE_ENTRY_SIZE)), + trace); + mmio_write_32(AP_MSS_ATF_CORE_3_CTRL_BASE, + ((current_position_core_3 + 1) & + AP_MSS_ATF_TRACE_SIZE_MASK)); +} +#endif /* PM_TRACE_ENABLE */ +#endif /* MSS_SUPPORT */ diff --git a/plat/marvell/armada/a8k/common/plat_thermal.c b/plat/marvell/armada/a8k/common/plat_thermal.c new file mode 100644 index 0000000..a2fc0d0 --- /dev/null +++ b/plat/marvell/armada/a8k/common/plat_thermal.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <drivers/marvell/thermal.h> +#include <lib/mmio.h> + +#include <mvebu_def.h> + +#define THERMAL_TIMEOUT 1200 + +#define THERMAL_SEN_CTRL_LSB_STRT_OFFSET 0 +#define THERMAL_SEN_CTRL_LSB_STRT_MASK \ + (0x1 << THERMAL_SEN_CTRL_LSB_STRT_OFFSET) +#define THERMAL_SEN_CTRL_LSB_RST_OFFSET 1 +#define THERMAL_SEN_CTRL_LSB_RST_MASK \ + (0x1 << THERMAL_SEN_CTRL_LSB_RST_OFFSET) +#define THERMAL_SEN_CTRL_LSB_EN_OFFSET 2 +#define THERMAL_SEN_CTRL_LSB_EN_MASK \ + (0x1 << THERMAL_SEN_CTRL_LSB_EN_OFFSET) + +#define THERMAL_SEN_CTRL_STATS_VALID_OFFSET 16 +#define THERMAL_SEN_CTRL_STATS_VALID_MASK \ + (0x1 << THERMAL_SEN_CTRL_STATS_VALID_OFFSET) +#define THERMAL_SEN_CTRL_STATS_TEMP_OUT_OFFSET 0 +#define THERMAL_SEN_CTRL_STATS_TEMP_OUT_MASK \ + (0x3FF << THERMAL_SEN_CTRL_STATS_TEMP_OUT_OFFSET) + +#define THERMAL_SEN_OUTPUT_MSB 512 +#define THERMAL_SEN_OUTPUT_COMP 1024 + +struct tsen_regs { + uint32_t ext_tsen_ctrl_lsb; + uint32_t ext_tsen_ctrl_msb; + uint32_t ext_tsen_status; +}; + +static int ext_tsen_probe(struct tsen_config *tsen_cfg) +{ + uint32_t reg, timeout = 0; + struct tsen_regs *base; + + if (tsen_cfg == NULL && tsen_cfg->regs_base == NULL) { + ERROR("initial thermal sensor configuration is missing\n"); + return -1; + } + base = (struct tsen_regs *)tsen_cfg->regs_base; + + INFO("initializing thermal sensor\n"); + + /* initialize thermal sensor hardware reset once */ + reg = mmio_read_32((uintptr_t)&base->ext_tsen_ctrl_lsb); + reg &= ~THERMAL_SEN_CTRL_LSB_RST_OFFSET; /* de-assert TSEN_RESET */ + reg |= THERMAL_SEN_CTRL_LSB_EN_MASK; /* set TSEN_EN to 1 */ + reg |= THERMAL_SEN_CTRL_LSB_STRT_MASK; /* set TSEN_START to 1 */ + mmio_write_32((uintptr_t)&base->ext_tsen_ctrl_lsb, reg); + + reg = mmio_read_32((uintptr_t)&base->ext_tsen_status); + while ((reg & THERMAL_SEN_CTRL_STATS_VALID_MASK) == 0 && + timeout < THERMAL_TIMEOUT) { + udelay(100); + reg = mmio_read_32((uintptr_t)&base->ext_tsen_status); + timeout++; + } + + if ((reg & THERMAL_SEN_CTRL_STATS_VALID_MASK) == 0) { + ERROR("thermal sensor is not ready\n"); + return -1; + } + + tsen_cfg->tsen_ready = 1; + + VERBOSE("thermal sensor was initialized\n"); + + return 0; +} + +static int ext_tsen_read(struct tsen_config *tsen_cfg, int *temp) +{ + uint32_t reg; + struct tsen_regs *base; + + if (tsen_cfg == NULL && !tsen_cfg->tsen_ready) { + ERROR("thermal sensor was not initialized\n"); + return -1; + } + base = (struct tsen_regs *)tsen_cfg->regs_base; + + reg = mmio_read_32((uintptr_t)&base->ext_tsen_status); + reg = ((reg & THERMAL_SEN_CTRL_STATS_TEMP_OUT_MASK) >> + THERMAL_SEN_CTRL_STATS_TEMP_OUT_OFFSET); + + /* + * TSEN output format is signed as a 2s complement number + * ranging from-512 to +511. when MSB is set, need to + * calculate the complement number + */ + if (reg >= THERMAL_SEN_OUTPUT_MSB) + reg -= THERMAL_SEN_OUTPUT_COMP; + + if (tsen_cfg->tsen_divisor == 0) { + ERROR("thermal sensor divisor cannot be zero\n"); + return -1; + } + + *temp = ((tsen_cfg->tsen_gain * ((int)reg)) + + tsen_cfg->tsen_offset) / tsen_cfg->tsen_divisor; + + return 0; +} + +static struct tsen_config tsen_cfg = { + .tsen_offset = 153400, + .tsen_gain = 425, + .tsen_divisor = 1000, + .tsen_ready = 0, + .regs_base = (void *)MVEBU_AP_EXT_TSEN_BASE, + .ptr_tsen_probe = ext_tsen_probe, + .ptr_tsen_read = ext_tsen_read +}; + +struct tsen_config *marvell_thermal_config_get(void) +{ + return &tsen_cfg; +} |