Merge changes from topic "msm8916" into integration

* changes:
  feat(msm8916): allow booting secondary CPU cores
  feat(msm8916): setup hardware for non-secure world
  feat(gic): allow overriding GICD_PIDR2_GICV2 address
  feat(msm8916): initial platform port
  docs(msm8916): new port for Qualcomm Snapdragon 410
This commit is contained in:
Manish Pandey 2022-02-03 22:59:34 +01:00 committed by TrustedFirmware Code Review
commit e0a6a512b5
19 changed files with 1118 additions and 0 deletions

View File

@ -348,6 +348,9 @@ subsections:
deprecated:
- plat/qti/sc7280
- title: MSM8916
scope: msm8916
- title: Raspberry Pi
scope: rpi

View File

@ -580,6 +580,15 @@ QTI platform port
:|F|: docs/plat/qti.rst
:|F|: plat/qti/
QTI MSM8916 platform port
^^^^^^^^^^^^^^^^^^^^^^^^^
:|M|: Stephan Gerhold <stephan@gerhold.net>
:|G|: `stephan-gh`_
:|M|: Nikita Travkin <nikita@trvn.ru>
:|G|: `TravMurav`_
:|F|: docs/plat/qti-msm8916.rst
:|F|: plat/qti/msm8916/
Raspberry Pi 3 platform port
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
:|M|: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
@ -797,9 +806,11 @@ Conventional Changelog Extensions
.. _smaeul: https://github.com/smaeul
.. _soby-mathew: https://github.com/soby-mathew
.. _sreekare: https://github.com/sreekare
.. _stephan-gh: https://github.com/stephan-gh
.. _thloh85-intel: https://github.com/thloh85-intel
.. _thomas-arm: https://github.com/thomas-arm
.. _TonyXie06: https://github.com/TonyXie06
.. _TravMurav: https://github.com/TravMurav
.. _vwadekar: https://github.com/vwadekar
.. _venkatesh: https://github.com/vabbarap
.. _Yann-lms: https://github.com/Yann-lms

View File

@ -32,6 +32,7 @@ Platform Ports
qemu
qemu-sbsa
qti
qti-msm8916
rpi3
rpi4
rcar-gen3

116
docs/plat/qti-msm8916.rst Normal file
View File

@ -0,0 +1,116 @@
Qualcomm Snapdragon 410 (MSM8916/APQ8016)
=========================================
The `Qualcomm Snapdragon 410`_ is Qualcomm's first 64-bit SoC, released in 2014
with four ARM Cortex-A53 cores. There are differents variants (MSM8916,
APQ8016(E), ...) that are all very similar. A popular device based on APQ8016E
is the `DragonBoard 410c`_ single-board computer, but the SoC is also used in
various mid-range smartphones/tablets.
The TF-A/BL31 port for MSM8916 provides a minimal, community-maintained
EL3 firmware. It is primarily based on information from the public
`Snapdragon 410E Technical Reference Manual`_ combined with a lot of
trial and error to actually make it work.
.. note::
Unlike the :doc:`QTI SC7180/SC7280 <qti>` ports, this port does **not**
make use of a proprietary binary components (QTISECLIB). It is fully
open-source but therefore limited to publicly documented hardware
components.
Functionality
-------------
The BL31 port is much more minimal compared to the original firmware and
therefore expects the non-secure world (e.g. Linux) to manage more hardware,
such as the SMMUs and all remote processors (RPM, WCNSS, Venus, Modem).
Everything except modem is currently functional with a slightly modified version
of mainline Linux.
.. warning::
This port is **not secure**. There is no special secure memory and the
used DRAM is available from both the non-secure and secure worlds.
Unfortunately, the hardware used for memory protection is not described
in the APQ8016E documentation.
The port is primarily intended as a minimal PSCI implementation (without a
separate secure world) where this limitation is not a big problem. Booting
secondary CPU cores (PSCI ``CPU_ON``) is supported. Basic CPU core power
management (``CPU_SUSPEND``) is functional but still work-in-progress and
will be added later once ready.
Boot Flow
---------
BL31 replaces the original ``tz`` firmware in the boot flow::
Boot ROM (PBL) -> SBL -> BL31 (EL3) -> U-Boot (EL2) -> Linux (EL2)
By default, BL31 enters the non-secure world in EL2 AArch64 state at address
``0x8f600000``. The original hypervisor firmware (``hyp``) is not used, you can
use KVM or another hypervisor. The entry address is fixed in the BL31 binary
but can be changed using the ``PRELOADED_BL33_BASE`` make file parameter.
Using an AArch64 bootloader (such as `U-Boot for DragonBoard 410c`_) is
recommended. AArch32 bootloaders (such as the original Little Kernel bootloader
from Qualcomm) are not directly supported, although it is possible to use an EL2
shim loader to temporarily switch to AArch32 state.
Installation
------------
First, setup the cross compiler for AArch64 and build TF-A for ``msm8916``::
$ make CROSS_COMPILE=aarch64-linux-gnu- PLAT=msm8916
The BL31 ELF image is generated in ``build/msm8916/release/bl31/bl31.elf``.
This image must be "signed" before flashing it, even if the board has secure
boot disabled. In this case the signature does not provide any security,
but it provides the firmware with required metadata.
The `DragonBoard 410c`_ does not have secure boot enabled by default. In this
case you can simply sign the ELF image using a randomly generated key. You can
use e.g. `qtestsign`_::
$ ./qtestsign.py tz build/msm8916/release/bl31/bl31.elf
Then install the resulting ``build/msm8916/release/bl31/bl31-test-signed.mbn``
to the ``tz`` partition on the device. BL31 should be running after a reboot.
.. warning::
Do not flash incorrectly signed firmware on devices that have secure
boot enabled! Make sure that you have a way to recover the board in case
of problems (e.g. using EDL).
Boot Trace
----------
BL31 prints some lines on the debug console UART2, which will usually look like
this (with ``DEBUG=1``, otherwise only the ``NOTICE`` lines are shown)::
...
S - DDR Frequency, 400 MHz
NOTICE: BL31: v2.6(debug):v2.6
NOTICE: BL31: Built : 20:00:00, Dec 01 2021
INFO: BL31: Platform setup start
INFO: ARM GICv2 driver initialized
INFO: BL31: Platform setup done
INFO: BL31: Initializing runtime services
INFO: BL31: cortex_a53: CPU workaround for 819472 was applied
INFO: BL31: cortex_a53: CPU workaround for 824069 was applied
INFO: BL31: cortex_a53: CPU workaround for 826319 was applied
INFO: BL31: cortex_a53: CPU workaround for 827319 was applied
INFO: BL31: cortex_a53: CPU workaround for 835769 was applied
INFO: BL31: cortex_a53: CPU workaround for disable_non_temporal_hint was applied
INFO: BL31: cortex_a53: CPU workaround for 843419 was applied
INFO: BL31: cortex_a53: CPU workaround for 1530924 was applied
INFO: BL31: Preparing for EL3 exit to normal world
INFO: Entry point address = 0x8f600000
INFO: SPSR = 0x3c9
U-Boot 2021.10 (Dec 01 2021 - 20:00:00 +0000)
Qualcomm-DragonBoard 410C
...
.. _Qualcomm Snapdragon 410: https://www.qualcomm.com/products/snapdragon-processors-410
.. _DragonBoard 410c: https://www.96boards.org/product/dragonboard410c/
.. _Snapdragon 410E Technical Reference Manual: https://developer.qualcomm.com/download/sd410/snapdragon-410e-technical-reference-manual.pdf
.. _U-Boot for DragonBoard 410c: https://u-boot.readthedocs.io/en/latest/board/qualcomm/dragonboard410c.html
.. _qtestsign: https://github.com/msm8916-mainline/qtestsign

View File

@ -8,6 +8,7 @@
#define GICV2_H
#include <drivers/arm/gic_common.h>
#include <platform_def.h>
/*******************************************************************************
* GICv2 miscellaneous definitions
@ -30,7 +31,14 @@
#define GICD_SGIR U(0xF00)
#define GICD_CPENDSGIR U(0xF10)
#define GICD_SPENDSGIR U(0xF20)
/*
* Some GICv2 implementations violate the specification and have this register
* at a different address. Allow overriding it in platform_def.h as workaround.
*/
#ifndef GICD_PIDR2_GICV2
#define GICD_PIDR2_GICV2 U(0xFE8)
#endif
#define ITARGETSR_SHIFT 2
#define GIC_TARGET_CPU_MASK U(0xff)

View File

@ -0,0 +1,164 @@
/*
* Copyright (c) 2021, Stephan Gerhold <stephan@gerhold.net>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <msm8916_mmap.h>
#define APCS_TCM_START_ADDR 0x10
#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 x1, #BLSP_UART2_BASE
/*
* If the non-secure world has been actively using the UART there might
* be still some characters left to be sent in the FIFO. In that case,
* resetting the transmitter too early might cause all output to become
* corrupted. To avoid that, try to flush (wait until FIFO empty) first.
*/
mov x4, lr
bl console_uartdm_core_flush
mov lr, x4
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 x1, #BLSP_UART2_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 x1, #BLSP_UART2_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
/* There is just a single cluster so this is very simple */
mrs x0, mpidr_el1
and x0, x0, #MPIDR_CPU_MASK
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
ret
endfunc plat_get_my_entrypoint
/* -------------------------------------------------
* void plat_reset_handler(void)
* Perform additional initialization after reset.
* Clobber list : x0 - x18, x30
* -------------------------------------------------
*/
func plat_reset_handler
/*
* Check if the CPU is running at the correct address.
* During cold boot the CPU enters here at the wrong address
* using the "boot remapper". (It remaps the BL31_BASE to
* the CPU reset address 0x0).
*/
mov x0, #BL31_BASE
adr x1, bl31_entrypoint
cmp x0, x1
b.ne _remapped_cold_boot
/* Already running at correct address, just return directly */
ret
_remapped_cold_boot:
/*
* The previous boot stage seems to use the L2 cache as TCM.
* Disable the TCM redirect before enabling caches to avoid
* strange crashes.
*/
mov x2, #APCS_CFG
ldr w3, [x2, #APCS_TCM_START_ADDR]
and w3, w3, #~APCS_TCM_REDIRECT_EN_0
str w3, [x2, #APCS_TCM_START_ADDR]
/* Enter BL31 again at the real address */
br x0
endfunc plat_reset_handler
/* -------------------------------------------------
* 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

View File

@ -0,0 +1,154 @@
/*
* Copyright (c) 2021, 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
/* 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
1: /* Loop until TX FIFO has space */
ldr w2, [x1, #UART_DM_SR]
tbz w2, #UART_DM_SR_TXRDY_BIT, 1b
/* 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

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2021, 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 0x8000000 /* 128 MiB */
#define APCS_BASE 0x0b000000
#define APCS_SIZE 0x800000 /* 8 MiB */
#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 BLSP_UART1_BASE (PCNOC_BASE + 0x78af000)
#define BLSP_UART2_BASE (PCNOC_BASE + 0x78b0000)
#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_CFG (APCS_BASE + 0x10000)
#define APCS_GLB (APCS_BASE + 0x11000)
#define APCS_L2_SAW2 (APCS_BASE + 0x12000)
#define APCS_QTMR (APCS_BASE + 0x20000)
#define APCS_ALIAS_ACS(cpu) (APCS_BASE + 0x88000 + ((cpu) * 0x10000))
#define APCS_ALIAS_SAW2(cpu) (APCS_BASE + 0x89000 + ((cpu) * 0x10000))
#endif /* MSM8916_MMAP_H */

View File

@ -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 */

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2021, 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>
/*
* 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 + 0x20000) /* 128 KiB */
#define BL31_PROGBITS_LIMIT (BL31_BASE + 0x10000) /* 64 KiB */
#define CACHE_WRITEBACK_GRANULE U(64)
#define PLATFORM_STACK_SIZE U(0x1000)
/* CPU topology: single cluster with 4 cores */
#define PLATFORM_CLUSTER_COUNT U(1)
#define PLATFORM_MAX_CPUS_PER_CLUSTER U(4)
#define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER_COUNT * \
PLATFORM_MAX_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 frequency */
#define PLAT_SYSCNT_FREQ 19200000
/*
* 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)
#endif /* PLATFORM_DEF_H */

View File

@ -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 */

View File

@ -0,0 +1,219 @@
/*
* Copyright (c) 2021, Stephan Gerhold <stephan@gerhold.net>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <arch.h>
#include <common/debug.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 <plat/common/platform.h>
#include "msm8916_gicv2.h"
#include <msm8916_mmap.h>
#include <platform_def.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 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),
};
static console_t console;
unsigned int plat_get_syscnt_freq2(void)
{
return PLAT_SYSCNT_FREQ;
}
#define CLK_ENABLE BIT_32(0)
#define CLK_OFF BIT_32(31)
#define GPIO_BLSP_UART2_TX 4
#define GPIO_BLSP_UART2_RX 5
#define GPIO_CFG_FUNC_BLSP_UART2 (U(0x2) << 2)
#define GPIO_CFG_DRV_STRENGTH_16MA (U(0x7) << 6)
#define GCC_BLSP1_AHB_CBCR (GCC_BASE + 0x01008)
#define GCC_BLSP1_UART2_APPS_CBCR (GCC_BASE + 0x0302c)
#define GCC_APCS_CLOCK_BRANCH_ENA_VOTE (GCC_BASE + 0x45004)
#define BLSP1_AHB_CLK_ENA BIT_32(10)
/*
* 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_uart2(void)
{
/* Route GPIOs to BLSP UART2 */
mmio_write_32(TLMM_GPIO_CFG(GPIO_BLSP_UART2_TX),
GPIO_CFG_FUNC_BLSP_UART2 | GPIO_CFG_DRV_STRENGTH_16MA);
mmio_write_32(TLMM_GPIO_CFG(GPIO_BLSP_UART2_RX),
GPIO_CFG_FUNC_BLSP_UART2 | GPIO_CFG_DRV_STRENGTH_16MA);
/* 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 UART2 clock */
mmio_setbits_32(GCC_BLSP1_UART2_APPS_CBCR, CLK_ENABLE);
while (mmio_read_32(GCC_BLSP1_UART2_APPS_CBCR) & CLK_OFF)
;
}
void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
u_register_t arg2, u_register_t arg3)
{
/* Initialize the debug console as early as possible */
msm8916_enable_blsp_uart2();
console_uartdm_register(&console, BLSP_UART2_BASE);
}
void bl31_plat_arch_setup(void)
{
mmap_add_region(BL31_BASE, BL31_BASE, BL31_END - BL31_BASE,
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();
enable_mmu_el3(0);
}
static void msm8916_configure_timer(void)
{
/* Set timer frequency */
mmio_write_32(APCS_QTMR + CNTCTLBASE_CNTFRQ, plat_get_syscnt_freq2());
/* Make frame 0 available to non-secure world */
mmio_write_32(APCS_QTMR + CNTNSAR, BIT_32(CNTNSAR_NS_SHIFT(0)));
mmio_write_32(APCS_QTMR + CNTACR_BASE(0),
BIT_32(CNTACR_RPCT_SHIFT) | BIT_32(CNTACR_RVCT_SHIFT) |
BIT_32(CNTACR_RFRQ_SHIFT) | BIT_32(CNTACR_RVOFF_SHIFT) |
BIT_32(CNTACR_RWVT_SHIFT) | BIT_32(CNTACR_RWPT_SHIFT));
}
/*
* 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)
#define APCS_BOOT_START_ADDR_SEC (APCS_CFG + 0x04)
#define REMAP_EN BIT_32(0)
#define APCS_AA64NAA32_REG (APCS_CFG + 0x0c)
static void msm8916_configure_cpu_pm(void)
{
unsigned int cpu;
/* Disallow non-secure access to boot remapper / TCM registers */
mmio_write_32(APCS_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, APCS_GLB_SECURE_STS_NS | APCS_GLB_SECURE_PWR_NS);
/* Disallow non-secure access to L2 SAW2 */
mmio_write_32(APCS_L2_SAW2, 0);
/* Disallow non-secure access to CPU ACS and SAW2 */
for (cpu = 0; cpu < PLATFORM_CORE_COUNT; cpu++) {
mmio_write_32(APCS_ALIAS_ACS(cpu), 0);
mmio_write_32(APCS_ALIAS_SAW2(cpu), 0);
}
/* 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(APCS_BOOT_START_ADDR_SEC, BL31_BASE | REMAP_EN);
mmio_write_32(APCS_AA64NAA32_REG, 1);
}
/*
* 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 GCC_SMMU_CFG_CBCR (GCC_BASE + 0x12038)
#define GCC_APCS_SMMU_CLOCK_BRANCH_ENA_VOTE (GCC_BASE + 0x4500c)
#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)
static void msm8916_configure_smmu(void)
{
/* Enable SMMU configuration clock to enable register access */
mmio_setbits_32(GCC_APCS_SMMU_CLOCK_BRANCH_ENA_VOTE, SMMU_CFG_CLK_ENA);
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);
/* Disable configuration clock again */
mmio_clrbits_32(GCC_APCS_SMMU_CLOCK_BRANCH_ENA_VOTE, SMMU_CFG_CLK_ENA);
}
void bl31_platform_setup(void)
{
INFO("BL31: Platform setup start\n");
generic_delay_timer_init();
msm8916_configure_timer();
msm8916_gicv2_init();
msm8916_configure_cpu_pm();
msm8916_configure_smmu();
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;
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2021, Stephan Gerhold <stephan@gerhold.net>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <msm8916_mmap.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)
/* Boot a secondary CPU core for the first time. */
void msm8916_cpu_boot(unsigned int core)
{
uintptr_t acs = APCS_ALIAS_ACS(core);
uint32_t pwr_ctl;
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();
}

View File

@ -0,0 +1,59 @@
/*
* 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>
#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
#define IRQ_SEC_PHY_TIMER (16 + 2) /* PPI #2 */
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);
gicv2_distif_init();
gicv2_pcpu_distif_init();
gicv2_cpuif_enable();
}

View File

@ -0,0 +1,12 @@
/*
* 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);
#endif /* MSM8916_GICV2_H */

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2021, Stephan Gerhold <stephan@gerhold.net>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <common/debug.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"
static int msm8916_pwr_domain_on(u_register_t mpidr)
{
unsigned int core = MPIDR_AFFLVL0_VAL(mpidr);
VERBOSE("PSCI: Booting CPU %d\n", core);
msm8916_cpu_boot(core);
return PSCI_E_SUCCESS;
}
static void msm8916_pwr_domain_on_finish(const psci_power_state_t *target_state)
{
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)
{
msm8916_entry_point = sec_entrypoint;
*psci_ops = &msm8916_psci_ops;
return 0;
}

View File

@ -0,0 +1,12 @@
/*
* Copyright (c) 2021, Stephan Gerhold <stephan@gerhold.net>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef MSM8916_PM_H
#define MSM8916_PM_H
void msm8916_cpu_boot(unsigned int core);
#endif /* MSM8916_PM_H */

View File

@ -0,0 +1,35 @@
/*
* 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[PLAT_MAX_PWR_LVL + 1] = {
PLATFORM_SYSTEM_COUNT,
PLATFORM_CLUSTER_COUNT,
PLATFORM_MAX_CPUS_PER_CLUSTER,
};
int plat_core_pos_by_mpidr(u_register_t mpidr)
{
unsigned int core = MPIDR_AFFLVL0_VAL(mpidr);
if (MPIDR_AFFLVL3_VAL(mpidr) > 0 ||
MPIDR_AFFLVL2_VAL(mpidr) > 0 ||
MPIDR_AFFLVL1_VAL(mpidr) > 0 ||
core >= PLATFORM_MAX_CPUS_PER_CLUSTER) {
return -1;
}
return core;
}
const unsigned char *plat_get_power_domain_tree_desc(void)
{
return plat_power_domain_tree_desc;
}

View File

@ -0,0 +1,62 @@
#
# Copyright (c) 2021, 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 := ${XLAT_TABLES_LIB_SRCS}
PLAT_INCLUDES := -Iinclude/plat/arm/common/${ARCH} \
-Iplat/qti/msm8916/include
BL31_SOURCES += ${GICV2_SOURCES} \
drivers/delay_timer/delay_timer.c \
drivers/delay_timer/generic_delay_timer.c \
lib/cpus/${ARCH}/cortex_a53.S \
plat/common/plat_gicv2.c \
plat/common/plat_psci_common.c \
plat/qti/msm8916/msm8916_bl31_setup.c \
plat/qti/msm8916/msm8916_cpu_boot.c \
plat/qti/msm8916/msm8916_gicv2.c \
plat/qti/msm8916/msm8916_pm.c \
plat/qti/msm8916/msm8916_topology.c \
plat/qti/msm8916/${ARCH}/msm8916_helpers.S \
plat/qti/msm8916/${ARCH}/uartdm_console.S
# Only BL31 is supported at the moment and is entered on a single CPU
RESET_TO_BL31 := 1
COLD_BOOT_SINGLE_CPU := 1
# Build config flags
# ------------------
BL31_BASE ?= 0x86500000
BL32_BASE ?= 0x86000000
PRELOADED_BL33_BASE ?= 0x8f600000
# 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_AMU := 0
ENABLE_SPE_FOR_LOWER_ELS := 0
ENABLE_SVE_FOR_NS := 0
# MSM8916 uses ARM Cortex-A53 r0p0 so likely all the errata apply
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 := 0 # Workaround works only for >= r0p3
ERRATA_A53_1530924 := 1
$(eval $(call add_define,BL31_BASE))
$(eval $(call add_define,BL32_BASE))