diff options
Diffstat (limited to '')
19 files changed, 1644 insertions, 0 deletions
diff --git a/plat/allwinner/common/allwinner-common.mk b/plat/allwinner/common/allwinner-common.mk new file mode 100644 index 0000000..3164a25 --- /dev/null +++ b/plat/allwinner/common/allwinner-common.mk @@ -0,0 +1,114 @@ +# +# Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +include lib/xlat_tables_v2/xlat_tables.mk +include lib/libfdt/libfdt.mk +include drivers/arm/gic/v2/gicv2.mk + +AW_PLAT := plat/allwinner + +PLAT_INCLUDES := -Iinclude/plat/arm/common/aarch64 \ + -I${AW_PLAT}/common/include \ + -I${AW_PLAT}/${PLAT}/include + +PLAT_BL_COMMON_SOURCES := drivers/ti/uart/${ARCH}/16550_console.S \ + ${XLAT_TABLES_LIB_SRCS} \ + ${AW_PLAT}/common/plat_helpers.S \ + ${AW_PLAT}/common/sunxi_common.c + +BL31_SOURCES += drivers/allwinner/axp/common.c \ + ${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 \ + ${AW_PLAT}/common/sunxi_bl31_setup.c \ + ${AW_PLAT}/${PLAT}/sunxi_idle_states.c \ + ${AW_PLAT}/common/sunxi_pm.c \ + ${AW_PLAT}/${PLAT}/sunxi_power.c \ + ${AW_PLAT}/common/sunxi_security.c \ + ${AW_PLAT}/common/sunxi_topology.c + +# By default, attempt to use SCPI to the ARISC management processor. If SCPI +# is not enabled or SCP firmware is not loaded, fall back to a simpler native +# implementation that does not support CPU or system suspend. +# +# If SCP firmware will always be present (or absent), the unused implementation +# can be compiled out. +SUNXI_PSCI_USE_NATIVE ?= 1 +SUNXI_PSCI_USE_SCPI ?= 1 + +$(eval $(call assert_boolean,SUNXI_PSCI_USE_NATIVE)) +$(eval $(call assert_boolean,SUNXI_PSCI_USE_SCPI)) +$(eval $(call add_define,SUNXI_PSCI_USE_NATIVE)) +$(eval $(call add_define,SUNXI_PSCI_USE_SCPI)) + +ifeq (${SUNXI_PSCI_USE_NATIVE}${SUNXI_PSCI_USE_SCPI},00) +$(error "At least one of SCPI or native PSCI ops must be enabled") +endif + +ifeq (${SUNXI_PSCI_USE_NATIVE},1) +BL31_SOURCES += ${AW_PLAT}/common/sunxi_cpu_ops.c \ + ${AW_PLAT}/common/sunxi_native_pm.c +endif + +ifeq (${SUNXI_PSCI_USE_SCPI},1) +BL31_SOURCES += drivers/allwinner/sunxi_msgbox.c \ + drivers/arm/css/scpi/css_scpi.c \ + ${AW_PLAT}/common/sunxi_scpi_pm.c +endif + +SUNXI_SETUP_REGULATORS ?= 1 +$(eval $(call assert_boolean,SUNXI_SETUP_REGULATORS)) +$(eval $(call add_define,SUNXI_SETUP_REGULATORS)) + +SUNXI_BL31_IN_DRAM ?= 0 +$(eval $(call assert_boolean,SUNXI_BL31_IN_DRAM)) + +ifeq (${SUNXI_BL31_IN_DRAM},1) +SUNXI_AMEND_DTB := 1 +$(eval $(call add_define,SUNXI_BL31_IN_DRAM)) +endif + +SUNXI_AMEND_DTB ?= 0 +$(eval $(call assert_boolean,SUNXI_AMEND_DTB)) +$(eval $(call add_define,SUNXI_AMEND_DTB)) + +ifeq (${SUNXI_AMEND_DTB},1) +BL31_SOURCES += common/fdt_fixup.c \ + ${AW_PLAT}/common/sunxi_prepare_dtb.c +endif + +# The bootloader is guaranteed to only run on CPU 0 by the boot ROM. +COLD_BOOT_SINGLE_CPU := 1 + +# Do not enable SPE (not supported on ARM v8.0). +ENABLE_SPE_FOR_NS := 0 + +# Do not enable SVE (not supported on ARM v8.0). +ENABLE_SVE_FOR_NS := 0 + +# Enable workarounds for Cortex-A53 errata. Allwinner uses at least r0p4. +ERRATA_A53_835769 := 1 +ERRATA_A53_843419 := 1 +ERRATA_A53_855873 := 1 +ERRATA_A53_1530924 := 1 + +# The traditional U-Boot load address is 160MB into DRAM. +PRELOADED_BL33_BASE ?= 0x4a000000 + +# The reset vector can be changed for each CPU. +PROGRAMMABLE_RESET_ADDRESS := 1 + +# Allow mapping read-only data as execute-never. +SEPARATE_CODE_AND_RODATA := 1 + +# BL31 gets loaded alongside BL33 (U-Boot) by U-Boot's SPL +RESET_TO_BL31 := 1 + +# This platform is single-cluster and does not require coherency setup. +WARMBOOT_ENABLE_DCACHE_EARLY := 1 diff --git a/plat/allwinner/common/arisc_off.S b/plat/allwinner/common/arisc_off.S new file mode 100644 index 0000000..ed10832 --- /dev/null +++ b/plat/allwinner/common/arisc_off.S @@ -0,0 +1,115 @@ +# turn_off_core.S +# +# Copyright (c) 2018, Andre Przywara <osp@andrep.de> +# SPDX-License-Identifier: BSD-3-Clause +# +# OpenRISC assembly to turn off an ARM core on an Allwinner SoC from +# the arisc management controller. +# Generate a binary representation with: +# $ or1k-elf-as -c -o turn_off_core.o turn_off_core.S +# $ or1k-elf-objcopy -O binary --reverse-bytes=4 turn_off_core.o \ +# turn_off_core.bin +# The encoded instructions go into an array defined in +# plat/allwinner/sun50i_*/include/core_off_arisc.h, to be handed off to +# the arisc processor. +# +# This routine is meant to be called directly from arisc reset (put the +# start address in the reset vector), to be actually triggered by that +# very ARM core to be turned off. +# It expects the core number presented as a mask in the upper half of +# r3, so to be patched in the lower 16 bits of the first instruction, +# overwriting the 0 in this code here. +# The code will do the following: +# - Read the C_CPU_STATUS register, which contains the status of the WFI +# lines of each of the four A53 cores. +# - Loop until the core in question reaches WFI. +# - Using that mask, activate the core output clamps by setting the +# respective core bit in CPUX_PWROFF_GATING_REG (0x1f01500). +# Note that the clamp for core 0 covers more than just the core, activating +# it hangs the whole system. So we skip this step for core 0. +# - Using the negated mask, assert the core's reset line by clearing the +# respective bit in C_RST_CTRL (0x1f01c30). +# - Finally turn off the core's power switch by writing 0xff to the +# respective CPUx_PWR_SWITCH_REG (0x1f01540 ff.) +# - Assert the arisc's own reset to end execution. +# This also signals other arisc users that the chip is free again. +# So in C this would look like: +# while (!(readl(0x1700030) & (1U << core_nr))) +# ; +# if (core_nr != 0) +# writel(readl(0x1f01500) | (1U << core_nr), 0x1f01500); +# writel(readl(0x1f01c30) & ~(1U << core_nr), 0x1f01c30); +# writel(0xff, 0x1f01540 + (core_nr * 4)); +# (using A64/H5 addresses) + +.text +_start: + l.movhi r3, 0 # FIXUP! with core mask + l.movhi r0, 0 # clear r0 + l.movhi r13, 0x170 # r13: CPU_CFG_BASE=0x01700000 +wait_wfi: + l.lwz r5, 0x30(r13) # load C_CPU_STATUS + l.and r5, r5, r3 # mask requested core + l.sfeq r5, r0 # is it not yet in WFI? + l.bf wait_wfi # try again + + l.srli r6, r3, 16 # move mask to lower 16 bits + l.sfeqi r6, 1 # core 0 is special + l.bf 1f # don't touch the bit for core 0 + l.movhi r13, 0x1f0 # address of R_CPUCFG (delay) + l.lwz r5, 0x1500(r13) # core output clamps + l.or r5, r5, r6 # set bit to ... + l.sw 0x1500(r13), r5 # ... activate for our core + +1: l.lwz r5, 0x1c30(r13) # CPU power-on reset + l.xori r6, r6, -1 # negate core mask + l.and r5, r5, r6 # clear bit to ... + l.sw 0x1c30(r13), r5 # ... assert for our core + + l.ff1 r6, r3 # get core number from high mask + l.addi r6, r6, -17 # convert to 0-3 + l.slli r6, r6, 2 # r5: core number*4 (0-12) + l.add r6, r6, r13 # add to base address + l.ori r5, r0, 0xff # 0xff means all switches off + l.sw 0x1540(r6), r5 # core power switch registers + +reset: l.sw 0x1c00(r13),r0 # pull down our own reset line + + l.j reset # just in case .... + l.nop 0x0 # (delay slot) + +# same as above, but with the MMIO addresses matching the H6 SoC +_start_h6: + l.movhi r3, 0 # FIXUP! with core mask + l.movhi r0, 0 # clear r0 + l.movhi r13, 0x901 # r13: CPU_CFG_BASE=0x09010000 +1: + l.lwz r5, 0x80(r13) # load C_CPU_STATUS + l.and r5, r5, r3 # mask requested core + l.sfeq r5, r0 # is it not yet in WFI? + l.bf 1b # try again + + l.srli r6, r3, 16 # move mask to lower 16 bits(ds) + l.sfeqi r6, 1 # core 0 is special + l.bf 1f # don't touch the bit for core 0 + l.movhi r13, 0x700 # address of R_CPUCFG (ds) + l.lwz r5, 0x0444(r13) # core output clamps + l.or r5, r5, r6 # set bit to ... + l.sw 0x0444(r13), r5 # ... activate for our core + +1: l.lwz r5, 0x0440(r13) # CPU power-on reset + l.xori r6, r6, -1 # negate core mask + l.and r5, r5, r6 # clear bit to ... + l.sw 0x0440(r13), r5 # ... assert for our core + + l.ff1 r6, r3 # get core number from high mask + l.addi r6, r6, -17 # convert to 0-3 + l.slli r6, r6, 2 # r5: core number*4 (0-12) + l.add r6, r6, r13 # add to base address + l.ori r5, r0, 0xff # 0xff means all switches off + l.sw 0x0450(r6), r5 # core power switch registers + +1: l.sw 0x0400(r13),r0 # pull down our own reset line + + l.j 1b # just in case ... + l.nop 0x0 # (delay slot) diff --git a/plat/allwinner/common/include/mentor_i2c_plat.h b/plat/allwinner/common/include/mentor_i2c_plat.h new file mode 100644 index 0000000..d03f2d1 --- /dev/null +++ b/plat/allwinner/common/include/mentor_i2c_plat.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 Icenowy Zheng <icenowy@aosc.io> + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ +/* This driver provides I2C support for Allwinner sunXi SoCs */ + +#ifndef MENTOR_I2C_PLAT_H +#define MENTOR_I2C_PLAT_H + +#define CONFIG_SYS_TCLK 24000000 +#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C_SLAVE 0 + +#define I2C_INTERRUPT_CLEAR_INVERTED + +struct mentor_i2c_regs { + uint32_t slave_address; + uint32_t xtnd_slave_addr; + uint32_t data; + uint32_t control; + uint32_t status; + uint32_t baudrate; + uint32_t soft_reset; +}; + +#endif /* MENTOR_I2C_PLAT_H */ diff --git a/plat/allwinner/common/include/plat_macros.S b/plat/allwinner/common/include/plat_macros.S new file mode 100644 index 0000000..77f183d --- /dev/null +++ b/plat/allwinner/common/include/plat_macros.S @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017-2018, 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 <sunxi_mmap.h> + + /* --------------------------------------------- + * The below required platform porting macro + * prints out relevant GIC and CCI registers + * whenever an unhandled exception is taken in + * BL31. + * Clobbers: x0 - x10, x16, x17, sp + * --------------------------------------------- + */ + .macro plat_crash_print_regs + mov_imm x17, SUNXI_GICC_BASE + mov_imm x16, SUNXI_GICD_BASE + arm_print_gic_regs + .endm + +#endif /* PLAT_MACROS_S */ diff --git a/plat/allwinner/common/include/platform_def.h b/plat/allwinner/common/include/platform_def.h new file mode 100644 index 0000000..c9d075a --- /dev/null +++ b/plat/allwinner/common/include/platform_def.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include <common/tbbr/tbbr_img_def.h> +#include <lib/utils_def.h> +#include <plat/common/common_def.h> + +#include <sunxi_mmap.h> + +#ifdef SUNXI_BL31_IN_DRAM + +#define BL31_BASE SUNXI_DRAM_BASE +#define BL31_LIMIT (SUNXI_DRAM_BASE + 0x40000) + +#define MAX_XLAT_TABLES 4 +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) + +#define SUNXI_BL33_VIRT_BASE PRELOADED_BL33_BASE + +#else /* !SUNXI_BL31_IN_DRAM */ + +#define BL31_BASE (SUNXI_SRAM_A2_BASE + \ + SUNXI_SRAM_A2_BL31_OFFSET) +#define BL31_LIMIT (SUNXI_SRAM_A2_BASE + \ + SUNXI_SRAM_A2_SIZE - SUNXI_SCP_SIZE) + +/* Overwrite U-Boot SPL, but reserve the first page for the SPL header. */ +#define BL31_NOBITS_BASE (SUNXI_SRAM_A1_BASE + 0x1000) +#define BL31_NOBITS_LIMIT (SUNXI_SRAM_A1_BASE + SUNXI_SRAM_A1_SIZE) + +#define MAX_XLAT_TABLES 1 +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 28) + +#define SUNXI_BL33_VIRT_BASE SUNXI_DRAM_VIRT_BASE + +/* The SCP firmware is allocated the last 16KiB of SRAM A2. */ +#define SUNXI_SCP_BASE BL31_LIMIT +#define SUNXI_SCP_SIZE 0x4000 + +#endif /* SUNXI_BL31_IN_DRAM */ + +/* How much DRAM to map (to map BL33, for fetching the DTB from U-Boot) */ +#define SUNXI_DRAM_MAP_SIZE (64U << 20) + +#define CACHE_WRITEBACK_SHIFT 6 +#define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT) + +#define MAX_STATIC_MMAP_REGIONS 3 +#define MAX_MMAP_REGIONS (5 + MAX_STATIC_MMAP_REGIONS) + +#define PLAT_CSS_SCP_COM_SHARED_MEM_BASE \ + (SUNXI_SRAM_A2_BASE + SUNXI_SRAM_A2_SIZE - 0x200) + +/* These states are used directly for SCPI communication. */ +#define PLAT_MAX_PWR_LVL_STATES U(3) +#define PLAT_MAX_RET_STATE U(1) +#define PLAT_MAX_OFF_STATE U(3) + +#define PLAT_MAX_PWR_LVL U(2) +#define PLAT_NUM_PWR_DOMAINS (U(1) + \ + PLATFORM_CLUSTER_COUNT + \ + PLATFORM_CORE_COUNT) + +#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) + +#define PLATFORM_CLUSTER_COUNT U(1) +#define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER_COUNT * \ + PLATFORM_MAX_CPUS_PER_CLUSTER) +#define PLATFORM_MAX_CPUS_PER_CLUSTER U(4) +#define PLATFORM_STACK_SIZE (0x1000 / PLATFORM_CORE_COUNT) + +#ifndef SPD_none +#ifndef BL32_BASE +#define BL32_BASE SUNXI_DRAM_BASE +#endif +#endif + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/allwinner/common/include/sunxi_cpucfg_ncat.h b/plat/allwinner/common/include/sunxi_cpucfg_ncat.h new file mode 100644 index 0000000..22828c2 --- /dev/null +++ b/plat/allwinner/common/include/sunxi_cpucfg_ncat.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SUNXI_CPUCFG_H +#define SUNXI_CPUCFG_H + +#include <sunxi_mmap.h> + +/* c = cluster, n = core */ +#define SUNXI_CPUCFG_CLS_CTRL_REG0(c) (SUNXI_CPUCFG_BASE + 0x0010 + (c) * 0x10) +#define SUNXI_CPUCFG_CLS_CTRL_REG1(c) (SUNXI_CPUCFG_BASE + 0x0014 + (c) * 0x10) +#define SUNXI_CPUCFG_CACHE_CFG_REG (SUNXI_CPUCFG_BASE + 0x0024) +/* The T507 datasheet does not mention this register. */ +#define SUNXI_CPUCFG_DBG_REG0 (SUNXI_CPUCFG_BASE + 0x00c0) + +#define SUNXI_CPUCFG_RST_CTRL_REG(c) (SUNXI_CPUCFG_BASE + 0x0000 + (c) * 4) +#define SUNXI_CPUCFG_RVBAR_LO_REG(n) (SUNXI_CPUCFG_BASE + 0x0040 + (n) * 8) +#define SUNXI_CPUCFG_RVBAR_HI_REG(n) (SUNXI_CPUCFG_BASE + 0x0044 + (n) * 8) + +#define SUNXI_C0_CPU_CTRL_REG(n) (SUNXI_CPUCFG_BASE + 0x0060 + (n) * 4) + +#define SUNXI_CPU_CTRL_REG(n) (SUNXI_CPUSUBSYS_BASE + 0x20 + (n) * 4) +#define SUNXI_ALT_RVBAR_LO_REG(n) (SUNXI_CPUSUBSYS_BASE + 0x40 + (n) * 8) +#define SUNXI_ALT_RVBAR_HI_REG(n) (SUNXI_CPUSUBSYS_BASE + 0x44 + (n) * 8) + +#define SUNXI_POWERON_RST_REG(c) (SUNXI_R_CPUCFG_BASE + 0x0040 + (c) * 4) +#define SUNXI_POWEROFF_GATING_REG(c) (SUNXI_R_CPUCFG_BASE + 0x0044 + (c) * 4) +#define SUNXI_CPU_POWER_CLAMP_REG(c, n) (SUNXI_R_CPUCFG_BASE + 0x0050 + \ + (c) * 0x10 + (n) * 4) +#define SUNXI_CPU_UNK_REG(n) (SUNXI_R_CPUCFG_BASE + 0x0070 + (n) * 4) + +#define SUNXI_CPUIDLE_EN_REG (SUNXI_R_CPUCFG_BASE + 0x0100) +#define SUNXI_CORE_CLOSE_REG (SUNXI_R_CPUCFG_BASE + 0x0104) +#define SUNXI_PWR_SW_DELAY_REG (SUNXI_R_CPUCFG_BASE + 0x0140) +#define SUNXI_CONFIG_DELAY_REG (SUNXI_R_CPUCFG_BASE + 0x0144) + +#define SUNXI_AA64nAA32_REG SUNXI_CPUCFG_CLS_CTRL_REG0 +#define SUNXI_AA64nAA32_OFFSET 24 + +#endif /* SUNXI_CPUCFG_H */ diff --git a/plat/allwinner/common/include/sunxi_cpucfg_ncat2.h b/plat/allwinner/common/include/sunxi_cpucfg_ncat2.h new file mode 100644 index 0000000..d4aec19 --- /dev/null +++ b/plat/allwinner/common/include/sunxi_cpucfg_ncat2.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Sipeed + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SUNXI_CPUCFG_H +#define SUNXI_CPUCFG_H + +#include <sunxi_mmap.h> + +/* c = cluster, n = core */ +#define SUNXI_CPUCFG_CLS_CTRL_REG0(c) (SUNXI_C0_CPUXCFG_BASE + 0x0010) +#define SUNXI_CPUCFG_CLS_CTRL_REG1(c) (SUNXI_C0_CPUXCFG_BASE + 0x0014) +#define SUNXI_CPUCFG_CACHE_CFG_REG (SUNXI_C0_CPUXCFG_BASE + 0x0024) +#define SUNXI_CPUCFG_DBG_REG0 (SUNXI_C0_CPUXCFG_BASE + 0x00c0) + +#define SUNXI_CPUCFG_RST_CTRL_REG(c) (SUNXI_C0_CPUXCFG_BASE + 0x0000) +#define SUNXI_CPUCFG_GEN_CTRL_REG0(c) (SUNXI_CPUCFG_BASE + 0x0000) +#define SUNXI_CPUCFG_RVBAR_LO_REG(n) (SUNXI_CPUCFG_BASE + 0x0040 + (n) * 8) +#define SUNXI_CPUCFG_RVBAR_HI_REG(n) (SUNXI_CPUCFG_BASE + 0x0044 + (n) * 8) + +#define SUNXI_POWERON_RST_REG(c) (SUNXI_R_CPUCFG_BASE + 0x0040 + (c) * 4) +#define SUNXI_POWEROFF_GATING_REG(c) (SUNXI_R_CPUCFG_BASE + 0x0044 + (c) * 4) +#define SUNXI_CPU_POWER_CLAMP_REG(c, n) (SUNXI_R_CPUCFG_BASE + 0x0050 + \ + (c) * 0x10 + (n) * 4) + +#define SUNXI_AA64nAA32_REG SUNXI_CPUCFG_GEN_CTRL_REG0 +#define SUNXI_AA64nAA32_OFFSET 4 + +static inline bool sunxi_cpucfg_has_per_cluster_regs(void) +{ + return true; +} + +#endif /* SUNXI_CPUCFG_H */ diff --git a/plat/allwinner/common/include/sunxi_def.h b/plat/allwinner/common/include/sunxi_def.h new file mode 100644 index 0000000..20f6c49 --- /dev/null +++ b/plat/allwinner/common/include/sunxi_def.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SUNXI_DEF_H +#define SUNXI_DEF_H + +/* Clock configuration */ +#define SUNXI_OSC24M_CLK_IN_HZ 24000000 + +/* UART configuration */ +#define SUNXI_UART0_BAUDRATE 115200 +#define SUNXI_UART0_CLK_IN_HZ SUNXI_OSC24M_CLK_IN_HZ + +#define SUNXI_SOC_A64 0x1689 +#define SUNXI_SOC_H5 0x1718 +#define SUNXI_SOC_H6 0x1728 +#define SUNXI_SOC_H616 0x1823 +#define SUNXI_SOC_R329 0x1851 + +#define SUNXI_VER_BITS_MASK 0xffU +#define JEDEC_ALLWINNER_BKID 9U +#define JEDEC_ALLWINNER_MFID 0x9eU + +#endif /* SUNXI_DEF_H */ diff --git a/plat/allwinner/common/include/sunxi_private.h b/plat/allwinner/common/include/sunxi_private.h new file mode 100644 index 0000000..6a38657 --- /dev/null +++ b/plat/allwinner/common/include/sunxi_private.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SUNXI_PRIVATE_H +#define SUNXI_PRIVATE_H + +#include <common/fdt_fixup.h> + +#include <lib/psci/psci.h> + +extern const struct psci_cpu_idle_state sunxi_idle_states[]; + +void sunxi_configure_mmu_el3(int flags); + +void sunxi_cpu_on(u_register_t mpidr); +void sunxi_cpu_power_off_others(void); +void sunxi_cpu_power_off_self(void); +void sunxi_power_down(void); + +#if SUNXI_PSCI_USE_NATIVE +void sunxi_set_native_psci_ops(const plat_psci_ops_t **psci_ops); +#else +static inline void sunxi_set_native_psci_ops(const plat_psci_ops_t **psci_ops) +{ +} +#endif +#if SUNXI_PSCI_USE_SCPI +bool sunxi_psci_is_scpi(void); +int sunxi_set_scpi_psci_ops(const plat_psci_ops_t **psci_ops); +#else +static inline bool sunxi_psci_is_scpi(void) +{ + return false; +} +static inline int sunxi_set_scpi_psci_ops(const plat_psci_ops_t **psci_ops) +{ + return -1; +} +#endif +int sunxi_validate_ns_entrypoint(uintptr_t ns_entrypoint); + +int sunxi_pmic_setup(uint16_t socid, const void *fdt); +void sunxi_security_setup(void); + +uint16_t sunxi_read_soc_id(void); +void sunxi_set_gpio_out(char port, int pin, bool level_high); +int sunxi_init_platform_r_twi(uint16_t socid, bool use_rsb); +void sunxi_execute_arisc_code(uint32_t *code, size_t size, uint16_t param); + +#if SUNXI_AMEND_DTB +void sunxi_prepare_dtb(void *fdt); +#else +static inline void sunxi_prepare_dtb(void *fdt) +{ +} +#endif + +#endif /* SUNXI_PRIVATE_H */ diff --git a/plat/allwinner/common/plat_helpers.S b/plat/allwinner/common/plat_helpers.S new file mode 100644 index 0000000..b00c7ae --- /dev/null +++ b/plat/allwinner/common/plat_helpers.S @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <asm_macros.S> +#include <sunxi_def.h> +#include <sunxi_mmap.h> + + .globl plat_crash_console_init + .globl plat_crash_console_putc + .globl plat_crash_console_flush + .globl plat_my_core_pos + .globl platform_mem_init + .globl plat_report_exception + +func plat_crash_console_init + mov_imm x0, SUNXI_UART0_BASE + mov_imm x1, SUNXI_UART0_CLK_IN_HZ + mov_imm x2, SUNXI_UART0_BAUDRATE + b console_16550_core_init +endfunc plat_crash_console_init + +func plat_crash_console_putc + mov_imm x1, SUNXI_UART0_BASE + b console_16550_core_putc +endfunc plat_crash_console_putc + +func plat_crash_console_flush + ret +endfunc plat_crash_console_flush + +func plat_my_core_pos + mrs x0, mpidr_el1 + and x1, x0, #MPIDR_CLUSTER_MASK + and x0, x0, #MPIDR_CPU_MASK + add x0, x0, x1, LSR #6 + ret +endfunc plat_my_core_pos + +func platform_mem_init + ret +endfunc platform_mem_init + +func plat_report_exception + ret +endfunc plat_report_exception diff --git a/plat/allwinner/common/sunxi_bl31_setup.c b/plat/allwinner/common/sunxi_bl31_setup.c new file mode 100644 index 0000000..a32124a --- /dev/null +++ b/plat/allwinner/common/sunxi_bl31_setup.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <libfdt.h> + +#include <platform_def.h> + +#include <arch.h> +#include <arch_helpers.h> +#include <common/debug.h> +#include <common/fdt_fixup.h> +#include <common/fdt_wrappers.h> +#include <drivers/arm/gicv2.h> +#include <drivers/console.h> +#include <drivers/generic_delay_timer.h> +#include <drivers/ti/uart/uart_16550.h> +#include <lib/mmio.h> +#include <plat/common/platform.h> + +#include <sunxi_def.h> +#include <sunxi_mmap.h> +#include <sunxi_private.h> + + +static entry_point_info_t bl32_image_ep_info; +static entry_point_info_t bl33_image_ep_info; + +static console_t console; + +static void *fdt; + +static const gicv2_driver_data_t sunxi_gic_data = { + .gicd_base = SUNXI_GICD_BASE, + .gicc_base = SUNXI_GICC_BASE, +}; + +/* + * Try to find a DTB loaded in memory by previous stages. + * + * At the moment we implement a heuristic to find the DTB attached to U-Boot: + * U-Boot appends its DTB to the end of the image. Assuming that BL33 is + * U-Boot, try to find the size of the U-Boot image to learn the DTB address. + * The generic ARMv8 U-Boot image contains the load address and its size + * as u64 variables at the beginning of the image. There might be padding + * or other headers before that data, so scan the first 2KB after the BL33 + * entry point to find the load address, which should be followed by the + * size. Adding those together gives us the address of the DTB. + */ +static void sunxi_find_dtb(void) +{ + uint64_t *u_boot_base; + int i; + + u_boot_base = (void *)SUNXI_BL33_VIRT_BASE; + + for (i = 0; i < 2048 / sizeof(uint64_t); i++) { + void *dtb_base; + + if (u_boot_base[i] != PRELOADED_BL33_BASE) + continue; + + /* Does the suspected U-Boot size look anyhow reasonable? */ + if (u_boot_base[i + 1] >= 256 * 1024 * 1024) + continue; + + /* end of the image: base address + size */ + dtb_base = (char *)u_boot_base + u_boot_base[i + 1]; + + if (fdt_check_header(dtb_base) == 0) { + fdt = dtb_base; + return; + } + } +} + +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 soon as possible */ + console_16550_register(SUNXI_UART0_BASE, SUNXI_UART0_CLK_IN_HZ, + SUNXI_UART0_BAUDRATE, &console); + +#ifdef BL32_BASE + /* Populate entry point information for BL32 */ + SET_PARAM_HEAD(&bl32_image_ep_info, PARAM_EP, VERSION_1, 0); + SET_SECURITY_STATE(bl32_image_ep_info.h.attr, SECURE); + bl32_image_ep_info.pc = BL32_BASE; +#endif + + /* Populate entry point information for BL33 */ + SET_PARAM_HEAD(&bl33_image_ep_info, PARAM_EP, VERSION_1, 0); + /* + * Tell BL31 where the non-trusted software image + * is located and the entry state information + */ + bl33_image_ep_info.pc = PRELOADED_BL33_BASE; + bl33_image_ep_info.spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, + DISABLE_ALL_EXCEPTIONS); + SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); +} + +void bl31_plat_arch_setup(void) +{ + sunxi_configure_mmu_el3(0); +} + +void bl31_platform_setup(void) +{ + const char *soc_name; + uint16_t soc_id = sunxi_read_soc_id(); + + switch (soc_id) { + case SUNXI_SOC_A64: + soc_name = "A64/H64/R18"; + break; + case SUNXI_SOC_H5: + soc_name = "H5"; + break; + case SUNXI_SOC_H6: + soc_name = "H6"; + break; + case SUNXI_SOC_H616: + soc_name = "H616"; + break; + case SUNXI_SOC_R329: + soc_name = "R329"; + break; + default: + soc_name = "unknown"; + break; + } + NOTICE("BL31: Detected Allwinner %s SoC (%04x)\n", soc_name, soc_id); + + generic_delay_timer_init(); + + sunxi_find_dtb(); + if (fdt) { + const char *model; + int length; + + model = fdt_getprop(fdt, 0, "model", &length); + NOTICE("BL31: Found U-Boot DTB at %p, model: %s\n", fdt, + model ?: "unknown"); + } else { + NOTICE("BL31: No DTB found.\n"); + } + + /* Configure the interrupt controller */ + gicv2_driver_init(&sunxi_gic_data); + gicv2_distif_init(); + gicv2_pcpu_distif_init(); + gicv2_cpuif_enable(); + + sunxi_security_setup(); + + /* + * On the A64 U-Boot's SPL sets the bus clocks to some conservative + * values, to work around FEL mode instabilities with SRAM C accesses. + * FEL mode is gone when we reach ATF, so bring the AHB1 bus + * (the "main" bus) clock frequency back to the recommended 200MHz, + * for improved performance. + */ + if (soc_id == SUNXI_SOC_A64) + mmio_write_32(SUNXI_CCU_BASE + 0x54, 0x00003180); + + /* + * U-Boot or the kernel don't setup AHB2, which leaves it at the + * AHB1 frequency (200 MHz, see above). However Allwinner recommends + * 300 MHz, for improved Ethernet and USB performance. Switch the + * clock to use "PLL_PERIPH0 / 2". + */ + if (soc_id == SUNXI_SOC_A64 || soc_id == SUNXI_SOC_H5) + mmio_write_32(SUNXI_CCU_BASE + 0x5c, 0x1); + + sunxi_pmic_setup(soc_id, fdt); + + INFO("BL31: Platform setup done\n"); +} + +void bl31_plat_runtime_setup(void) +{ + /* Change the DTB if the configuration requires so. */ + sunxi_prepare_dtb(fdt); + + console_switch_state(CONSOLE_FLAG_RUNTIME); +} + +entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type) +{ + assert(sec_state_is_valid(type) != 0); + + if (type == NON_SECURE) + return &bl33_image_ep_info; + + if ((type == SECURE) && bl32_image_ep_info.pc) + return &bl32_image_ep_info; + + return NULL; +} diff --git a/plat/allwinner/common/sunxi_common.c b/plat/allwinner/common/sunxi_common.c new file mode 100644 index 0000000..62f4fcb --- /dev/null +++ b/plat/allwinner/common/sunxi_common.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> + +#include <common/debug.h> +#include <lib/mmio.h> +#include <lib/smccc.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <services/arm_arch_svc.h> + +#include <sunxi_def.h> +#include <sunxi_mmap.h> +#include <sunxi_private.h> + +static const mmap_region_t sunxi_mmap[MAX_STATIC_MMAP_REGIONS + 1] = { + MAP_REGION_FLAT(SUNXI_SRAM_BASE, SUNXI_SRAM_SIZE, + MT_DEVICE | MT_RW | MT_SECURE | MT_EXECUTE_NEVER), + MAP_REGION_FLAT(SUNXI_DEV_BASE, SUNXI_DEV_SIZE, + MT_DEVICE | MT_RW | MT_SECURE | MT_EXECUTE_NEVER), + MAP_REGION(PRELOADED_BL33_BASE, SUNXI_BL33_VIRT_BASE, + SUNXI_DRAM_MAP_SIZE, MT_RW_DATA | MT_NS), + {}, +}; + +unsigned int plat_get_syscnt_freq2(void) +{ + return SUNXI_OSC24M_CLK_IN_HZ; +} + +void sunxi_configure_mmu_el3(int flags) +{ + mmap_add_region(BL_CODE_BASE, BL_CODE_BASE, + BL_CODE_END - BL_CODE_BASE, + MT_CODE | MT_SECURE); + mmap_add_region(BL_CODE_END, BL_CODE_END, + BL_END - BL_CODE_END, + MT_RW_DATA | MT_SECURE); +#if SEPARATE_CODE_AND_RODATA + mmap_add_region(BL_RO_DATA_BASE, BL_RO_DATA_BASE, + BL_RO_DATA_END - BL_RO_DATA_BASE, + MT_RO_DATA | MT_SECURE); +#endif +#if SEPARATE_NOBITS_REGION + mmap_add_region(BL_NOBITS_BASE, BL_NOBITS_BASE, + BL_NOBITS_END - BL_NOBITS_BASE, + MT_RW_DATA | MT_SECURE); +#endif +#if USE_COHERENT_MEM + 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); +#endif + + mmap_add(sunxi_mmap); + init_xlat_tables(); + + enable_mmu_el3(0); +} + +#define SRAM_VER_REG (SUNXI_SYSCON_BASE + 0x24) +uint16_t sunxi_read_soc_id(void) +{ + uint32_t reg = mmio_read_32(SRAM_VER_REG); + + /* Set bit 15 to prepare for the SOCID read. */ + mmio_write_32(SRAM_VER_REG, reg | BIT(15)); + + reg = mmio_read_32(SRAM_VER_REG); + + /* deactivate the SOCID access again */ + mmio_write_32(SRAM_VER_REG, reg & ~BIT(15)); + + return reg >> 16; +} + +/* + * Configure a given pin to the GPIO-OUT function and sets its level. + * The port is given as a capital letter, the pin is the number within + * this port group. + * So to set pin PC7 to high, use: sunxi_set_gpio_out('C', 7, true); + */ +void sunxi_set_gpio_out(char port, int pin, bool level_high) +{ + uintptr_t port_base; + + if (port < 'A' || port > 'L') + return; + if (port == 'L') + port_base = SUNXI_R_PIO_BASE; + else + port_base = SUNXI_PIO_BASE + (port - 'A') * 0x24; + + /* Set the new level first before configuring the pin. */ + if (level_high) + mmio_setbits_32(port_base + 0x10, BIT(pin)); + else + mmio_clrbits_32(port_base + 0x10, BIT(pin)); + + /* configure pin as GPIO out (4(3) bits per pin, 1: GPIO out */ + mmio_clrsetbits_32(port_base + (pin / 8) * 4, + 0x7 << ((pin % 8) * 4), + 0x1 << ((pin % 8) * 4)); +} + +int sunxi_init_platform_r_twi(uint16_t socid, bool use_rsb) +{ + uint32_t pin_func = 0x77; + uint32_t device_bit; + unsigned int reset_offset = 0xb0; + + switch (socid) { + case SUNXI_SOC_H5: + if (use_rsb) + return -ENODEV; + pin_func = 0x22; + device_bit = BIT(6); + break; + case SUNXI_SOC_H6: + case SUNXI_SOC_H616: + pin_func = use_rsb ? 0x22 : 0x33; + device_bit = BIT(16); + reset_offset = use_rsb ? 0x1bc : 0x19c; + break; + case SUNXI_SOC_A64: + pin_func = use_rsb ? 0x22 : 0x33; + device_bit = use_rsb ? BIT(3) : BIT(6); + break; + default: + INFO("R_I2C/RSB on Allwinner 0x%x SoC not supported\n", socid); + return -ENODEV; + } + + /* un-gate R_PIO clock */ + if (socid != SUNXI_SOC_H6 && socid != SUNXI_SOC_H616) + mmio_setbits_32(SUNXI_R_PRCM_BASE + 0x28, BIT(0)); + + /* switch pins PL0 and PL1 to the desired function */ + mmio_clrsetbits_32(SUNXI_R_PIO_BASE + 0x00, 0xffU, pin_func); + + /* level 2 drive strength */ + mmio_clrsetbits_32(SUNXI_R_PIO_BASE + 0x14, 0x0fU, 0xaU); + + /* set both pins to pull-up */ + mmio_clrsetbits_32(SUNXI_R_PIO_BASE + 0x1c, 0x0fU, 0x5U); + + /* un-gate clock */ + if (socid != SUNXI_SOC_H6 && socid != SUNXI_SOC_H616) + mmio_setbits_32(SUNXI_R_PRCM_BASE + 0x28, device_bit); + else + mmio_setbits_32(SUNXI_R_PRCM_BASE + reset_offset, BIT(0)); + + /* assert, then de-assert reset of I2C/RSB controller */ + mmio_clrbits_32(SUNXI_R_PRCM_BASE + reset_offset, device_bit); + mmio_setbits_32(SUNXI_R_PRCM_BASE + reset_offset, device_bit); + + return 0; +} + +int32_t plat_is_smccc_feature_available(u_register_t fid) +{ + switch (fid) { + case SMCCC_ARCH_SOC_ID: + return SMC_ARCH_CALL_SUCCESS; + default: + return SMC_ARCH_CALL_NOT_SUPPORTED; + } +} + +int32_t plat_get_soc_version(void) +{ + int32_t ret; + + ret = SOC_ID_SET_JEP_106(JEDEC_ALLWINNER_BKID, JEDEC_ALLWINNER_MFID); + + return ret | (sunxi_read_soc_id() & SOC_ID_IMPL_DEF_MASK); +} + +int32_t plat_get_soc_revision(void) +{ + uint32_t reg = mmio_read_32(SRAM_VER_REG); + + return reg & SUNXI_VER_BITS_MASK; +} diff --git a/plat/allwinner/common/sunxi_cpu_ops.c b/plat/allwinner/common/sunxi_cpu_ops.c new file mode 100644 index 0000000..30841e2 --- /dev/null +++ b/plat/allwinner/common/sunxi_cpu_ops.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <platform_def.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <lib/mmio.h> +#include <lib/utils_def.h> +#include <plat/common/platform.h> + +#include <sunxi_cpucfg.h> +#include <sunxi_mmap.h> +#include <sunxi_private.h> + +#ifndef SUNXI_C0_CPU_CTRL_REG +#define SUNXI_C0_CPU_CTRL_REG(n) 0 +#define SUNXI_CPU_UNK_REG(n) 0 +#define SUNXI_CPU_CTRL_REG(n) 0 +#endif + +static void sunxi_cpu_disable_power(unsigned int cluster, unsigned int core) +{ + if (mmio_read_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core)) == 0xff) + return; + + VERBOSE("PSCI: Disabling power to cluster %d core %d\n", cluster, core); + + mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0xff); +} + +static void sunxi_cpu_enable_power(unsigned int cluster, unsigned int core) +{ + if (mmio_read_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core)) == 0) + return; + + VERBOSE("PSCI: Enabling power to cluster %d core %d\n", cluster, core); + + /* Power enable sequence from original Allwinner sources */ + mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0xfe); + mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0xf8); + mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0xe0); + mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0x80); + mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0x00); + udelay(1); +} + +/* We can't turn ourself off like this, but it works for other cores. */ +static void sunxi_cpu_off(u_register_t mpidr) +{ + unsigned int cluster = MPIDR_AFFLVL1_VAL(mpidr); + unsigned int core = MPIDR_AFFLVL0_VAL(mpidr); + + VERBOSE("PSCI: Powering off cluster %d core %d\n", cluster, core); + + if (sunxi_cpucfg_has_per_cluster_regs()) { + /* Deassert DBGPWRDUP */ + mmio_clrbits_32(SUNXI_CPUCFG_DBG_REG0, BIT(core)); + /* Activate the core output clamps, but not for core 0. */ + if (core != 0) { + mmio_setbits_32(SUNXI_POWEROFF_GATING_REG(cluster), + BIT(core)); + } + /* Assert CPU power-on reset */ + mmio_clrbits_32(SUNXI_POWERON_RST_REG(cluster), BIT(core)); + /* Remove power from the CPU */ + sunxi_cpu_disable_power(cluster, core); + } else { + /* power down(?) debug core */ + mmio_clrbits_32(SUNXI_C0_CPU_CTRL_REG(core), BIT(8)); + /* ??? Activate the core output clamps, but not for core 0 */ + if (core != 0) { + mmio_setbits_32(SUNXI_CPU_UNK_REG(core), BIT(1)); + } + /* ??? Assert CPU power-on reset ??? */ + mmio_clrbits_32(SUNXI_CPU_UNK_REG(core), BIT(0)); + /* Remove power from the CPU */ + sunxi_cpu_disable_power(cluster, core); + } +} + +void sunxi_cpu_on(u_register_t mpidr) +{ + unsigned int cluster = MPIDR_AFFLVL1_VAL(mpidr); + unsigned int core = MPIDR_AFFLVL0_VAL(mpidr); + + VERBOSE("PSCI: Powering on cluster %d core %d\n", cluster, core); + + if (sunxi_cpucfg_has_per_cluster_regs()) { + /* Assert CPU core reset */ + mmio_clrbits_32(SUNXI_CPUCFG_RST_CTRL_REG(cluster), BIT(core)); + /* Assert CPU power-on reset */ + mmio_clrbits_32(SUNXI_POWERON_RST_REG(cluster), BIT(core)); + /* Set CPU to start in AArch64 mode */ + mmio_setbits_32(SUNXI_AA64nAA32_REG(cluster), + BIT(SUNXI_AA64nAA32_OFFSET + core)); + /* Apply power to the CPU */ + sunxi_cpu_enable_power(cluster, core); + /* Release the core output clamps */ + mmio_clrbits_32(SUNXI_POWEROFF_GATING_REG(cluster), BIT(core)); + /* Deassert CPU power-on reset */ + mmio_setbits_32(SUNXI_POWERON_RST_REG(cluster), BIT(core)); + /* Deassert CPU core reset */ + mmio_setbits_32(SUNXI_CPUCFG_RST_CTRL_REG(cluster), BIT(core)); + /* Assert DBGPWRDUP */ + mmio_setbits_32(SUNXI_CPUCFG_DBG_REG0, BIT(core)); + } else { + /* Assert CPU core reset */ + mmio_clrbits_32(SUNXI_C0_CPU_CTRL_REG(core), BIT(0)); + /* ??? Assert CPU power-on reset ??? */ + mmio_clrbits_32(SUNXI_CPU_UNK_REG(core), BIT(0)); + + /* Set CPU to start in AArch64 mode */ + mmio_setbits_32(SUNXI_CPU_CTRL_REG(core), BIT(0)); + + /* Apply power to the CPU */ + sunxi_cpu_enable_power(cluster, core); + + /* ??? Release the core output clamps ??? */ + mmio_clrbits_32(SUNXI_CPU_UNK_REG(core), BIT(1)); + /* ??? Deassert CPU power-on reset ??? */ + mmio_setbits_32(SUNXI_CPU_UNK_REG(core), BIT(0)); + /* Deassert CPU core reset */ + mmio_setbits_32(SUNXI_C0_CPU_CTRL_REG(core), BIT(0)); + /* power up(?) debug core */ + mmio_setbits_32(SUNXI_C0_CPU_CTRL_REG(core), BIT(8)); + } +} + +void sunxi_cpu_power_off_others(void) +{ + u_register_t self = read_mpidr(); + unsigned int cluster; + unsigned int core; + + for (cluster = 0; cluster < PLATFORM_CLUSTER_COUNT; ++cluster) { + for (core = 0; core < PLATFORM_MAX_CPUS_PER_CLUSTER; ++core) { + u_register_t mpidr = (cluster << MPIDR_AFF1_SHIFT) | + (core << MPIDR_AFF0_SHIFT) | + BIT(31); + if (mpidr != self) + sunxi_cpu_off(mpidr); + } + } +} diff --git a/plat/allwinner/common/sunxi_native_pm.c b/plat/allwinner/common/sunxi_native_pm.c new file mode 100644 index 0000000..148f50e --- /dev/null +++ b/plat/allwinner/common/sunxi_native_pm.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch_helpers.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 <sunxi_mmap.h> +#include <sunxi_private.h> + +#define SUNXI_WDOG0_CTRL_REG (SUNXI_R_WDOG_BASE + 0x0010) +#define SUNXI_WDOG0_CFG_REG (SUNXI_R_WDOG_BASE + 0x0014) +#define SUNXI_WDOG0_MODE_REG (SUNXI_R_WDOG_BASE + 0x0018) + +static int sunxi_pwr_domain_on(u_register_t mpidr) +{ + sunxi_cpu_on(mpidr); + + return PSCI_E_SUCCESS; +} + +static void sunxi_pwr_domain_off(const psci_power_state_t *target_state) +{ + gicv2_cpuif_disable(); + + sunxi_cpu_power_off_self(); +} + +static void sunxi_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + gicv2_pcpu_distif_init(); + gicv2_cpuif_enable(); +} + +static void __dead2 sunxi_system_off(void) +{ + gicv2_cpuif_disable(); + + /* Attempt to power down the board (may not return) */ + sunxi_power_down(); + + /* Turn off all CPUs */ + sunxi_cpu_power_off_others(); + sunxi_cpu_power_off_self(); + psci_power_down_wfi(); +} + +static void __dead2 sunxi_system_reset(void) +{ + gicv2_cpuif_disable(); + + /* Reset the whole system when the watchdog times out */ + mmio_write_32(SUNXI_WDOG0_CFG_REG, 1); + /* Enable the watchdog with the shortest timeout (0.5 seconds) */ + mmio_write_32(SUNXI_WDOG0_MODE_REG, (0 << 4) | 1); + /* Wait for twice the watchdog timeout before panicking */ + mdelay(1000); + + ERROR("PSCI: System reset failed\n"); + panic(); +} + +static const plat_psci_ops_t sunxi_native_psci_ops = { + .pwr_domain_on = sunxi_pwr_domain_on, + .pwr_domain_off = sunxi_pwr_domain_off, + .pwr_domain_on_finish = sunxi_pwr_domain_on_finish, + .system_off = sunxi_system_off, + .system_reset = sunxi_system_reset, + .validate_ns_entrypoint = sunxi_validate_ns_entrypoint, +}; + +void sunxi_set_native_psci_ops(const plat_psci_ops_t **psci_ops) +{ + *psci_ops = &sunxi_native_psci_ops; +} diff --git a/plat/allwinner/common/sunxi_pm.c b/plat/allwinner/common/sunxi_pm.c new file mode 100644 index 0000000..ebc406b --- /dev/null +++ b/plat/allwinner/common/sunxi_pm.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <platform_def.h> + +#include <common/debug.h> +#include <common/fdt_fixup.h> +#include <lib/mmio.h> +#include <lib/psci/psci.h> + +#include <sunxi_cpucfg.h> +#include <sunxi_private.h> + +static bool psci_is_scpi; + +#if SUNXI_PSCI_USE_SCPI +bool sunxi_psci_is_scpi(void) +{ + return psci_is_scpi; +} +#endif + +#ifndef SUNXI_ALT_RVBAR_LO_REG +#define SUNXI_ALT_RVBAR_LO_REG(n) 0 +#define SUNXI_ALT_RVBAR_HI_REG(n) 0 +#endif + +int sunxi_validate_ns_entrypoint(uintptr_t ns_entrypoint) +{ + /* The non-secure entry point must be in DRAM */ + if (ns_entrypoint < SUNXI_DRAM_BASE) { + return PSCI_E_INVALID_ADDRESS; + } + + return PSCI_E_SUCCESS; +} + +int plat_setup_psci_ops(uintptr_t sec_entrypoint, + const plat_psci_ops_t **psci_ops) +{ + assert(psci_ops); + + /* Program all CPU entry points. */ + for (unsigned int cpu = 0; cpu < PLATFORM_CORE_COUNT; ++cpu) { + if (sunxi_cpucfg_has_per_cluster_regs()) { + mmio_write_32(SUNXI_CPUCFG_RVBAR_LO_REG(cpu), + sec_entrypoint & 0xffffffff); + mmio_write_32(SUNXI_CPUCFG_RVBAR_HI_REG(cpu), + sec_entrypoint >> 32); + } else { + mmio_write_32(SUNXI_ALT_RVBAR_LO_REG(cpu), + sec_entrypoint & 0xffffffff); + mmio_write_32(SUNXI_ALT_RVBAR_HI_REG(cpu), + sec_entrypoint >> 32); + } + } + + if (sunxi_set_scpi_psci_ops(psci_ops) == 0) { + INFO("PSCI: Suspend is available via SCPI\n"); + psci_is_scpi = true; + } else { + INFO("PSCI: Suspend is unavailable\n"); + sunxi_set_native_psci_ops(psci_ops); + } + + return 0; +} diff --git a/plat/allwinner/common/sunxi_prepare_dtb.c b/plat/allwinner/common/sunxi_prepare_dtb.c new file mode 100644 index 0000000..66af35a --- /dev/null +++ b/plat/allwinner/common/sunxi_prepare_dtb.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021, ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <libfdt.h> + +#include <common/debug.h> +#include <common/fdt_fixup.h> +#include <common/fdt_wrappers.h> + +#include <sunxi_private.h> + +void sunxi_prepare_dtb(void *fdt) +{ + int ret; + + if (fdt == NULL || fdt_check_header(fdt) != 0) { + return; + } + + ret = fdt_open_into(fdt, fdt, 0x10000); + if (ret < 0) { + ERROR("Preparing devicetree at %p: error %d\n", fdt, ret); + return; + } + +#ifdef SUNXI_BL31_IN_DRAM + /* Reserve memory used by Trusted Firmware. */ + if (fdt_add_reserved_memory(fdt, "tf-a@40000000", BL31_BASE, + BL31_LIMIT - BL31_BASE)) { + WARN("Failed to add reserved memory nodes to DT.\n"); + } +#endif + + if (sunxi_psci_is_scpi()) { + ret = fdt_add_cpu_idle_states(fdt, sunxi_idle_states); + if (ret < 0) { + WARN("Failed to add idle states to DT: %d\n", ret); + } + } + + ret = fdt_pack(fdt); + if (ret < 0) { + ERROR("Failed to pack devicetree at %p: error %d\n", + fdt, ret); + } + + clean_dcache_range((uintptr_t)fdt, fdt_blob_size(fdt)); + INFO("Changed devicetree.\n"); +} diff --git a/plat/allwinner/common/sunxi_scpi_pm.c b/plat/allwinner/common/sunxi_scpi_pm.c new file mode 100644 index 0000000..6a0e967 --- /dev/null +++ b/plat/allwinner/common/sunxi_scpi_pm.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <platform_def.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/arm/css/css_scpi.h> +#include <drivers/arm/gicv2.h> +#include <lib/mmio.h> +#include <lib/psci/psci.h> + +#include <sunxi_mmap.h> +#include <sunxi_private.h> + +/* + * The addresses for the SCP exception vectors are defined in the or1k + * architecture specification. + */ +#define OR1K_VEC_FIRST 0x01 +#define OR1K_VEC_LAST 0x0e +#define OR1K_VEC_ADDR(n) (0x100 * (n)) + +/* + * This magic value is the little-endian representation of the or1k + * instruction "l.mfspr r2, r0, 0x12", which is guaranteed to be the + * first instruction in the SCP firmware. + */ +#define SCP_FIRMWARE_MAGIC 0xb4400012 + +#define PLAT_LOCAL_PSTATE_WIDTH U(4) +#define PLAT_LOCAL_PSTATE_MASK ((U(1) << PLAT_LOCAL_PSTATE_WIDTH) - 1) + +#define CPU_PWR_LVL MPIDR_AFFLVL0 +#define CLUSTER_PWR_LVL MPIDR_AFFLVL1 +#define SYSTEM_PWR_LVL MPIDR_AFFLVL2 + +#define CPU_PWR_STATE(state) \ + ((state)->pwr_domain_state[CPU_PWR_LVL]) +#define CLUSTER_PWR_STATE(state) \ + ((state)->pwr_domain_state[CLUSTER_PWR_LVL]) +#define SYSTEM_PWR_STATE(state) \ + ((state)->pwr_domain_state[SYSTEM_PWR_LVL]) + +static void sunxi_cpu_standby(plat_local_state_t cpu_state) +{ + u_register_t scr = read_scr_el3(); + + assert(is_local_state_retn(cpu_state)); + + write_scr_el3(scr | SCR_IRQ_BIT); + wfi(); + write_scr_el3(scr); +} + +static int sunxi_pwr_domain_on(u_register_t mpidr) +{ + scpi_set_css_power_state(mpidr, + scpi_power_on, + scpi_power_on, + scpi_power_on); + + return PSCI_E_SUCCESS; +} + +static void sunxi_pwr_domain_off(const psci_power_state_t *target_state) +{ + plat_local_state_t cpu_pwr_state = CPU_PWR_STATE(target_state); + plat_local_state_t cluster_pwr_state = CLUSTER_PWR_STATE(target_state); + plat_local_state_t system_pwr_state = SYSTEM_PWR_STATE(target_state); + + if (is_local_state_off(cpu_pwr_state)) { + gicv2_cpuif_disable(); + } + + scpi_set_css_power_state(read_mpidr(), + cpu_pwr_state, + cluster_pwr_state, + system_pwr_state); +} + +static void sunxi_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) { + gicv2_distif_init(); + } + if (is_local_state_off(CPU_PWR_STATE(target_state))) { + gicv2_pcpu_distif_init(); + gicv2_cpuif_enable(); + } +} + +static void __dead2 sunxi_system_off(void) +{ + uint32_t ret; + + gicv2_cpuif_disable(); + + /* Send the power down request to the SCP. */ + ret = scpi_sys_power_state(scpi_system_shutdown); + if (ret != SCP_OK) { + ERROR("PSCI: SCPI %s failed: %d\n", "shutdown", ret); + } + + psci_power_down_wfi(); +} + +static void __dead2 sunxi_system_reset(void) +{ + uint32_t ret; + + gicv2_cpuif_disable(); + + /* Send the system reset request to the SCP. */ + ret = scpi_sys_power_state(scpi_system_reboot); + if (ret != SCP_OK) { + ERROR("PSCI: SCPI %s failed: %d\n", "reboot", ret); + } + + psci_power_down_wfi(); +} + +static int sunxi_system_reset2(int is_vendor, int reset_type, u_register_t cookie) +{ + uint32_t ret; + + if (is_vendor || (reset_type != PSCI_RESET2_SYSTEM_WARM_RESET)) + return PSCI_E_NOT_SUPPORTED; + + gicv2_cpuif_disable(); + + /* Send the system reset request to the SCP. */ + ret = scpi_sys_power_state(scpi_system_reset); + if (ret != SCP_OK) { + ERROR("PSCI: SCPI %s failed: %d\n", "reset", ret); + return PSCI_E_INVALID_PARAMS; + } + + psci_power_down_wfi(); + + /* + * Should not reach here. + * However sunxi_system_reset2 has to return some value + * according to PSCI v1.1 spec. + */ + return PSCI_E_SUCCESS; +} + +static int sunxi_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + unsigned int power_level = psci_get_pstate_pwrlvl(power_state); + unsigned int state_id = psci_get_pstate_id(power_state); + unsigned int type = psci_get_pstate_type(power_state); + unsigned int i; + + assert(req_state != NULL); + + if (power_level > PLAT_MAX_PWR_LVL) { + return PSCI_E_INVALID_PARAMS; + } + + if (type == PSTATE_TYPE_STANDBY) { + return PSCI_E_INVALID_PARAMS; + } + + /* Pass through the requested PSCI state as-is. */ + for (i = 0; i <= power_level; ++i) { + unsigned int local_pstate = state_id & PLAT_LOCAL_PSTATE_MASK; + + req_state->pwr_domain_state[i] = local_pstate; + state_id >>= PLAT_LOCAL_PSTATE_WIDTH; + } + + /* Higher power domain levels should all remain running */ + for (; i <= PLAT_MAX_PWR_LVL; ++i) { + req_state->pwr_domain_state[i] = PSCI_LOCAL_STATE_RUN; + } + + return PSCI_E_SUCCESS; +} + +static void sunxi_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + assert(req_state != NULL); + + for (unsigned int i = 0; i <= PLAT_MAX_PWR_LVL; ++i) { + req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; + } +} + +static const plat_psci_ops_t sunxi_scpi_psci_ops = { + .cpu_standby = sunxi_cpu_standby, + .pwr_domain_on = sunxi_pwr_domain_on, + .pwr_domain_off = sunxi_pwr_domain_off, + .pwr_domain_suspend = sunxi_pwr_domain_off, + .pwr_domain_on_finish = sunxi_pwr_domain_on_finish, + .pwr_domain_suspend_finish = sunxi_pwr_domain_on_finish, + .system_off = sunxi_system_off, + .system_reset = sunxi_system_reset, + .system_reset2 = sunxi_system_reset2, + .validate_power_state = sunxi_validate_power_state, + .validate_ns_entrypoint = sunxi_validate_ns_entrypoint, + .get_sys_suspend_power_state = sunxi_get_sys_suspend_power_state, +}; + +int sunxi_set_scpi_psci_ops(const plat_psci_ops_t **psci_ops) +{ + *psci_ops = &sunxi_scpi_psci_ops; + + /* Check for a valid SCP firmware. */ + if (mmio_read_32(SUNXI_SCP_BASE) != SCP_FIRMWARE_MAGIC) { + return -1; + } + + /* Program SCP exception vectors to the firmware entrypoint. */ + for (unsigned int i = OR1K_VEC_FIRST; i <= OR1K_VEC_LAST; ++i) { + uint32_t vector = SUNXI_SRAM_A2_BASE + OR1K_VEC_ADDR(i); + uint32_t offset = SUNXI_SCP_BASE - vector; + + mmio_write_32(vector, offset >> 2); + } + + /* Take the SCP out of reset. */ + mmio_setbits_32(SUNXI_R_CPUCFG_BASE, BIT(0)); + + /* Wait for the SCP firmware to boot. */ + return scpi_wait_ready(); +} diff --git a/plat/allwinner/common/sunxi_security.c b/plat/allwinner/common/sunxi_security.c new file mode 100644 index 0000000..98b91c3 --- /dev/null +++ b/plat/allwinner/common/sunxi_security.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <lib/mmio.h> + +#include <sunxi_ccu.h> +#include <sunxi_mmap.h> +#include <sunxi_private.h> +#include <sunxi_spc.h> + +#define DMA_SEC_REG 0x20 + +/* + * Setup the peripherals to be accessible by non-secure world. + * This will not work for the Secure Peripherals Controller (SPC) unless + * a fuse it burnt (seems to be an erratum), but we do it nevertheless, + * to allow booting on boards using secure boot. + */ +void sunxi_security_setup(void) +{ + int i; + + INFO("Configuring SPC Controller\n"); + /* SPC setup: set all devices to non-secure */ + for (i = 0; i < SUNXI_SPC_NUM_PORTS; i++) + mmio_write_32(SUNXI_SPC_DECPORT_SET_REG(i), 0xffffffff); + + /* set MBUS clocks, bus clocks (AXI/AHB/APB) and PLLs to non-secure */ + mmio_write_32(SUNXI_CCU_SEC_SWITCH_REG, 0x7); + + /* Set R_PRCM bus clocks to non-secure */ + mmio_write_32(SUNXI_R_PRCM_SEC_SWITCH_REG, 0x1); + + /* Set all DMA channels (16 max.) to non-secure */ + mmio_write_32(SUNXI_DMA_BASE + DMA_SEC_REG, 0xffff); +} diff --git a/plat/allwinner/common/sunxi_topology.c b/plat/allwinner/common/sunxi_topology.c new file mode 100644 index 0000000..45be1e0 --- /dev/null +++ b/plat/allwinner/common/sunxi_topology.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <platform_def.h> + +#include <arch.h> +#include <plat/common/platform.h> + +static const unsigned char plat_power_domain_tree_desc[PLAT_MAX_PWR_LVL + 1] = { + /* One root node for the SoC */ + 1, + /* One node for each cluster */ + PLATFORM_CLUSTER_COUNT, + /* One set of CPUs per cluster */ + PLATFORM_MAX_CPUS_PER_CLUSTER, +}; + +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_MAX_CPUS_PER_CLUSTER) { + return -1; + } + + return cluster * PLATFORM_MAX_CPUS_PER_CLUSTER + core; +} + +const unsigned char *plat_get_power_domain_tree_desc(void) +{ + return plat_power_domain_tree_desc; +} |