summaryrefslogtreecommitdiffstats
path: root/plat/marvell/armada/a8k/common
diff options
context:
space:
mode:
Diffstat (limited to 'plat/marvell/armada/a8k/common')
-rw-r--r--plat/marvell/armada/a8k/common/a8k_common.mk192
-rw-r--r--plat/marvell/armada/a8k/common/aarch64/a8k_common.c70
-rw-r--r--plat/marvell/armada/a8k/common/aarch64/plat_arch_config.c95
-rw-r--r--plat/marvell/armada/a8k/common/aarch64/plat_helpers.S112
-rw-r--r--plat/marvell/armada/a8k/common/ble/ble.ld.S76
-rw-r--r--plat/marvell/armada/a8k/common/ble/ble.mk35
-rw-r--r--plat/marvell/armada/a8k/common/ble/ble_main.c99
-rw-r--r--plat/marvell/armada/a8k/common/ble/ble_mem.S30
-rw-r--r--plat/marvell/armada/a8k/common/include/a8k_plat_def.h195
-rw-r--r--plat/marvell/armada/a8k/common/include/ddr_info.h9
-rw-r--r--plat/marvell/armada/a8k/common/include/mentor_i2c_plat.h33
-rw-r--r--plat/marvell/armada/a8k/common/include/plat_macros.S20
-rw-r--r--plat/marvell/armada/a8k/common/include/platform_def.h196
-rw-r--r--plat/marvell/armada/a8k/common/mss/mss_a8k.mk22
-rw-r--r--plat/marvell/armada/a8k/common/mss/mss_bl2_setup.c165
-rw-r--r--plat/marvell/armada/a8k/common/mss/mss_bl31_setup.c37
-rw-r--r--plat/marvell/armada/a8k/common/mss/mss_defs.h33
-rw-r--r--plat/marvell/armada/a8k/common/mss/mss_pm_ipc.c85
-rw-r--r--plat/marvell/armada/a8k/common/mss/mss_pm_ipc.h35
-rw-r--r--plat/marvell/armada/a8k/common/plat_bl1_setup.c19
-rw-r--r--plat/marvell/armada/a8k/common/plat_bl31_setup.c148
-rw-r--r--plat/marvell/armada/a8k/common/plat_ble_setup.c765
-rw-r--r--plat/marvell/armada/a8k/common/plat_pm.c853
-rw-r--r--plat/marvell/armada/a8k/common/plat_pm_trace.c94
-rw-r--r--plat/marvell/armada/a8k/common/plat_thermal.c130
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;
+}