summaryrefslogtreecommitdiffstats
path: root/plat/qti/msm8916
diff options
context:
space:
mode:
Diffstat (limited to 'plat/qti/msm8916')
-rw-r--r--plat/qti/msm8916/aarch32/msm8916_helpers.S145
-rw-r--r--plat/qti/msm8916/aarch32/uartdm_console.S183
-rw-r--r--plat/qti/msm8916/aarch64/msm8916_helpers.S166
-rw-r--r--plat/qti/msm8916/aarch64/uartdm_console.S179
-rw-r--r--plat/qti/msm8916/include/msm8916_mmap.h51
-rw-r--r--plat/qti/msm8916/include/plat_macros.S27
-rw-r--r--plat/qti/msm8916/include/platform_def.h75
-rw-r--r--plat/qti/msm8916/include/uartdm_console.h12
-rw-r--r--plat/qti/msm8916/msm8916_bl31_setup.c64
-rw-r--r--plat/qti/msm8916/msm8916_config.c195
-rw-r--r--plat/qti/msm8916/msm8916_config.h13
-rw-r--r--plat/qti/msm8916/msm8916_cpu_boot.c150
-rw-r--r--plat/qti/msm8916/msm8916_gicv2.c62
-rw-r--r--plat/qti/msm8916/msm8916_gicv2.h13
-rw-r--r--plat/qti/msm8916/msm8916_pm.c103
-rw-r--r--plat/qti/msm8916/msm8916_pm.h13
-rw-r--r--plat/qti/msm8916/msm8916_setup.c122
-rw-r--r--plat/qti/msm8916/msm8916_setup.h14
-rw-r--r--plat/qti/msm8916/msm8916_topology.c39
-rw-r--r--plat/qti/msm8916/platform.mk98
-rw-r--r--plat/qti/msm8916/sp_min/msm8916_sp_min_setup.c50
-rw-r--r--plat/qti/msm8916/sp_min/sp_min-msm8916.mk11
-rw-r--r--plat/qti/msm8916/tsp/msm8916_tsp_setup.c31
-rw-r--r--plat/qti/msm8916/tsp/tsp-msm8916.mk8
24 files changed, 1824 insertions, 0 deletions
diff --git a/plat/qti/msm8916/aarch32/msm8916_helpers.S b/plat/qti/msm8916/aarch32/msm8916_helpers.S
new file mode 100644
index 0000000..dc35043
--- /dev/null
+++ b/plat/qti/msm8916/aarch32/msm8916_helpers.S
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2021-2023, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+#include <platform_def.h>
+
+#include <msm8916_mmap.h>
+
+#if PLATFORM_CORE_COUNT > 1
+#define APCS_TCM_START_ADDR 0x10
+#else
+#define APCS_TCM_START_ADDR 0x34
+#endif
+#define APCS_TCM_REDIRECT_EN_0 BIT_32(0)
+
+ .globl plat_crash_console_init
+ .globl plat_crash_console_putc
+ .globl plat_crash_console_flush
+ .globl plat_panic_handler
+ .globl plat_my_core_pos
+ .globl plat_get_my_entrypoint
+ .globl plat_reset_handler
+ .globl platform_mem_init
+ .globl msm8916_entry_point
+
+ /* -------------------------------------------------
+ * int plat_crash_console_init(void)
+ * Initialize the crash console.
+ * Out: r0 - 1 on success, 0 on error
+ * Clobber list : r0 - r4
+ * -------------------------------------------------
+ */
+func plat_crash_console_init
+ ldr r1, =BLSP_UART_BASE
+ mov r0, #1
+ b console_uartdm_core_init
+endfunc plat_crash_console_init
+
+ /* -------------------------------------------------
+ * int plat_crash_console_putc(int c)
+ * Print a character on the crash console.
+ * In : r0 - character to be printed
+ * Out: r0 - printed character on success
+ * Clobber list : r1, r2
+ * -------------------------------------------------
+ */
+func plat_crash_console_putc
+ ldr r1, =BLSP_UART_BASE
+ b console_uartdm_core_putc
+endfunc plat_crash_console_putc
+
+ /* -------------------------------------------------
+ * void plat_crash_console_flush(void)
+ * Force a write of all buffered data that has not
+ * been output.
+ * Clobber list : r1, r2
+ * -------------------------------------------------
+ */
+func plat_crash_console_flush
+ ldr r1, =BLSP_UART_BASE
+ b console_uartdm_core_flush
+endfunc plat_crash_console_flush
+
+ /* -------------------------------------------------
+ * void plat_panic_handler(void) __dead
+ * Called when an unrecoverable error occurs.
+ * -------------------------------------------------
+ */
+func plat_panic_handler
+ /* Try to shutdown/reset */
+ ldr r0, =MPM_PS_HOLD
+ mov r1, #0
+ str r1, [r0]
+1: b 1b
+endfunc plat_panic_handler
+
+ /* -------------------------------------------------
+ * unsigned int plat_my_core_pos(void)
+ * Out: r0 - index of the calling CPU
+ * -------------------------------------------------
+ */
+func plat_my_core_pos
+ .if PLATFORM_CORE_COUNT > 1
+ ldcopr r1, MPIDR
+ and r0, r1, #MPIDR_CPU_MASK
+ .if PLATFORM_CLUSTER_COUNT > 1
+ and r1, r1, #MPIDR_CLUSTER_MASK
+ orr r0, r0, r1, LSR #(MPIDR_AFFINITY_BITS - \
+ PLATFORM_CPU_PER_CLUSTER_SHIFT)
+ .endif
+ .else
+ /* There is just a single core so always 0 */
+ mov r0, #0
+ .endif
+ bx lr
+endfunc plat_my_core_pos
+
+ /* -------------------------------------------------
+ * uintptr_t plat_get_my_entrypoint(void)
+ * Distinguish cold and warm boot and return warm boot
+ * entry address if available.
+ * Out: r0 - warm boot entry point or 0 on cold boot
+ * -------------------------------------------------
+ */
+func plat_get_my_entrypoint
+ ldr r0, =msm8916_entry_point
+ ldr r0, [r0]
+ cmp r0, #0
+ bxne lr
+
+ /*
+ * Cold boot: Disable TCM redirect to L2 cache as early as
+ * possible to avoid crashes when making use of the cache.
+ */
+ ldr r1, =APCS_CFG(0)
+ ldr r2, [r1, #APCS_TCM_START_ADDR]
+ and r2, r2, #~APCS_TCM_REDIRECT_EN_0
+ str r2, [r1, #APCS_TCM_START_ADDR]
+ bx lr
+endfunc plat_get_my_entrypoint
+
+ /* -------------------------------------------------
+ * void platform_mem_init(void)
+ * Performs additional memory initialization early
+ * in the boot process.
+ * -------------------------------------------------
+ */
+func platform_mem_init
+ /* Nothing to do here, all memory is already initialized */
+ bx lr
+endfunc platform_mem_init
+
+ .data
+ .align 3
+
+ /* -------------------------------------------------
+ * Warm boot entry point for CPU. Set by PSCI code.
+ * -------------------------------------------------
+ */
+msm8916_entry_point:
+ .word 0
diff --git a/plat/qti/msm8916/aarch32/uartdm_console.S b/plat/qti/msm8916/aarch32/uartdm_console.S
new file mode 100644
index 0000000..a19776a
--- /dev/null
+++ b/plat/qti/msm8916/aarch32/uartdm_console.S
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2021-2023, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * Based on aarch32/skeleton_console.S:
+ * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <asm_macros.S>
+#include <console_macros.S>
+
+/* UART DM registers */
+#define UART_DM_DMEN 0x03c /* DMA / data packing */
+#define UART_DM_SR 0x0a4 /* status register */
+#define UART_DM_CR 0x0a8 /* command register */
+#define UART_DM_TF 0x100 /* transmit FIFO */
+
+#define UART_DM_DMEN_TX_SC BIT_32(4) /* TX single character mode */
+
+#define UART_DM_SR_TXRDY BIT_32(2) /* TX FIFO has space */
+#define UART_DM_SR_TXEMT BIT_32(3) /* TX FIFO is empty */
+
+#define UART_DM_CR_RESET_RX (U(0x01) << 4) /* reset receiver */
+#define UART_DM_CR_RESET_TX (U(0x02) << 4) /* reset transmitter */
+#define UART_DM_CR_TX_ENABLE BIT_32(2) /* enable transmitter */
+
+ .globl console_uartdm_register
+ .globl console_uartdm_core_init
+ .globl console_uartdm_putc
+ .globl console_uartdm_core_putc
+ .globl console_uartdm_flush
+ .globl console_uartdm_core_flush
+
+ /* -----------------------------------------------------------
+ * int console_uartdm_register(console_t *console,
+ * uintptr_t base_addr)
+ * Function to initialize and register the console. The caller
+ * needs to pass an empty console_t structure in which *MUST*
+ * be allocated in persistent memory (e.g. a global or static
+ * local variable, *NOT* on the stack).
+ * In : r0 - pointer to empty console_t structure
+ * r1 - base address
+ * Out: r0 - 1 on success, 0 on error
+ * Clobber list : r0 - r7
+ * -----------------------------------------------------------
+ */
+func console_uartdm_register
+ str r1, [r0, #CONSOLE_T_BASE]
+ mov r7, lr
+ bl console_uartdm_core_init
+ mov lr, r7
+
+ /* Register the new console */
+ finish_console_register uartdm putc=1, flush=1
+endfunc console_uartdm_register
+
+ /* -----------------------------------------------------------
+ * void console_uartdm_core_init(unused, uintptr_t base_addr)
+ * Function to initialize the console.
+ * In : r0 - unused
+ * r1 - base address
+ * Out: void
+ * Clobber list : r1, r2, r3
+ * -----------------------------------------------------------
+ */
+func console_uartdm_core_init
+ /*
+ * Try to flush remaining characters from the TX FIFO before resetting
+ * the transmitter. Unfortunately there is no good way to check if
+ * the transmitter is actually enabled (and will finish eventually),
+ * so use a timeout to avoid looping forever.
+ */
+ mov r2, #65536
+1:
+ ldr r3, [r1, #UART_DM_SR]
+ tst r3, #UART_DM_SR_TXEMT
+ bne 2f
+ subs r2, r2, #1
+ bne 1b
+ /* Timeout */
+
+2: /* Reset receiver */
+ mov r3, #UART_DM_CR_RESET_RX
+ str r3, [r1, #UART_DM_CR]
+
+ /* Reset transmitter */
+ mov r3, #UART_DM_CR_RESET_TX
+ str r3, [r1, #UART_DM_CR]
+
+ /*
+ * Disable BAM/DMA modes but enable single-character mode for TX.
+ * The single character mode allows simplifying the putc implementation
+ * since characters can be written directly to the FIFO instead of
+ * having to initiate a new transfer and waiting for its completion.
+ */
+ mov r3, #UART_DM_DMEN_TX_SC
+ str r3, [r1, #UART_DM_DMEN]
+
+ /* Enable transmitter */
+ mov r3, #UART_DM_CR_TX_ENABLE
+ str r3, [r1, #UART_DM_CR]
+
+ bx lr
+endfunc console_uartdm_core_init
+
+ /* -----------------------------------------------------------
+ * int console_uartdm_putc(int c, console_t *console)
+ * Function to output a character over the console.
+ * In : r0 - character to be printed
+ * r1 - pointer to console_t struct
+ * Out: r0 - printed character on success, < 0 on error.
+ * Clobber list : r0, r1, r2
+ * -----------------------------------------------------------
+ */
+func console_uartdm_putc
+ ldr r1, [r1, #CONSOLE_T_BASE]
+ b console_uartdm_core_putc
+endfunc console_uartdm_putc
+
+ /* -----------------------------------------------------------
+ * int console_uartdm_core_putc(int c, uintptr_t base_addr)
+ * Function to output a character over the console.
+ * In : r0 - character to be printed
+ * r1 - base address
+ * Out: r0 - printed character on success, < 0 on error.
+ * Clobber list : r2
+ * -----------------------------------------------------------
+ */
+func console_uartdm_core_putc
+ cmp r0, #'\n'
+ bne 2f
+
+1: /* Loop until TX FIFO has space */
+ ldr r2, [r1, #UART_DM_SR]
+ tst r2, #UART_DM_SR_TXRDY
+ beq 1b
+
+ /* Prepend '\r' to '\n' */
+ mov r2, #'\r'
+ str r2, [r1, #UART_DM_TF]
+
+2: /* Loop until TX FIFO has space */
+ ldr r2, [r1, #UART_DM_SR]
+ tst r2, #UART_DM_SR_TXRDY
+ beq 2b
+
+ /* Write character to FIFO */
+ str r0, [r1, #UART_DM_TF]
+ bx lr
+endfunc console_uartdm_core_putc
+
+ /* -----------------------------------------------------------
+ * void console_uartdm_flush(console_t *console)
+ * Function to force a write of all buffered data
+ * that has not been output.
+ * In : r0 - pointer to console_t struct
+ * Out: void
+ * Clobber list : r0, r1, r2, r3, r4, r5
+ * -----------------------------------------------------------
+ */
+func console_uartdm_flush
+ ldr r1, [r0, #CONSOLE_T_BASE]
+ b console_uartdm_core_flush
+endfunc console_uartdm_flush
+
+ /* -----------------------------------------------------------
+ * void console_uartdm_core_flush(unused, uintptr_t base_addr)
+ * Function to force a write of all buffered data
+ * that has not been output.
+ * In : r0 - unused
+ * r1 - base address
+ * Out: void
+ * Clobber list : r2
+ * -----------------------------------------------------------
+ */
+func console_uartdm_core_flush
+1: /* Loop until TX FIFO is empty */
+ ldr r2, [r1, #UART_DM_SR]
+ tst r2, #UART_DM_SR_TXEMT
+ beq 1b
+ bx lr
+endfunc console_uartdm_core_flush
diff --git a/plat/qti/msm8916/aarch64/msm8916_helpers.S b/plat/qti/msm8916/aarch64/msm8916_helpers.S
new file mode 100644
index 0000000..de9438a
--- /dev/null
+++ b/plat/qti/msm8916/aarch64/msm8916_helpers.S
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2021-2023, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+#include <platform_def.h>
+
+#include <msm8916_mmap.h>
+
+#if PLATFORM_CORE_COUNT > 1
+#define APCS_TCM_START_ADDR 0x10
+#else
+#define APCS_TCM_START_ADDR 0x34
+#endif
+#define APCS_TCM_REDIRECT_EN_0 BIT_32(0)
+
+ .globl plat_crash_console_init
+ .globl plat_crash_console_putc
+ .globl plat_crash_console_flush
+ .globl plat_panic_handler
+ .globl plat_my_core_pos
+ .globl plat_get_my_entrypoint
+ .globl plat_reset_handler
+ .globl platform_mem_init
+ .globl msm8916_entry_point
+
+ /* -------------------------------------------------
+ * int plat_crash_console_init(void)
+ * Initialize the crash console.
+ * Out: x0 - 1 on success, 0 on error
+ * Clobber list : x0 - x4
+ * -------------------------------------------------
+ */
+func plat_crash_console_init
+ mov_imm x1, BLSP_UART_BASE
+ mov x0, #1
+ b console_uartdm_core_init
+endfunc plat_crash_console_init
+
+ /* -------------------------------------------------
+ * int plat_crash_console_putc(int c)
+ * Print a character on the crash console.
+ * In : w0 - character to be printed
+ * Out: w0 - printed character on success
+ * Clobber list : x1, x2
+ * -------------------------------------------------
+ */
+func plat_crash_console_putc
+ mov_imm x1, BLSP_UART_BASE
+ b console_uartdm_core_putc
+endfunc plat_crash_console_putc
+
+ /* -------------------------------------------------
+ * void plat_crash_console_flush(void)
+ * Force a write of all buffered data that has not
+ * been output.
+ * Clobber list : x1, x2
+ * -------------------------------------------------
+ */
+func plat_crash_console_flush
+ mov_imm x1, BLSP_UART_BASE
+ b console_uartdm_core_flush
+endfunc plat_crash_console_flush
+
+ /* -------------------------------------------------
+ * void plat_panic_handler(void) __dead
+ * Called when an unrecoverable error occurs.
+ * -------------------------------------------------
+ */
+func plat_panic_handler
+ /* Try to shutdown/reset */
+ mov_imm x0, MPM_PS_HOLD
+ str wzr, [x0]
+1: b 1b
+endfunc plat_panic_handler
+
+ /* -------------------------------------------------
+ * unsigned int plat_my_core_pos(void)
+ * Out: x0 - index of the calling CPU
+ * -------------------------------------------------
+ */
+func plat_my_core_pos
+ .if PLATFORM_CORE_COUNT > 1
+ mrs x1, mpidr_el1
+ and x0, x1, #MPIDR_CPU_MASK
+ .if PLATFORM_CLUSTER_COUNT > 1
+ and x1, x1, #MPIDR_CLUSTER_MASK
+ orr x0, x0, x1, LSR #(MPIDR_AFFINITY_BITS - \
+ PLATFORM_CPU_PER_CLUSTER_SHIFT)
+ .endif
+ .else
+ /* There is just a single core so always 0 */
+ mov x0, #0
+ .endif
+ ret
+endfunc plat_my_core_pos
+
+ /* -------------------------------------------------
+ * uintptr_t plat_get_my_entrypoint(void)
+ * Distinguish cold and warm boot and return warm boot
+ * entry address if available.
+ * Out: x0 - warm boot entry point or 0 on cold boot
+ * -------------------------------------------------
+ */
+func plat_get_my_entrypoint
+ ldr x0, msm8916_entry_point
+ cbz x0, 1f
+ ret
+1:
+ /*
+ * Cold boot: Disable TCM redirect to L2 cache as early as
+ * possible to avoid crashes when making use of the cache.
+ */
+ mov_imm x1, APCS_CFG(0)
+ ldr w2, [x1, #APCS_TCM_START_ADDR]
+ and w2, w2, #~APCS_TCM_REDIRECT_EN_0
+ str w2, [x1, #APCS_TCM_START_ADDR]
+
+ /*
+ * After reset the CPU always starts executing at the fixed reset
+ * address (0x0), which does not match the link address of BL31.
+ * The "boot remapper" redirects all memory accesses to the real
+ * physical address in DRAM.
+ *
+ * For warm boots, this is already handled by loading the real
+ * entry point address above.
+ *
+ * For cold boots, check if the CPU is using the boot remapper,
+ * i.e. if bl31_entrypoint appears to be at the reset address (0x0).
+ */
+ adr x1, bl31_entrypoint
+ cbnz x1, 2f
+
+ /*
+ * Add the real BL31_BASE offset to the return address in the link
+ * register so the CPU will continue at the real address after return.
+ */
+ mov_imm x1, BL31_BASE
+ add lr, lr, x1
+2:
+ ret
+endfunc plat_get_my_entrypoint
+
+ /* -------------------------------------------------
+ * void platform_mem_init(void)
+ * Performs additional memory initialization early
+ * in the boot process.
+ * -------------------------------------------------
+ */
+func platform_mem_init
+ /* Nothing to do here, all memory is already initialized */
+ ret
+endfunc platform_mem_init
+
+ .data
+ .align 3
+
+ /* -------------------------------------------------
+ * Warm boot entry point for CPU. Set by PSCI code.
+ * -------------------------------------------------
+ */
+msm8916_entry_point:
+ .quad 0
diff --git a/plat/qti/msm8916/aarch64/uartdm_console.S b/plat/qti/msm8916/aarch64/uartdm_console.S
new file mode 100644
index 0000000..6c65daf
--- /dev/null
+++ b/plat/qti/msm8916/aarch64/uartdm_console.S
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2021-2023, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * Based on aarch64/skeleton_console.S:
+ * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <asm_macros.S>
+#include <console_macros.S>
+
+/* UART DM registers */
+#define UART_DM_DMEN 0x03c /* DMA / data packing */
+#define UART_DM_SR 0x0a4 /* status register */
+#define UART_DM_CR 0x0a8 /* command register */
+#define UART_DM_TF 0x100 /* transmit FIFO */
+
+#define UART_DM_DMEN_TX_SC BIT_32(4) /* TX single character mode */
+
+#define UART_DM_SR_TXRDY_BIT 2 /* TX FIFO has space */
+#define UART_DM_SR_TXEMT_BIT 3 /* TX FIFO is empty */
+
+#define UART_DM_CR_RESET_RX (U(0x01) << 4) /* reset receiver */
+#define UART_DM_CR_RESET_TX (U(0x02) << 4) /* reset transmitter */
+#define UART_DM_CR_TX_ENABLE BIT_32(2) /* enable transmitter */
+
+ .globl console_uartdm_register
+ .globl console_uartdm_core_init
+ .globl console_uartdm_putc
+ .globl console_uartdm_core_putc
+ .globl console_uartdm_flush
+ .globl console_uartdm_core_flush
+
+ /* -----------------------------------------------------------
+ * int console_uartdm_register(console_t *console,
+ * uintptr_t base_addr)
+ * Function to initialize and register the console. The caller
+ * needs to pass an empty console_t structure in which *MUST*
+ * be allocated in persistent memory (e.g. a global or static
+ * local variable, *NOT* on the stack).
+ * In : x0 - pointer to empty console_t structure
+ * x1 - base address
+ * Out: x0 - 1 on success, 0 on error
+ * Clobber list : x0 - x7
+ * -----------------------------------------------------------
+ */
+func console_uartdm_register
+ str x1, [x0, #CONSOLE_T_BASE]
+ mov x7, lr
+ bl console_uartdm_core_init
+ mov lr, x7
+
+ /* Register the new console */
+ finish_console_register uartdm putc=1, flush=1
+endfunc console_uartdm_register
+
+ /* -----------------------------------------------------------
+ * void console_uartdm_core_init(unused, uintptr_t base_addr)
+ * Function to initialize the console.
+ * In : x0 - unused
+ * x1 - base address
+ * Out: void
+ * Clobber list : x1, x2, x3
+ * -----------------------------------------------------------
+ */
+func console_uartdm_core_init
+ /*
+ * Try to flush remaining characters from the TX FIFO before resetting
+ * the transmitter. Unfortunately there is no good way to check if
+ * the transmitter is actually enabled (and will finish eventually),
+ * so use a timeout to avoid looping forever.
+ */
+ mov w2, #65536
+1:
+ ldr w3, [x1, #UART_DM_SR]
+ tbnz w3, #UART_DM_SR_TXEMT_BIT, 2f
+ subs w2, w2, #1
+ b.ne 1b
+ /* Timeout */
+
+2: /* Reset receiver */
+ mov w3, #UART_DM_CR_RESET_RX
+ str w3, [x1, #UART_DM_CR]
+
+ /* Reset transmitter */
+ mov w3, #UART_DM_CR_RESET_TX
+ str w3, [x1, #UART_DM_CR]
+
+ /*
+ * Disable BAM/DMA modes but enable single-character mode for TX.
+ * The single character mode allows simplifying the putc implementation
+ * since characters can be written directly to the FIFO instead of
+ * having to initiate a new transfer and waiting for its completion.
+ */
+ mov w3, #UART_DM_DMEN_TX_SC
+ str w3, [x1, #UART_DM_DMEN]
+
+ /* Enable transmitter */
+ mov w3, #UART_DM_CR_TX_ENABLE
+ str w3, [x1, #UART_DM_CR]
+
+ ret
+endfunc console_uartdm_core_init
+
+ /* -----------------------------------------------------------
+ * int console_uartdm_putc(int c, console_t *console)
+ * Function to output a character over the console.
+ * In : w0 - character to be printed
+ * x1 - pointer to console_t struct
+ * Out: w0 - printed character on success, < 0 on error.
+ * Clobber list : x0, x1, x2
+ * -----------------------------------------------------------
+ */
+func console_uartdm_putc
+ ldr x1, [x1, #CONSOLE_T_BASE]
+ b console_uartdm_core_putc
+endfunc console_uartdm_putc
+
+ /* -----------------------------------------------------------
+ * int console_uartdm_core_putc(int c, uintptr_t base_addr)
+ * Function to output a character over the console.
+ * In : w0 - character to be printed
+ * x1 - base address
+ * Out: w0 - printed character on success, < 0 on error.
+ * Clobber list : x2
+ * -----------------------------------------------------------
+ */
+func console_uartdm_core_putc
+ cmp w0, #'\n'
+ b.ne 2f
+
+1: /* Loop until TX FIFO has space */
+ ldr w2, [x1, #UART_DM_SR]
+ tbz w2, #UART_DM_SR_TXRDY_BIT, 1b
+
+ /* Prepend '\r' to '\n' */
+ mov w2, #'\r'
+ str w2, [x1, #UART_DM_TF]
+
+2: /* Loop until TX FIFO has space */
+ ldr w2, [x1, #UART_DM_SR]
+ tbz w2, #UART_DM_SR_TXRDY_BIT, 2b
+
+ /* Write character to FIFO */
+ str w0, [x1, #UART_DM_TF]
+ ret
+endfunc console_uartdm_core_putc
+
+ /* -----------------------------------------------------------
+ * void console_uartdm_flush(console_t *console)
+ * Function to force a write of all buffered data
+ * that has not been output.
+ * In : x0 - pointer to console_t struct
+ * Out: void
+ * Clobber list : x0, x1, x2, x3, x4, x5
+ * -----------------------------------------------------------
+ */
+func console_uartdm_flush
+ ldr x1, [x0, #CONSOLE_T_BASE]
+ b console_uartdm_core_flush
+endfunc console_uartdm_flush
+
+ /* -----------------------------------------------------------
+ * void console_uartdm_core_flush(unused, uintptr_t base_addr)
+ * Function to force a write of all buffered data
+ * that has not been output.
+ * In : x0 - unused
+ * x1 - base address
+ * Out: void
+ * Clobber list : x2
+ * -----------------------------------------------------------
+ */
+func console_uartdm_core_flush
+1: /* Loop until TX FIFO is empty */
+ ldr w2, [x1, #UART_DM_SR]
+ tbz w2, #UART_DM_SR_TXEMT_BIT, 1b
+ ret
+endfunc console_uartdm_core_flush
diff --git a/plat/qti/msm8916/include/msm8916_mmap.h b/plat/qti/msm8916/include/msm8916_mmap.h
new file mode 100644
index 0000000..20c5a57
--- /dev/null
+++ b/plat/qti/msm8916/include/msm8916_mmap.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021-2023, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MSM8916_MMAP_H
+#define MSM8916_MMAP_H
+
+#define PCNOC_BASE 0x00000000
+#define PCNOC_SIZE SZ_128M
+#define APCS_BASE 0x0b000000
+#define APCS_SIZE SZ_8M
+
+#define MPM_BASE (PCNOC_BASE + 0x04a0000)
+#define MPM_PS_HOLD (MPM_BASE + 0xb000)
+
+#define TLMM_BASE (PCNOC_BASE + 0x1000000)
+#define TLMM_GPIO_CFG(n) (TLMM_BASE + ((n) * 0x1000))
+
+#define GCC_BASE (PCNOC_BASE + 0x1800000)
+
+#define APPS_SMMU_BASE (PCNOC_BASE + 0x1e00000)
+#define APPS_SMMU_QCOM (APPS_SMMU_BASE + 0xf0000)
+#define GPU_SMMU_BASE (PCNOC_BASE + 0x1f00000)
+
+#define BLSP1_BASE (PCNOC_BASE + 0x7880000)
+#define BLSP1_UART_BASE(n) (BLSP1_BASE + 0x2f000 + (((n) - 1) * 0x1000))
+#define BLSP_UART_BASE BLSP1_UART_BASE(QTI_UART_NUM)
+
+#define APCS_QGIC2_BASE (APCS_BASE + 0x00000)
+#define APCS_QGIC2_GICD (APCS_QGIC2_BASE + 0x0000)
+#define APCS_QGIC2_GICC (APCS_QGIC2_BASE + 0x2000)
+#define APCS_BANKED_ACS (APCS_BASE + 0x08000)
+#define APCS_BANKED_SAW2 (APCS_BASE + 0x09000)
+
+#define _APCS_CLUSTER(cluster) (APCS_BASE + ((cluster) * 0x100000))
+#define _APCS_CPU(cluster, cpu) (_APCS_CLUSTER(cluster) + ((cpu) * 0x10000))
+#define APCS_CFG(cluster) (_APCS_CLUSTER(cluster) + 0x10000)
+#define APCS_GLB(cluster) (_APCS_CLUSTER(cluster) + 0x11000)
+#define APCS_L2_SAW2(cluster) (_APCS_CLUSTER(cluster) + 0x12000)
+#define APCS_QTMR(cluster) (_APCS_CLUSTER(cluster) + 0x20000)
+#define APCS_ALIAS_ACS(cluster, cpu) (_APCS_CPU(cluster, cpu) + 0x88000)
+#define APCS_ALIAS_SAW2(cluster, cpu) (_APCS_CPU(cluster, cpu) + 0x89000)
+
+/* Only on platforms with multiple clusters (e.g. MSM8939) */
+#define APCS_CCI_BASE (APCS_BASE + 0x1c0000)
+#define APCS_CCI_SAW2 (APCS_BASE + 0x1d2000)
+#define APCS_CCI_ACS (APCS_BASE + 0x1d4000)
+
+#endif /* MSM8916_MMAP_H */
diff --git a/plat/qti/msm8916/include/plat_macros.S b/plat/qti/msm8916/include/plat_macros.S
new file mode 100644
index 0000000..552add2
--- /dev/null
+++ b/plat/qti/msm8916/include/plat_macros.S
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PLAT_MACROS_S
+#define PLAT_MACROS_S
+
+#include <arm_macros.S>
+
+#include <msm8916_mmap.h>
+
+ /* ---------------------------------------------
+ * The below required platform porting macro
+ * prints out relevant GIC registers whenever
+ * an unhandled exception is taken in BL31.
+ * Clobbers: x0 - x10, x16, x17, sp
+ * ---------------------------------------------
+ */
+ .macro plat_crash_print_regs
+ mov_imm x16, APCS_QGIC2_GICD
+ mov_imm x17, APCS_QGIC2_GICC
+ arm_print_gic_regs
+ .endm
+
+#endif /* PLAT_MACROS_S */
diff --git a/plat/qti/msm8916/include/platform_def.h b/plat/qti/msm8916/include/platform_def.h
new file mode 100644
index 0000000..a5baacd
--- /dev/null
+++ b/plat/qti/msm8916/include/platform_def.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2021-2023, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#ifndef PLATFORM_DEF_H
+#define PLATFORM_DEF_H
+
+#include <plat/common/common_def.h>
+
+#ifdef __aarch64__
+/*
+ * There is at least 1 MiB available for BL31. However, at the moment the
+ * "msm8916_entry_point" variable in the data section is read through the
+ * 64 KiB region of the "boot remapper" after reset. For simplicity, limit
+ * the end of the data section (BL31_PROGBITS_LIMIT) to 64 KiB for now and
+ * the overall limit to 128 KiB. This could be increased if needed by placing
+ * the "msm8916_entry_point" variable explicitly in the first 64 KiB of BL31.
+ */
+#define BL31_LIMIT (BL31_BASE + SZ_128K)
+#define BL31_PROGBITS_LIMIT (BL31_BASE + SZ_64K)
+#endif
+#define BL32_LIMIT (BL32_BASE + SZ_128K)
+
+#define CACHE_WRITEBACK_GRANULE U(64)
+#define PLATFORM_STACK_SIZE SZ_4K
+
+/* CPU topology: one or two clusters with 4 cores each */
+#ifdef PLAT_msm8939
+#define PLATFORM_CLUSTER_COUNT U(2)
+#else
+#define PLATFORM_CLUSTER_COUNT U(1)
+#endif
+#if defined(PLAT_mdm9607)
+#define PLATFORM_CPU_PER_CLUSTER_SHIFT U(0) /* 1 */
+#else
+#define PLATFORM_CPU_PER_CLUSTER_SHIFT U(2) /* 4 */
+#endif
+#define PLATFORM_CPUS_PER_CLUSTER (1 << PLATFORM_CPU_PER_CLUSTER_SHIFT)
+#define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER_COUNT * \
+ PLATFORM_CPUS_PER_CLUSTER)
+
+/* Power management */
+#define PLATFORM_SYSTEM_COUNT U(1)
+#define PLAT_NUM_PWR_DOMAINS (PLATFORM_SYSTEM_COUNT + \
+ PLATFORM_CLUSTER_COUNT + \
+ PLATFORM_CORE_COUNT)
+#define PLAT_MAX_PWR_LVL MPIDR_AFFLVL2
+#define PLAT_MAX_RET_STATE U(2)
+#define PLAT_MAX_OFF_STATE U(3)
+
+/* Translation tables */
+#define MAX_MMAP_REGIONS 8
+#define MAX_XLAT_TABLES 4
+
+#define PLAT_PHY_ADDR_SPACE_SIZE (ULL(1) << 32)
+#define PLAT_VIRT_ADDR_SPACE_SIZE (ULL(1) << 32)
+
+/* Timer */
+#define PLAT_SYSCNT_FREQ 19200000
+#define IRQ_SEC_PHY_TIMER (16 + 2) /* PPI #2 */
+
+/*
+ * The Qualcomm QGIC2 implementation seems to have PIDR0-4 and PIDR4-7
+ * erroneously swapped for some reason. PIDR2 is actually at 0xFD8.
+ * Override the address in <drivers/arm/gicv2.h> to avoid a failing assert().
+ */
+#define GICD_PIDR2_GICV2 U(0xFD8)
+
+/* TSP */
+#define TSP_IRQ_SEC_PHY_TIMER IRQ_SEC_PHY_TIMER
+#define TSP_SEC_MEM_BASE BL32_BASE
+#define TSP_SEC_MEM_SIZE (BL32_LIMIT - BL32_BASE)
+
+#endif /* PLATFORM_DEF_H */
diff --git a/plat/qti/msm8916/include/uartdm_console.h b/plat/qti/msm8916/include/uartdm_console.h
new file mode 100644
index 0000000..0f09ba8
--- /dev/null
+++ b/plat/qti/msm8916/include/uartdm_console.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2021, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef UARTDM_CONSOLE_H
+#define UARTDM_CONSOLE_H
+
+int console_uartdm_register(console_t *console, uintptr_t base_addr);
+
+#endif /* UARTDM_CONSOLE_H */
diff --git a/plat/qti/msm8916/msm8916_bl31_setup.c b/plat/qti/msm8916/msm8916_bl31_setup.c
new file mode 100644
index 0000000..c588020
--- /dev/null
+++ b/plat/qti/msm8916/msm8916_bl31_setup.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2021-2023, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <arch.h>
+#include <common/debug.h>
+#include <plat/common/platform.h>
+
+#include "msm8916_config.h"
+#include "msm8916_setup.h"
+
+static struct {
+ entry_point_info_t bl32;
+ entry_point_info_t bl33;
+} image_ep_info = {
+ /* BL32 entry point */
+ SET_STATIC_PARAM_HEAD(bl32, PARAM_EP, VERSION_1,
+ entry_point_info_t, SECURE),
+ .bl32.pc = BL32_BASE,
+
+ /* BL33 entry point */
+ SET_STATIC_PARAM_HEAD(bl33, PARAM_EP, VERSION_1,
+ entry_point_info_t, NON_SECURE),
+ .bl33.pc = PRELOADED_BL33_BASE,
+ .bl33.spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS),
+};
+
+void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
+ u_register_t arg2, u_register_t arg3)
+{
+ msm8916_early_platform_setup();
+ msm8916_configure_early();
+}
+
+void bl31_plat_arch_setup(void)
+{
+ msm8916_plat_arch_setup(BL31_BASE, BL31_END - BL31_BASE);
+ enable_mmu_el3(0);
+}
+
+void bl31_platform_setup(void)
+{
+ INFO("BL31: Platform setup start\n");
+ msm8916_platform_setup();
+ msm8916_configure();
+ INFO("BL31: Platform setup done\n");
+}
+
+entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type)
+{
+ switch (type) {
+ case SECURE:
+ return &image_ep_info.bl32;
+ case NON_SECURE:
+ return &image_ep_info.bl33;
+ default:
+ assert(sec_state_is_valid(type));
+ return NULL;
+ }
+}
diff --git a/plat/qti/msm8916/msm8916_config.c b/plat/qti/msm8916/msm8916_config.c
new file mode 100644
index 0000000..0ac604b
--- /dev/null
+++ b/plat/qti/msm8916/msm8916_config.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2021-2023, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <arch.h>
+#include <drivers/arm/cci.h>
+#include <lib/mmio.h>
+
+#include "msm8916_config.h"
+#include "msm8916_gicv2.h"
+#include <msm8916_mmap.h>
+#include <platform_def.h>
+
+static const int cci_map[] = { 3, 4 };
+
+void msm8916_configure_early(void)
+{
+ if (PLATFORM_CLUSTER_COUNT > 1) {
+ cci_init(APCS_CCI_BASE, cci_map, ARRAY_SIZE(cci_map));
+ cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1()));
+ }
+}
+
+static void msm8916_configure_timer(uintptr_t base)
+{
+ /* Set timer frequency */
+ mmio_write_32(base + CNTCTLBASE_CNTFRQ, PLAT_SYSCNT_FREQ);
+
+ /* Make all timer frames available to non-secure world */
+ mmio_write_32(base + CNTNSAR, GENMASK_32(7, 0));
+}
+
+/*
+ * The APCS register regions always start with a SECURE register that should
+ * be cleared to 0 to only allow secure access. Since BL31 handles most of
+ * the CPU power management, most of them can be cleared to secure access only.
+ */
+#define APCS_GLB_SECURE_STS_NS BIT_32(0)
+#define APCS_GLB_SECURE_PWR_NS BIT_32(1)
+#if PLATFORM_CORE_COUNT > 1
+#define APCS_BOOT_START_ADDR_SEC 0x04
+#define APCS_AA64NAA32_REG 0x0c
+#else
+#define APCS_BOOT_START_ADDR_SEC 0x18
+#endif
+#define REMAP_EN BIT_32(0)
+
+static void msm8916_configure_apcs_cluster(unsigned int cluster)
+{
+ uintptr_t cfg = APCS_CFG(cluster);
+ unsigned int cpu;
+
+ /* Disallow non-secure access to boot remapper / TCM registers */
+ mmio_write_32(cfg, 0);
+
+ /*
+ * Disallow non-secure access to power management registers.
+ * However, allow STS and PWR since those also seem to control access
+ * to CPU frequency related registers (e.g. APCS_CMD_RCGR). If these
+ * bits are not set, CPU frequency control fails in the non-secure world.
+ */
+ mmio_write_32(APCS_GLB(cluster),
+ APCS_GLB_SECURE_STS_NS | APCS_GLB_SECURE_PWR_NS);
+
+ if (PLATFORM_CORE_COUNT > 1) {
+ /* Disallow non-secure access to L2 SAW2 */
+ mmio_write_32(APCS_L2_SAW2(cluster), 0);
+
+ /* Disallow non-secure access to CPU ACS and SAW2 */
+ for (cpu = 0; cpu < PLATFORM_CPUS_PER_CLUSTER; cpu++) {
+ mmio_write_32(APCS_ALIAS_ACS(cluster, cpu), 0);
+ mmio_write_32(APCS_ALIAS_SAW2(cluster, cpu), 0);
+ }
+ } else {
+ /* There is just one core so no aliases exist */
+ mmio_write_32(APCS_BANKED_ACS, 0);
+ mmio_write_32(APCS_BANKED_SAW2, 0);
+ }
+
+#ifdef __aarch64__
+ /* Make sure all further warm boots end up in BL31 and aarch64 state */
+ CASSERT((BL31_BASE & 0xffff) == 0, assert_bl31_base_64k_aligned);
+ mmio_write_32(cfg + APCS_BOOT_START_ADDR_SEC, BL31_BASE | REMAP_EN);
+ mmio_write_32(cfg + APCS_AA64NAA32_REG, 1);
+#else
+ /* Make sure all further warm boots end up in BL32 */
+ CASSERT((BL32_BASE & 0xffff) == 0, assert_bl32_base_64k_aligned);
+ mmio_write_32(cfg + APCS_BOOT_START_ADDR_SEC, BL32_BASE | REMAP_EN);
+#endif
+
+ msm8916_configure_timer(APCS_QTMR(cluster));
+}
+
+static void msm8916_configure_apcs(void)
+{
+ unsigned int cluster;
+
+ for (cluster = 0; cluster < PLATFORM_CLUSTER_COUNT; cluster++) {
+ msm8916_configure_apcs_cluster(cluster);
+ }
+
+ if (PLATFORM_CLUSTER_COUNT > 1) {
+ /* Disallow non-secure access to CCI ACS and SAW2 */
+ mmio_write_32(APCS_CCI_ACS, 0);
+ mmio_write_32(APCS_CCI_SAW2, 0);
+ }
+}
+
+/*
+ * MSM8916 has a special "interrupt aggregation logic" in the APPS SMMU,
+ * which allows routing context bank interrupts to one of 3 interrupt numbers
+ * ("TZ/HYP/NS"). Route all interrupts to the non-secure interrupt number
+ * by default to avoid special setup on the non-secure side.
+ */
+#define CLK_OFF BIT_32(31)
+#define GCC_APSS_TCU_CBCR (GCC_BASE + 0x12018)
+#define GCC_GFX_TCU_CBCR (GCC_BASE + 0x12020)
+#define GCC_SMMU_CFG_CBCR (GCC_BASE + 0x12038)
+#define GCC_RPM_SMMU_CLOCK_BRANCH_ENA_VOTE (GCC_BASE + 0x3600c)
+#define GCC_APCS_SMMU_CLOCK_BRANCH_ENA_VOTE (GCC_BASE + 0x4500c)
+#define APSS_TCU_CLK_ENA BIT_32(1)
+#define GFX_TCU_CLK_ENA BIT_32(2)
+#define GFX_TBU_CLK_ENA BIT_32(3)
+#define SMMU_CFG_CLK_ENA BIT_32(12)
+#define APPS_SMMU_INTR_SEL_NS (APPS_SMMU_QCOM + 0x2000)
+#define APPS_SMMU_INTR_SEL_NS_EN_ALL U(0xffffffff)
+
+#define SMMU_SACR 0x010
+#define SMMU_SACR_CACHE_LOCK BIT_32(26)
+#define SMMU_IDR7 0x03c
+#define SMMU_IDR7_MINOR(val) (((val) >> 0) & 0xf)
+#define SMMU_IDR7_MAJOR(val) (((val) >> 4) & 0xf)
+
+static void msm8916_smmu_cache_unlock(uintptr_t smmu_base, uintptr_t clk_cbcr)
+{
+ uint32_t version;
+
+ /* Wait for clock */
+ while (mmio_read_32(clk_cbcr) & CLK_OFF) {
+ }
+
+ version = mmio_read_32(smmu_base + SMMU_IDR7);
+ VERBOSE("SMMU(0x%lx) r%dp%d\n", smmu_base,
+ SMMU_IDR7_MAJOR(version), SMMU_IDR7_MINOR(version));
+
+ /* For SMMU r2p0+ clear CACHE_LOCK to allow writes to CBn_ACTLR */
+ if (SMMU_IDR7_MAJOR(version) >= 2) {
+ mmio_clrbits_32(smmu_base + SMMU_SACR, SMMU_SACR_CACHE_LOCK);
+ }
+}
+
+static void msm8916_configure_smmu(void)
+{
+ uint32_t ena_bits = APSS_TCU_CLK_ENA | SMMU_CFG_CLK_ENA;
+
+ /* Single core (MDM) platforms do not have a GPU */
+ if (PLATFORM_CORE_COUNT > 1) {
+ ena_bits |= GFX_TCU_CLK_ENA | GFX_TBU_CLK_ENA;
+ }
+
+ /* Enable SMMU clocks to enable register access */
+ mmio_write_32(GCC_APCS_SMMU_CLOCK_BRANCH_ENA_VOTE, ena_bits);
+
+ /* Wait for configuration clock */
+ while (mmio_read_32(GCC_SMMU_CFG_CBCR) & CLK_OFF) {
+ }
+
+ /* Route all context bank interrupts to non-secure interrupt */
+ mmio_write_32(APPS_SMMU_INTR_SEL_NS, APPS_SMMU_INTR_SEL_NS_EN_ALL);
+
+ /* Clear sACR.CACHE_LOCK bit if needed for MMU-500 r2p0+ */
+ msm8916_smmu_cache_unlock(APPS_SMMU_BASE, GCC_APSS_TCU_CBCR);
+ if (PLATFORM_CORE_COUNT > 1) {
+ msm8916_smmu_cache_unlock(GPU_SMMU_BASE, GCC_GFX_TCU_CBCR);
+ }
+
+ /*
+ * Keep APCS vote for SMMU clocks for rest of booting process, but make
+ * sure other vote registers (such as RPM) do not keep permanent votes.
+ */
+ VERBOSE("Clearing GCC_RPM_SMMU_CLOCK_BRANCH_ENA_VOTE (was: 0x%x)\n",
+ mmio_read_32(GCC_RPM_SMMU_CLOCK_BRANCH_ENA_VOTE));
+ mmio_write_32(GCC_RPM_SMMU_CLOCK_BRANCH_ENA_VOTE, 0);
+}
+
+void msm8916_configure(void)
+{
+ msm8916_gicv2_configure();
+ msm8916_configure_apcs();
+ msm8916_configure_smmu();
+}
diff --git a/plat/qti/msm8916/msm8916_config.h b/plat/qti/msm8916/msm8916_config.h
new file mode 100644
index 0000000..977d02c
--- /dev/null
+++ b/plat/qti/msm8916/msm8916_config.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2022-2023, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MSM8916_CONFIG_H
+#define MSM8916_CONFIG_H
+
+void msm8916_configure(void);
+void msm8916_configure_early(void);
+
+#endif /* MSM8916_CONFIG_H */
diff --git a/plat/qti/msm8916/msm8916_cpu_boot.c b/plat/qti/msm8916/msm8916_cpu_boot.c
new file mode 100644
index 0000000..d6faa59
--- /dev/null
+++ b/plat/qti/msm8916/msm8916_cpu_boot.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2021-2022, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <lib/mmio.h>
+
+#include "msm8916_pm.h"
+
+#define CPU_PWR_CTL 0x4
+#define APC_PWR_GATE_CTL 0x14
+
+#define CPU_PWR_CTL_CLAMP BIT_32(0)
+#define CPU_PWR_CTL_CORE_MEM_CLAMP BIT_32(1)
+#define CPU_PWR_CTL_L1_RST_DIS BIT_32(2)
+#define CPU_PWR_CTL_CORE_MEM_HS BIT_32(3)
+#define CPU_PWR_CTL_CORE_RST BIT_32(4)
+#define CPU_PWR_CTL_COREPOR_RST BIT_32(5)
+#define CPU_PWR_CTL_GATE_CLK BIT_32(6)
+#define CPU_PWR_CTL_CORE_PWRD_UP BIT_32(7)
+
+#define APC_PWR_GATE_CTL_GHDS_EN BIT_32(0)
+#define APC_PWR_GATE_CTL_GHDS_CNT(cnt) ((cnt) << 24)
+
+#define PWR_CTL_OVERRIDE 0xc
+#define L2_PWR_CTL 0x14
+#define L2_PWR_STATUS 0x18
+#define CORE_CBCR 0x58
+
+#define PWR_CTL_OVERRIDE_PRESETDBG BIT_32(22)
+
+#define L2_PWR_CTL_L2_ARRAY_HS BIT_32(0)
+#define L2_PWR_CTL_SCU_ARRAY_HS BIT_32(1)
+#define L2_PWR_CTL_L2_RST_DIS BIT_32(2)
+#define L2_PWR_CTL_L2_HS_CLAMP BIT_32(8)
+#define L2_PWR_CTL_L2_HS_EN BIT_32(9)
+#define L2_PWR_CTL_L2_HS_RST BIT_32(10)
+#define L2_PWR_CTL_L2_SLEEP_STATE BIT_32(11)
+#define L2_PWR_CTL_SYS_RESET BIT_32(12)
+#define L2_PWR_CTL_L2_RET_SLP BIT_32(13)
+#define L2_PWR_CTL_SCU_ARRAY_HS_CLAMP BIT_32(14)
+#define L2_PWR_CTL_L2_ARRAY_HS_CLAMP BIT_32(15)
+#define L2_PWR_CTL_L2_HS_CNT(cnt) ((cnt) << 16)
+#define L2_PWR_CTL_PMIC_APC_ON BIT_32(28)
+
+#define L2_PWR_STATUS_L2_HS_STS BIT_32(9)
+
+#define CORE_CBCR_CLK_ENABLE BIT_32(0)
+#define CORE_CBCR_HW_CTL BIT_32(1)
+
+/* Boot a secondary CPU core for the first time. */
+void msm8916_cpu_boot(uintptr_t acs)
+{
+ uint32_t pwr_ctl;
+
+ VERBOSE("PSCI: Powering on CPU @ 0x%08lx\n", acs);
+
+ pwr_ctl = CPU_PWR_CTL_CLAMP | CPU_PWR_CTL_CORE_MEM_CLAMP |
+ CPU_PWR_CTL_CORE_RST | CPU_PWR_CTL_COREPOR_RST;
+ mmio_write_32(acs + CPU_PWR_CTL, pwr_ctl);
+ dsb();
+
+ mmio_write_32(acs + APC_PWR_GATE_CTL, APC_PWR_GATE_CTL_GHDS_EN |
+ APC_PWR_GATE_CTL_GHDS_CNT(16));
+ dsb();
+ udelay(2);
+
+ pwr_ctl &= ~CPU_PWR_CTL_CORE_MEM_CLAMP;
+ mmio_write_32(acs + CPU_PWR_CTL, pwr_ctl);
+ dsb();
+
+ pwr_ctl |= CPU_PWR_CTL_CORE_MEM_HS;
+ mmio_write_32(acs + CPU_PWR_CTL, pwr_ctl);
+ dsb();
+ udelay(2);
+
+ pwr_ctl &= ~CPU_PWR_CTL_CLAMP;
+ mmio_write_32(acs + CPU_PWR_CTL, pwr_ctl);
+ dsb();
+ udelay(2);
+
+ pwr_ctl &= ~(CPU_PWR_CTL_CORE_RST | CPU_PWR_CTL_COREPOR_RST);
+ mmio_write_32(acs + CPU_PWR_CTL, pwr_ctl);
+ dsb();
+
+ pwr_ctl |= CPU_PWR_CTL_CORE_PWRD_UP;
+ mmio_write_32(acs + CPU_PWR_CTL, pwr_ctl);
+ dsb();
+}
+
+/* Power on cluster L2 cache for the first time. */
+void msm8916_l2_boot(uintptr_t base)
+{
+ uint32_t pwr_ctl, cbcr, ovr;
+
+ /* Skip if cluster L2 is already powered on */
+ if (mmio_read_32(base + L2_PWR_STATUS) & L2_PWR_STATUS_L2_HS_STS) {
+ VERBOSE("PSCI: L2 cache @ 0x%08lx is already powered on\n", base);
+ return;
+ }
+
+ VERBOSE("PSCI: Powering on L2 cache @ 0x%08lx\n", base);
+
+ pwr_ctl = L2_PWR_CTL_L2_HS_CLAMP | L2_PWR_CTL_L2_HS_EN |
+ L2_PWR_CTL_L2_HS_RST | L2_PWR_CTL_SYS_RESET |
+ L2_PWR_CTL_SCU_ARRAY_HS_CLAMP | L2_PWR_CTL_L2_ARRAY_HS_CLAMP |
+ L2_PWR_CTL_L2_HS_CNT(16);
+ mmio_write_32(base + L2_PWR_CTL, pwr_ctl);
+
+ ovr = PWR_CTL_OVERRIDE_PRESETDBG;
+ mmio_write_32(base + PWR_CTL_OVERRIDE, ovr);
+ dsb();
+ udelay(2);
+
+ pwr_ctl &= ~(L2_PWR_CTL_SCU_ARRAY_HS_CLAMP |
+ L2_PWR_CTL_L2_ARRAY_HS_CLAMP);
+ mmio_write_32(base + L2_PWR_CTL, pwr_ctl);
+
+ pwr_ctl |= (L2_PWR_CTL_L2_ARRAY_HS | L2_PWR_CTL_SCU_ARRAY_HS);
+ mmio_write_32(base + L2_PWR_CTL, pwr_ctl);
+ dsb();
+ udelay(2);
+
+ cbcr = CORE_CBCR_CLK_ENABLE;
+ mmio_write_32(base + CORE_CBCR, cbcr);
+
+ pwr_ctl &= ~L2_PWR_CTL_L2_HS_CLAMP;
+ mmio_write_32(base + L2_PWR_CTL, pwr_ctl);
+ dsb();
+ udelay(2);
+
+ ovr &= ~PWR_CTL_OVERRIDE_PRESETDBG;
+ mmio_write_32(base + PWR_CTL_OVERRIDE, ovr);
+
+ pwr_ctl &= ~(L2_PWR_CTL_L2_HS_RST | L2_PWR_CTL_SYS_RESET);
+ mmio_write_32(base + L2_PWR_CTL, pwr_ctl);
+ dsb();
+ udelay(54);
+
+ pwr_ctl |= L2_PWR_CTL_PMIC_APC_ON;
+ mmio_write_32(base + L2_PWR_CTL, pwr_ctl);
+
+ cbcr |= CORE_CBCR_HW_CTL;
+ mmio_write_32(base + CORE_CBCR, cbcr);
+ dsb();
+}
diff --git a/plat/qti/msm8916/msm8916_gicv2.c b/plat/qti/msm8916/msm8916_gicv2.c
new file mode 100644
index 0000000..82cecf2
--- /dev/null
+++ b/plat/qti/msm8916/msm8916_gicv2.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2021, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <common/debug.h>
+#include <drivers/arm/gicv2.h>
+#include <lib/mmio.h>
+
+#include "msm8916_gicv2.h"
+#include <msm8916_mmap.h>
+#include <platform_def.h>
+
+#define IRQ_SEC_SGI_0 8
+#define IRQ_SEC_SGI_1 9
+#define IRQ_SEC_SGI_2 10
+#define IRQ_SEC_SGI_3 11
+#define IRQ_SEC_SGI_4 12
+#define IRQ_SEC_SGI_5 13
+#define IRQ_SEC_SGI_6 14
+#define IRQ_SEC_SGI_7 15
+
+static const interrupt_prop_t msm8916_interrupt_props[] = {
+ INTR_PROP_DESC(IRQ_SEC_SGI_0, GIC_HIGHEST_SEC_PRIORITY,
+ GICV2_INTR_GROUP0, GIC_INTR_CFG_LEVEL),
+ INTR_PROP_DESC(IRQ_SEC_SGI_1, GIC_HIGHEST_SEC_PRIORITY,
+ GICV2_INTR_GROUP0, GIC_INTR_CFG_LEVEL),
+ INTR_PROP_DESC(IRQ_SEC_SGI_2, GIC_HIGHEST_SEC_PRIORITY,
+ GICV2_INTR_GROUP0, GIC_INTR_CFG_LEVEL),
+ INTR_PROP_DESC(IRQ_SEC_SGI_3, GIC_HIGHEST_SEC_PRIORITY,
+ GICV2_INTR_GROUP0, GIC_INTR_CFG_LEVEL),
+ INTR_PROP_DESC(IRQ_SEC_SGI_4, GIC_HIGHEST_SEC_PRIORITY,
+ GICV2_INTR_GROUP0, GIC_INTR_CFG_LEVEL),
+ INTR_PROP_DESC(IRQ_SEC_SGI_5, GIC_HIGHEST_SEC_PRIORITY,
+ GICV2_INTR_GROUP0, GIC_INTR_CFG_LEVEL),
+ INTR_PROP_DESC(IRQ_SEC_SGI_6, GIC_HIGHEST_SEC_PRIORITY,
+ GICV2_INTR_GROUP0, GIC_INTR_CFG_LEVEL),
+ INTR_PROP_DESC(IRQ_SEC_SGI_7, GIC_HIGHEST_SEC_PRIORITY,
+ GICV2_INTR_GROUP0, GIC_INTR_CFG_LEVEL),
+ INTR_PROP_DESC(IRQ_SEC_PHY_TIMER, GIC_HIGHEST_SEC_PRIORITY,
+ GICV2_INTR_GROUP0, GIC_INTR_CFG_LEVEL),
+};
+
+static const gicv2_driver_data_t msm8916_gic_data = {
+ .gicd_base = APCS_QGIC2_GICD,
+ .gicc_base = APCS_QGIC2_GICC,
+ .interrupt_props = msm8916_interrupt_props,
+ .interrupt_props_num = ARRAY_SIZE(msm8916_interrupt_props),
+};
+
+void msm8916_gicv2_init(void)
+{
+ gicv2_driver_init(&msm8916_gic_data);
+}
+
+void msm8916_gicv2_configure(void)
+{
+ gicv2_distif_init();
+ gicv2_pcpu_distif_init();
+ gicv2_cpuif_enable();
+}
diff --git a/plat/qti/msm8916/msm8916_gicv2.h b/plat/qti/msm8916/msm8916_gicv2.h
new file mode 100644
index 0000000..919e294
--- /dev/null
+++ b/plat/qti/msm8916/msm8916_gicv2.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2021, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MSM8916_GICV2_H
+#define MSM8916_GICV2_H
+
+void msm8916_gicv2_init(void);
+void msm8916_gicv2_configure(void);
+
+#endif /* MSM8916_GICV2_H */
diff --git a/plat/qti/msm8916/msm8916_pm.c b/plat/qti/msm8916/msm8916_pm.c
new file mode 100644
index 0000000..fd44f04
--- /dev/null
+++ b/plat/qti/msm8916/msm8916_pm.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2021-2022, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/arm/cci.h>
+#include <drivers/arm/gicv2.h>
+#include <drivers/delay_timer.h>
+#include <lib/mmio.h>
+#include <lib/psci/psci.h>
+#include <plat/common/platform.h>
+
+#include <msm8916_mmap.h>
+#include "msm8916_pm.h"
+
+/*
+ * On platforms with two clusters the index of the APCS memory region is swapped
+ * compared to the MPIDR cluster affinity level: APCS cluster 0 manages CPUs
+ * with cluster affinity level 1, while APCS cluster 1 manages CPUs with level 0.
+ *
+ * On platforms with a single cluster there is only one APCS memory region.
+ */
+#if PLATFORM_CLUSTER_COUNT == 2
+#define MPIDR_APCS_CLUSTER(mpidr) !MPIDR_AFFLVL1_VAL(mpidr)
+#else
+#define MPIDR_APCS_CLUSTER(mpidr) 0
+#endif
+
+#define CLUSTER_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL1])
+
+static int msm8916_pwr_domain_on(u_register_t mpidr)
+{
+ /* Should be never called on single-core platforms */
+ if (PLATFORM_CORE_COUNT == 1) {
+ assert(false);
+ return PSCI_E_ALREADY_ON;
+ }
+
+ /* Power on L2 cache and secondary CPU core for the first time */
+ if (PLATFORM_CLUSTER_COUNT > 1) {
+ msm8916_l2_boot(APCS_GLB(MPIDR_APCS_CLUSTER(mpidr)));
+ }
+ msm8916_cpu_boot(APCS_ALIAS_ACS(MPIDR_APCS_CLUSTER(mpidr),
+ MPIDR_AFFLVL0_VAL(mpidr)));
+ return PSCI_E_SUCCESS;
+}
+
+static void msm8916_pwr_domain_on_finish(const psci_power_state_t *target_state)
+{
+ /* Should be never called on single-core platforms */
+ if (PLATFORM_CORE_COUNT == 1) {
+ assert(false);
+ return;
+ }
+
+ if (PLATFORM_CLUSTER_COUNT > 1 &&
+ CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
+ cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1()));
+ }
+
+ gicv2_pcpu_distif_init();
+ gicv2_cpuif_enable();
+}
+
+static void __dead2 msm8916_system_reset(void)
+{
+ mmio_write_32(MPM_PS_HOLD, 0);
+ mdelay(1000);
+
+ ERROR("PSCI: System reset failed\n");
+ panic();
+}
+
+static const plat_psci_ops_t msm8916_psci_ops = {
+ .pwr_domain_on = msm8916_pwr_domain_on,
+ .pwr_domain_on_finish = msm8916_pwr_domain_on_finish,
+ .system_off = msm8916_system_reset,
+ .system_reset = msm8916_system_reset,
+};
+
+/* Defined and used in msm8916_helpers.S */
+extern uintptr_t msm8916_entry_point;
+
+int plat_setup_psci_ops(uintptr_t sec_entrypoint,
+ const plat_psci_ops_t **psci_ops)
+{
+ /*
+ * The entry point is read with caches off (and even from two different
+ * physical addresses when read through the "boot remapper"), so make
+ * sure it is flushed to memory.
+ */
+ msm8916_entry_point = sec_entrypoint;
+ flush_dcache_range((uintptr_t)&msm8916_entry_point, sizeof(uintptr_t));
+
+ *psci_ops = &msm8916_psci_ops;
+ return 0;
+}
diff --git a/plat/qti/msm8916/msm8916_pm.h b/plat/qti/msm8916/msm8916_pm.h
new file mode 100644
index 0000000..f301d3c
--- /dev/null
+++ b/plat/qti/msm8916/msm8916_pm.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2021-2022, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MSM8916_PM_H
+#define MSM8916_PM_H
+
+void msm8916_cpu_boot(uintptr_t acs);
+void msm8916_l2_boot(uintptr_t base);
+
+#endif /* MSM8916_PM_H */
diff --git a/plat/qti/msm8916/msm8916_setup.c b/plat/qti/msm8916/msm8916_setup.c
new file mode 100644
index 0000000..69c0d78
--- /dev/null
+++ b/plat/qti/msm8916/msm8916_setup.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2021-2023, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <common/bl_common.h>
+#include <drivers/console.h>
+#include <drivers/generic_delay_timer.h>
+#include <lib/mmio.h>
+#include <lib/xlat_tables/xlat_mmu_helpers.h>
+#include <lib/xlat_tables/xlat_tables_v2.h>
+
+#include "msm8916_gicv2.h"
+#include <msm8916_mmap.h>
+#include "msm8916_setup.h"
+#include <uartdm_console.h>
+
+static const mmap_region_t msm8916_mmap[] = {
+ MAP_REGION_FLAT(PCNOC_BASE, PCNOC_SIZE,
+ MT_DEVICE | MT_RW | MT_SECURE | MT_EXECUTE_NEVER),
+ MAP_REGION_FLAT(APCS_BASE, APCS_SIZE,
+ MT_DEVICE | MT_RW | MT_SECURE | MT_EXECUTE_NEVER),
+ {},
+};
+
+static console_t console;
+
+unsigned int plat_get_syscnt_freq2(void)
+{
+ return PLAT_SYSCNT_FREQ;
+}
+
+#define GPIO_CFG_FUNC(n) ((n) << 2)
+#define GPIO_CFG_DRV_STRENGTH_MA(ma) (((ma) / 2 - 1) << 6)
+
+#define CLK_ENABLE BIT_32(0)
+#define CLK_OFF BIT_32(31)
+#define GCC_BLSP1_AHB_CBCR (GCC_BASE + 0x01008)
+#define GCC_BLSP1_UART_APPS_CBCR(n) (GCC_BASE + \
+ (((n) == 2) ? (0x0302c) : (0x0203c + (((n) - 1) * 0x1000))))
+#define GCC_APCS_CLOCK_BRANCH_ENA_VOTE (GCC_BASE + 0x45004)
+#define BLSP1_AHB_CLK_ENA BIT_32(10)
+
+struct uartdm_gpios {
+ unsigned int tx, rx, func;
+};
+
+static const struct uartdm_gpios uartdm_gpio_map[] = {
+#if defined(PLAT_msm8909)
+ {4, 5, 0x2}, {20, 21, 0x3},
+#elif defined(PLAT_msm8916) || defined(PLAT_msm8939)
+ {0, 1, 0x2}, {4, 5, 0x2},
+#elif defined(PLAT_mdm9607)
+ {12, 13, 0x2}, {4, 5, 0x2}, {0, 1, 0x1},
+ {16, 17, 0x2}, {8, 9, 0x2}, {20, 21, 0x2},
+#endif
+};
+
+/*
+ * The previous boot stage seems to disable most of the UART setup before exit
+ * so it must be enabled here again before the UART console can be used.
+ */
+static void msm8916_enable_blsp_uart(void)
+{
+ const struct uartdm_gpios *gpios = &uartdm_gpio_map[QTI_UART_NUM - 1];
+
+ CASSERT(QTI_UART_NUM > 0 && QTI_UART_NUM <= ARRAY_SIZE(uartdm_gpio_map),
+ assert_qti_blsp_uart_valid);
+
+ /* Route GPIOs to BLSP UART */
+ mmio_write_32(TLMM_GPIO_CFG(gpios->tx), GPIO_CFG_FUNC(gpios->func) |
+ GPIO_CFG_DRV_STRENGTH_MA(8));
+ mmio_write_32(TLMM_GPIO_CFG(gpios->rx), GPIO_CFG_FUNC(gpios->func) |
+ GPIO_CFG_DRV_STRENGTH_MA(8));
+
+ /* Enable AHB clock */
+ mmio_setbits_32(GCC_APCS_CLOCK_BRANCH_ENA_VOTE, BLSP1_AHB_CLK_ENA);
+ while (mmio_read_32(GCC_BLSP1_AHB_CBCR) & CLK_OFF) {
+ }
+
+ /* Enable BLSP UART clock */
+ mmio_setbits_32(GCC_BLSP1_UART_APPS_CBCR(QTI_UART_NUM), CLK_ENABLE);
+ while (mmio_read_32(GCC_BLSP1_UART_APPS_CBCR(QTI_UART_NUM)) & CLK_OFF) {
+ }
+}
+
+void msm8916_early_platform_setup(void)
+{
+ /* Initialize the debug console as early as possible */
+ msm8916_enable_blsp_uart();
+ console_uartdm_register(&console, BLSP_UART_BASE);
+
+ if (QTI_RUNTIME_UART) {
+ /* Mark UART as runtime usable */
+ console_set_scope(&console, CONSOLE_FLAG_BOOT |
+ CONSOLE_FLAG_RUNTIME | CONSOLE_FLAG_CRASH);
+ }
+}
+
+void msm8916_plat_arch_setup(uintptr_t base, size_t size)
+{
+ mmap_add_region(base, base, size, MT_RW_DATA | MT_SECURE);
+ mmap_add_region(BL_CODE_BASE, BL_CODE_BASE,
+ BL_CODE_END - BL_CODE_BASE,
+ MT_CODE | MT_SECURE);
+ mmap_add_region(BL_RO_DATA_BASE, BL_RO_DATA_BASE,
+ BL_RO_DATA_END - BL_RO_DATA_BASE,
+ MT_RO_DATA | MT_SECURE);
+ mmap_add_region(BL_COHERENT_RAM_BASE, BL_COHERENT_RAM_BASE,
+ BL_COHERENT_RAM_END - BL_COHERENT_RAM_BASE,
+ MT_DEVICE | MT_RW | MT_SECURE | MT_EXECUTE_NEVER);
+
+ mmap_add(msm8916_mmap);
+ init_xlat_tables();
+}
+
+void msm8916_platform_setup(void)
+{
+ generic_delay_timer_init();
+ msm8916_gicv2_init();
+}
diff --git a/plat/qti/msm8916/msm8916_setup.h b/plat/qti/msm8916/msm8916_setup.h
new file mode 100644
index 0000000..d2de943
--- /dev/null
+++ b/plat/qti/msm8916/msm8916_setup.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2022-2023, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MSM8916_SETUP_H
+#define MSM8916_SETUP_H
+
+void msm8916_early_platform_setup(void);
+void msm8916_plat_arch_setup(uintptr_t base, size_t size);
+void msm8916_platform_setup(void);
+
+#endif /* MSM8916_SETUP_H */
diff --git a/plat/qti/msm8916/msm8916_topology.c b/plat/qti/msm8916/msm8916_topology.c
new file mode 100644
index 0000000..d8cdc0e
--- /dev/null
+++ b/plat/qti/msm8916/msm8916_topology.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2022, Stephan Gerhold <stephan@gerhold.net>
+ * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <plat/common/platform.h>
+
+#include <platform_def.h>
+
+static const unsigned char plat_power_domain_tree_desc[] = {
+ PLATFORM_SYSTEM_COUNT,
+ PLATFORM_CLUSTER_COUNT,
+ PLATFORM_CPUS_PER_CLUSTER,
+#if PLATFORM_CLUSTER_COUNT > 1
+ PLATFORM_CPUS_PER_CLUSTER,
+#endif
+};
+
+int plat_core_pos_by_mpidr(u_register_t mpidr)
+{
+ unsigned int cluster = MPIDR_AFFLVL1_VAL(mpidr);
+ unsigned int core = MPIDR_AFFLVL0_VAL(mpidr);
+
+ if (MPIDR_AFFLVL3_VAL(mpidr) > 0 ||
+ MPIDR_AFFLVL2_VAL(mpidr) > 0 ||
+ cluster >= PLATFORM_CLUSTER_COUNT ||
+ core >= PLATFORM_CPUS_PER_CLUSTER) {
+ return -1;
+ }
+ return core | (cluster << PLATFORM_CPU_PER_CLUSTER_SHIFT);
+}
+
+const unsigned char *plat_get_power_domain_tree_desc(void)
+{
+ return plat_power_domain_tree_desc;
+}
diff --git a/plat/qti/msm8916/platform.mk b/plat/qti/msm8916/platform.mk
new file mode 100644
index 0000000..c71ad94
--- /dev/null
+++ b/plat/qti/msm8916/platform.mk
@@ -0,0 +1,98 @@
+#
+# Copyright (c) 2021-2023, Stephan Gerhold <stephan@gerhold.net>
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+include drivers/arm/gic/v2/gicv2.mk
+include lib/xlat_tables_v2/xlat_tables.mk
+
+PLAT_BL_COMMON_SOURCES := ${GICV2_SOURCES} \
+ ${XLAT_TABLES_LIB_SRCS} \
+ drivers/delay_timer/delay_timer.c \
+ drivers/delay_timer/generic_delay_timer.c \
+ plat/common/plat_gicv2.c \
+ plat/qti/msm8916/msm8916_gicv2.c \
+ plat/qti/msm8916/msm8916_setup.c \
+ plat/qti/msm8916/${ARCH}/msm8916_helpers.S \
+ plat/qti/msm8916/${ARCH}/uartdm_console.S
+
+MSM8916_CPU := $(if ${ARM_CORTEX_A7},cortex_a7,cortex_a53)
+MSM8916_PM_SOURCES := drivers/arm/cci/cci.c \
+ lib/cpus/${ARCH}/${MSM8916_CPU}.S \
+ plat/common/plat_psci_common.c \
+ plat/qti/msm8916/msm8916_config.c \
+ plat/qti/msm8916/msm8916_cpu_boot.c \
+ plat/qti/msm8916/msm8916_pm.c \
+ plat/qti/msm8916/msm8916_topology.c
+
+BL31_SOURCES += ${MSM8916_PM_SOURCES} \
+ plat/qti/msm8916/msm8916_bl31_setup.c
+
+PLAT_INCLUDES := -Iplat/qti/msm8916/include
+
+ifeq (${ARCH},aarch64)
+# arm_macros.S exists only on aarch64 currently
+PLAT_INCLUDES += -Iinclude/plat/arm/common/${ARCH}
+endif
+
+# Only BL31 is supported at the moment and is entered on a single CPU
+RESET_TO_BL31 := 1
+COLD_BOOT_SINGLE_CPU := 1
+
+# Have different sections for code and rodata
+SEPARATE_CODE_AND_RODATA := 1
+
+# Single cluster
+WARMBOOT_ENABLE_DCACHE_EARLY := 1
+
+# Disable features unsupported in ARMv8.0
+ENABLE_SPE_FOR_NS := 0
+ENABLE_SVE_FOR_NS := 0
+
+# Disable workarounds unnecessary for Cortex-A7/A53
+WORKAROUND_CVE_2017_5715 := 0
+WORKAROUND_CVE_2022_23960 := 0
+
+ifeq (${MSM8916_CPU},cortex_a53)
+# The Cortex-A53 revision varies depending on the SoC revision.
+# msm8916 uses r0p0, msm8939 uses r0p1 or r0p4. Enable all errata
+# and rely on the runtime detection to apply them only if needed.
+ERRATA_A53_819472 := 1
+ERRATA_A53_824069 := 1
+ERRATA_A53_826319 := 1
+ERRATA_A53_827319 := 1
+ERRATA_A53_835769 := 1
+ERRATA_A53_836870 := 1
+ERRATA_A53_843419 := 1
+ERRATA_A53_855873 := 1
+ERRATA_A53_1530924 := 1
+endif
+
+# Build config flags
+# ------------------
+BL31_BASE ?= 0x86500000
+PRELOADED_BL33_BASE ?= 0x8f600000
+
+ifeq (${ARCH},aarch64)
+ BL32_BASE ?= BL31_LIMIT
+ $(eval $(call add_define,BL31_BASE))
+else
+ ifeq (${AARCH32_SP},none)
+ $(error Variable AARCH32_SP has to be set for AArch32)
+ endif
+ # There is no BL31 on aarch32, so reuse its location for BL32
+ BL32_BASE ?= $(BL31_BASE)
+endif
+$(eval $(call add_define,BL32_BASE))
+
+# UART number to use for TF-A output during early boot
+QTI_UART_NUM ?= 2
+$(eval $(call assert_numeric,QTI_UART_NUM))
+$(eval $(call add_define,QTI_UART_NUM))
+
+# Set to 1 on the command line to keep using UART after early boot.
+# Requires reserving the UART and related clocks inside the normal world.
+QTI_RUNTIME_UART ?= 0
+$(eval $(call assert_boolean,QTI_RUNTIME_UART))
+$(eval $(call add_define,QTI_RUNTIME_UART))
diff --git a/plat/qti/msm8916/sp_min/msm8916_sp_min_setup.c b/plat/qti/msm8916/sp_min/msm8916_sp_min_setup.c
new file mode 100644
index 0000000..3c93305
--- /dev/null
+++ b/plat/qti/msm8916/sp_min/msm8916_sp_min_setup.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2022-2023, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <common/debug.h>
+#include <lib/xlat_tables/xlat_mmu_helpers.h>
+#include <platform_sp_min.h>
+
+#include "../msm8916_config.h"
+#include "../msm8916_setup.h"
+
+static struct {
+ entry_point_info_t bl33;
+} image_ep_info = {
+ /* BL33 entry point */
+ SET_STATIC_PARAM_HEAD(bl33, PARAM_EP, VERSION_1,
+ entry_point_info_t, NON_SECURE),
+ .bl33.pc = PRELOADED_BL33_BASE,
+ .bl33.spsr = SPSR_MODE32(MODE32_hyp, SPSR_T_ARM, SPSR_E_LITTLE,
+ DISABLE_ALL_EXCEPTIONS),
+};
+
+void sp_min_early_platform_setup2(u_register_t arg0, u_register_t arg1,
+ u_register_t arg2, u_register_t arg3)
+{
+ msm8916_early_platform_setup();
+ msm8916_configure_early();
+}
+
+void sp_min_plat_arch_setup(void)
+{
+ msm8916_plat_arch_setup(BL32_BASE, BL32_END - BL32_BASE);
+ enable_mmu_svc_mon(0);
+}
+
+void sp_min_platform_setup(void)
+{
+ INFO("SP_MIN: Platform setup start\n");
+ msm8916_platform_setup();
+ msm8916_configure();
+ INFO("SP_MIN: Platform setup done\n");
+}
+
+entry_point_info_t *sp_min_plat_get_bl33_ep_info(void)
+{
+ return &image_ep_info.bl33;
+}
diff --git a/plat/qti/msm8916/sp_min/sp_min-msm8916.mk b/plat/qti/msm8916/sp_min/sp_min-msm8916.mk
new file mode 100644
index 0000000..be07f94
--- /dev/null
+++ b/plat/qti/msm8916/sp_min/sp_min-msm8916.mk
@@ -0,0 +1,11 @@
+#
+# Copyright (c) 2022-2023, Stephan Gerhold <stephan@gerhold.net>
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BL32_SOURCES += ${MSM8916_PM_SOURCES} \
+ plat/common/${ARCH}/platform_mp_stack.S \
+ plat/qti/msm8916/sp_min/msm8916_sp_min_setup.c
+
+override RESET_TO_SP_MIN := 1
diff --git a/plat/qti/msm8916/tsp/msm8916_tsp_setup.c b/plat/qti/msm8916/tsp/msm8916_tsp_setup.c
new file mode 100644
index 0000000..218af57
--- /dev/null
+++ b/plat/qti/msm8916/tsp/msm8916_tsp_setup.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023, Stephan Gerhold <stephan@gerhold.net>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <common/debug.h>
+#include <platform_tsp.h>
+
+#include "../msm8916_setup.h"
+#include <platform_def.h>
+
+void tsp_early_platform_setup(void)
+{
+ msm8916_early_platform_setup();
+}
+
+void tsp_plat_arch_setup(void)
+{
+ msm8916_plat_arch_setup(BL32_BASE, BL32_END - BL32_BASE);
+ enable_mmu_el1(0);
+}
+
+void tsp_platform_setup(void)
+{
+ INFO("TSP: Platform setup start\n");
+ msm8916_platform_setup();
+ INFO("TSP: Platform setup done\n");
+
+ console_switch_state(CONSOLE_FLAG_RUNTIME);
+}
diff --git a/plat/qti/msm8916/tsp/tsp-msm8916.mk b/plat/qti/msm8916/tsp/tsp-msm8916.mk
new file mode 100644
index 0000000..0d50058
--- /dev/null
+++ b/plat/qti/msm8916/tsp/tsp-msm8916.mk
@@ -0,0 +1,8 @@
+#
+# Copyright (c) 2023, Stephan Gerhold <stephan@gerhold.net>
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BL32_SOURCES += plat/common/${ARCH}/platform_mp_stack.S \
+ plat/qti/msm8916/tsp/msm8916_tsp_setup.c