diff --git a/docs/plat/allwinner.rst b/docs/plat/allwinner.rst index d82380ddf..b6969896a 100644 --- a/docs/plat/allwinner.rst +++ b/docs/plat/allwinner.rst @@ -5,22 +5,8 @@ Trusted Firmware-A (TF-A) implements the EL3 firmware layer for Allwinner SoCs with ARMv8 cores. Only BL31 is used to provide proper EL3 setup and PSCI runtime services. -U-Boot's SPL acts as a loader, loading both BL31 and BL33 (typically U-Boot). -Loading is done from SD card, eMMC or SPI flash, also via an USB debug -interface (FEL). - -BL31 lives in SRAM A2, which is documented to be accessible from secure -world only. - -Current limitations: - -- Missing PMIC support - -After building bl31.bin, the binary must be fed to the U-Boot build system -to include it in the FIT image that the SPL loader will process. -bl31.bin can be either copied (or sym-linked) into U-Boot's root directory, -or the environment variable BL31 must contain the binary's path. -See the respective `U-Boot documentation`_ for more details. +Building TF-A +------------- To build for machines with an A64 or H5 SoC: @@ -34,8 +20,75 @@ To build for machines with an H6 SoC: make CROSS_COMPILE=aarch64-linux-gnu- PLAT=sun50i_h6 DEBUG=1 bl31 +To build for machines with an H616 or H313 SoC: + +.. code:: shell + + make CROSS_COMPILE=aarch64-linux-gnu- PLAT=sun50i_h616 DEBUG=1 bl31 + + +Installation +------------ + +U-Boot's SPL acts as a loader, loading both BL31 and BL33 (typically U-Boot). +Loading is done from SD card, eMMC or SPI flash, also via an USB debug +interface (FEL). + +After building bl31.bin, the binary must be fed to the U-Boot build system +to include it in the FIT image that the SPL loader will process. +bl31.bin can be either copied (or sym-linked) into U-Boot's root directory, +or the environment variable BL31 must contain the binary's path. +See the respective `U-Boot documentation`_ for more details. + .. _U-Boot documentation: https://gitlab.denx.de/u-boot/u-boot/-/blob/master/board/sunxi/README.sunxi64 +Memory layout +------------- + +A64, H5 and H6 SoCs +~~~~~~~~~~~~~~~~~~~ + +BL31 lives in SRAM A2, which is documented to be accessible from secure +world only. Since this SRAM region is very limited (48 KB), we take +several measures to reduce memory consumption. One of them is to confine +BL31 to only 28 bits of virtual address space, which reduces the number +of required page tables (each occupying 4KB of memory). +The mapping we use on those SoCs is as follows: + +:: + + 0 64K 16M 1GB 1G+160M physical address + +-+------+-+---+------+--...---+-------+----+------+---------- + |B| |S|///| |//...///| |////| | + |R| SRAM |C|///| dev |//...///| (sec) |////| BL33 | DRAM ... + |O| |P|///| MMIO |//...///| DRAM |////| | + |M| | |///| |//...///| (32M) |////| | + +-+------+-+---+------+--...---+-------+----+------+---------- + | | | | | | / / / / + | | | | | | / / / / + | | | | | | / / / / + | | | | | | / // / + | | | | | | / / / + +-+------+-+---+------+--+-------+------+ + |B| |S|///| |//| | | + |R| SRAM |C|///| dev |//| sec | BL33 | + |O| |P|///| MMIO |//| DRAM | | + |M| | |///| |//| | | + +-+------+-+---+------+--+-------+------+ + 0 64K 16M 160M 192M 256M virtual address + + +H616 SoC +~~~~~~~~ + +The H616 lacks the secure SRAM region present on the other SoCs, also +lacks the "ARISC" management processor (SCP) we use. BL31 thus needs to +run from DRAM, which prevents our compressed virtual memory map described +above. Since running in DRAM also lifts the restriction of the limited +SRAM size, we use the normal 1:1 mapping with 32 bits worth of virtual +address space. So the virtual addresses used in BL31 match the physical +addresses as presented above. + Trusted OS dispatcher --------------------- diff --git a/plat/allwinner/common/allwinner-common.mk b/plat/allwinner/common/allwinner-common.mk index da83b5e17..61ae9b6fb 100644 --- a/plat/allwinner/common/allwinner-common.mk +++ b/plat/allwinner/common/allwinner-common.mk @@ -85,9 +85,6 @@ PROGRAMMABLE_RESET_ADDRESS := 1 # Allow mapping read-only data as execute-never. SEPARATE_CODE_AND_RODATA := 1 -# Put NOBITS memory in SRAM A1, overwriting U-Boot's SPL. -SEPARATE_NOBITS_REGION := 1 - # BL31 gets loaded alongside BL33 (U-Boot) by U-Boot's SPL RESET_TO_BL31 := 1 diff --git a/plat/allwinner/common/include/platform_def.h b/plat/allwinner/common/include/platform_def.h index 93720fff2..4893368c2 100644 --- a/plat/allwinner/common/include/platform_def.h +++ b/plat/allwinner/common/include/platform_def.h @@ -13,18 +13,36 @@ #include +/* The SCP firmware is allocated the last 16KiB of SRAM A2. */ +#define SUNXI_SCP_SIZE 0x4000 + +#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 + 0x4000) #define BL31_LIMIT (SUNXI_SRAM_A2_BASE + \ SUNXI_SRAM_A2_SIZE - SUNXI_SCP_SIZE) - -/* The SCP firmware is allocated the last 16KiB of SRAM A2. */ #define SUNXI_SCP_BASE BL31_LIMIT -#define SUNXI_SCP_SIZE 0x4000 /* 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 + SUNXI_DRAM_SEC_SIZE) + +#endif /* SUNXI_BL31_IN_DRAM */ + /* How much memory to reserve as secure for BL32, if configured */ #define SUNXI_DRAM_SEC_SIZE (32U << 20) @@ -35,7 +53,6 @@ #define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT) #define MAX_MMAP_REGIONS (3 + PLATFORM_MMAP_REGIONS) -#define MAX_XLAT_TABLES 1 #define PLAT_CSS_SCP_COM_SHARED_MEM_BASE \ (SUNXI_SRAM_A2_BASE + SUNXI_SRAM_A2_SIZE - 0x200) @@ -50,7 +67,6 @@ PLATFORM_CORE_COUNT) #define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) -#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 28) #define PLATFORM_CLUSTER_COUNT U(1) #define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER_COUNT * \ diff --git a/plat/allwinner/common/include/sunxi_def.h b/plat/allwinner/common/include/sunxi_def.h index 73c445386..f0368167c 100644 --- a/plat/allwinner/common/include/sunxi_def.h +++ b/plat/allwinner/common/include/sunxi_def.h @@ -17,5 +17,6 @@ #define SUNXI_SOC_A64 0x1689 #define SUNXI_SOC_H5 0x1718 #define SUNXI_SOC_H6 0x1728 +#define SUNXI_SOC_H616 0x1823 #endif /* SUNXI_DEF_H */ diff --git a/plat/allwinner/common/include/sunxi_private.h b/plat/allwinner/common/include/sunxi_private.h index b68d23f3d..6cf46704f 100644 --- a/plat/allwinner/common/include/sunxi_private.h +++ b/plat/allwinner/common/include/sunxi_private.h @@ -41,4 +41,12 @@ 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); +#ifdef SUNXI_BL31_IN_DRAM +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/sunxi_bl31_setup.c b/plat/allwinner/common/sunxi_bl31_setup.c index b619b18ed..72bfbd966 100644 --- a/plat/allwinner/common/sunxi_bl31_setup.c +++ b/plat/allwinner/common/sunxi_bl31_setup.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -52,7 +54,7 @@ static void *sunxi_find_dtb(void) uint64_t *u_boot_base; int i; - u_boot_base = (void *)(SUNXI_DRAM_VIRT_BASE + SUNXI_DRAM_SEC_SIZE); + u_boot_base = (void *)SUNXI_BL33_VIRT_BASE; for (i = 0; i < 2048 / sizeof(uint64_t); i++) { uint32_t *dtb_base; @@ -123,6 +125,9 @@ void bl31_platform_setup(void) case SUNXI_SOC_H6: soc_name = "H6"; break; + case SUNXI_SOC_H616: + soc_name = "H616"; + break; default: soc_name = "unknown"; break; @@ -172,6 +177,8 @@ void bl31_platform_setup(void) sunxi_pmic_setup(soc_id, fdt); + sunxi_prepare_dtb(fdt); + INFO("BL31: Platform setup done\n"); } diff --git a/plat/allwinner/common/sunxi_common.c b/plat/allwinner/common/sunxi_common.c index 5b536a043..d47d3605b 100644 --- a/plat/allwinner/common/sunxi_common.c +++ b/plat/allwinner/common/sunxi_common.c @@ -6,13 +6,9 @@ #include -#include - -#include #include #include #include -#include #include #include @@ -21,16 +17,16 @@ static const mmap_region_t sunxi_mmap[PLATFORM_MMAP_REGIONS + 1] = { MAP_REGION_FLAT(SUNXI_SRAM_BASE, SUNXI_SRAM_SIZE, MT_RW_DATA | MT_SECURE), +#ifdef SUNXI_SCP_BASE MAP_REGION_FLAT(SUNXI_SCP_BASE, SUNXI_SCP_SIZE, MT_DEVICE | MT_RW | MT_SECURE | MT_EXECUTE_NEVER), +#endif MAP_REGION_FLAT(SUNXI_DEV_BASE, SUNXI_DEV_SIZE, MT_DEVICE | MT_RW | MT_SECURE | MT_EXECUTE_NEVER), MAP_REGION(SUNXI_DRAM_BASE, SUNXI_DRAM_VIRT_BASE, SUNXI_DRAM_SEC_SIZE, MT_RW_DATA | MT_SECURE), - MAP_REGION(PRELOADED_BL33_BASE, - SUNXI_DRAM_VIRT_BASE + SUNXI_DRAM_SEC_SIZE, - SUNXI_DRAM_MAP_SIZE, - MT_RO_DATA | MT_NS), + MAP_REGION(PRELOADED_BL33_BASE, SUNXI_BL33_VIRT_BASE, + SUNXI_DRAM_MAP_SIZE, MT_RW_DATA | MT_NS), {}, }; @@ -116,6 +112,7 @@ int sunxi_init_platform_r_twi(uint16_t socid, bool use_rsb) 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; @@ -130,7 +127,7 @@ int sunxi_init_platform_r_twi(uint16_t socid, bool use_rsb) } /* un-gate R_PIO clock */ - if (socid != SUNXI_SOC_H6) + 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 */ @@ -143,7 +140,7 @@ int sunxi_init_platform_r_twi(uint16_t socid, bool use_rsb) mmio_clrsetbits_32(SUNXI_R_PIO_BASE + 0x1c, 0x0fU, 0x5U); /* un-gate clock */ - if (socid != SUNXI_SOC_H6) + 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)); @@ -154,50 +151,3 @@ int sunxi_init_platform_r_twi(uint16_t socid, bool use_rsb) return 0; } - -/* This lock synchronises access to the arisc management processor. */ -DEFINE_BAKERY_LOCK(arisc_lock); - -/* - * Tell the "arisc" SCP core (an OpenRISC core) to execute some code. - * We don't have any service running there, so we place some OpenRISC code - * in SRAM, put the address of that into the reset vector and release the - * arisc reset line. The SCP will execute that code and pull the line up again. - */ -void sunxi_execute_arisc_code(uint32_t *code, size_t size, uint16_t param) -{ - uintptr_t arisc_reset_vec = SUNXI_SRAM_A2_BASE + 0x100; - - do { - bakery_lock_get(&arisc_lock); - /* Wait until the arisc is in reset state. */ - if (!(mmio_read_32(SUNXI_R_CPUCFG_BASE) & BIT(0))) - break; - - bakery_lock_release(&arisc_lock); - } while (1); - - /* Patch up the code to feed in an input parameter. */ - code[0] = (code[0] & ~0xffff) | param; - clean_dcache_range((uintptr_t)code, size); - - /* - * The OpenRISC unconditional branch has opcode 0, the branch offset - * is in the lower 26 bits, containing the distance to the target, - * in instruction granularity (32 bits). - */ - mmio_write_32(arisc_reset_vec, ((uintptr_t)code - arisc_reset_vec) / 4); - clean_dcache_range(arisc_reset_vec, 4); - - /* De-assert the arisc reset line to let it run. */ - mmio_setbits_32(SUNXI_R_CPUCFG_BASE, BIT(0)); - - /* - * We release the lock here, although the arisc is still busy. - * But as long as it runs, the reset line is high, so other users - * won't leave the loop above. - * Once it has finished, the code is supposed to clear the reset line, - * to signal this to other users. - */ - bakery_lock_release(&arisc_lock); -} diff --git a/plat/allwinner/common/sunxi_cpu_ops.c b/plat/allwinner/common/sunxi_cpu_ops.c index 43c03ac51..420b507ab 100644 --- a/plat/allwinner/common/sunxi_cpu_ops.c +++ b/plat/allwinner/common/sunxi_cpu_ops.c @@ -19,10 +19,6 @@ #include #include -#ifndef SUNXI_CPUIDLE_EN_REG -#include -#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) @@ -67,32 +63,6 @@ static void sunxi_cpu_off(u_register_t mpidr) sunxi_cpu_disable_power(cluster, core); } -void sunxi_cpu_power_off_self(void) -{ - u_register_t mpidr = read_mpidr(); - unsigned int core = MPIDR_AFFLVL0_VAL(mpidr); - - /* Simplifies assembly, all SoCs so far are single cluster anyway. */ - assert(MPIDR_AFFLVL1_VAL(mpidr) == 0); - -#ifdef SUNXI_CPUIDLE_EN_REG - /* Enable the CPUIDLE hardware (only really needs to be done once). */ - mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0x16aa0000); - mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0xaa160001); - - /* Trigger power off for this core. */ - mmio_write_32(SUNXI_CORE_CLOSE_REG, BIT_32(core)); -#else - /* - * If we are supposed to turn ourself off, tell the arisc SCP - * to do that work for us. The code expects the core mask to be - * patched into the first instruction. - */ - sunxi_execute_arisc_code(arisc_core_off, sizeof(arisc_core_off), - BIT_32(core)); -#endif -} - void sunxi_cpu_on(u_register_t mpidr) { unsigned int cluster = MPIDR_AFFLVL1_VAL(mpidr); diff --git a/plat/allwinner/sun50i_a64/platform.mk b/plat/allwinner/sun50i_a64/platform.mk index 5f4103580..e3c7c529b 100644 --- a/plat/allwinner/sun50i_a64/platform.mk +++ b/plat/allwinner/sun50i_a64/platform.mk @@ -12,3 +12,6 @@ BL31_SOURCES += drivers/allwinner/axp/axp803.c \ FDT_ASSUME_MASK := "(ASSUME_LATEST | ASSUME_NO_ROLLBACK | ASSUME_LIBFDT_ORDER)" $(eval $(call add_define,FDT_ASSUME_MASK)) + +# Put NOBITS memory in SRAM A1, overwriting U-Boot's SPL. +SEPARATE_NOBITS_REGION := 1 diff --git a/plat/allwinner/sun50i_a64/sunxi_power.c b/plat/allwinner/sun50i_a64/sunxi_power.c index 80a69c340..0fdb62d05 100644 --- a/plat/allwinner/sun50i_a64/sunxi_power.c +++ b/plat/allwinner/sun50i_a64/sunxi_power.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -205,3 +206,55 @@ void sunxi_power_down(void) } } + +/* This lock synchronises access to the arisc management processor. */ +static DEFINE_BAKERY_LOCK(arisc_lock); + +/* + * If we are supposed to turn ourself off, tell the arisc SCP to do that + * work for us. Without any SCPI provider running there, we place some + * OpenRISC code into SRAM, put the address of that into the reset vector + * and release the arisc reset line. The SCP will wait for the core to enter + * WFI, then execute that code and pull the line up again. + * The code expects the core mask to be patched into the first instruction. + */ +void sunxi_cpu_power_off_self(void) +{ + u_register_t mpidr = read_mpidr(); + unsigned int core = MPIDR_AFFLVL0_VAL(mpidr); + uintptr_t arisc_reset_vec = SUNXI_SRAM_A2_BASE + 0x100; + uint32_t *code = arisc_core_off; + + do { + bakery_lock_get(&arisc_lock); + /* Wait until the arisc is in reset state. */ + if (!(mmio_read_32(SUNXI_R_CPUCFG_BASE) & BIT(0))) + break; + + bakery_lock_release(&arisc_lock); + } while (1); + + /* Patch up the code to feed in an input parameter. */ + code[0] = (code[0] & ~0xffff) | BIT_32(core); + clean_dcache_range((uintptr_t)code, sizeof(arisc_core_off)); + + /* + * The OpenRISC unconditional branch has opcode 0, the branch offset + * is in the lower 26 bits, containing the distance to the target, + * in instruction granularity (32 bits). + */ + mmio_write_32(arisc_reset_vec, ((uintptr_t)code - arisc_reset_vec) / 4); + clean_dcache_range(arisc_reset_vec, 4); + + /* De-assert the arisc reset line to let it run. */ + mmio_setbits_32(SUNXI_R_CPUCFG_BASE, BIT(0)); + + /* + * We release the lock here, although the arisc is still busy. + * But as long as it runs, the reset line is high, so other users + * won't leave the loop above. + * Once it has finished, the code is supposed to clear the reset line, + * to signal this to other users. + */ + bakery_lock_release(&arisc_lock); +} diff --git a/plat/allwinner/sun50i_h6/include/core_off_arisc.h b/plat/allwinner/sun50i_h6/include/core_off_arisc.h deleted file mode 100644 index 63a5d8d96..000000000 --- a/plat/allwinner/sun50i_h6/include/core_off_arisc.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -static uint32_t arisc_core_off[] = { - 0x18600000, /* l.movhi r3, */ - 0x18000000, /* l.movhi r0, 0x0 */ - 0x19a00901, /* l.movhi r13, 0x901 */ - 0x84ad0080, /* l.lwz r5, 0x80(r13) */ - 0xe0a51803, /* l.and r5, r5, r3 */ - 0xe4050000, /* l.sfeq r5, r0 */ - 0x13fffffd, /* l.bf -12 */ - 0xb8c30050, /* l.srli r6, r3, 16 */ - - 0xbc060001, /* l.sfeqi r6, 1 */ - 0x10000005, /* l.bf +20 */ - 0x19a00700, /* l.movhi r13, 0x700 */ - 0x84ad0444, /* l.lwz r5, 0x0444(r13) */ - 0xe0a53004, /* l.or r5, r5, r6 */ - 0xd40d2c44, /* l.sw 0x0444(r13), r5 */ - - 0x84ad0440, /* l.lwz r5, 0x0440(r13) */ - 0xacc6ffff, /* l.xori r6, r6, -1 */ - 0xe0a53003, /* l.and r5, r5, r6 */ - 0xd40d2c40, /* l.sw 0x0440(r13), r5 */ - - 0xe0c3000f, /* l.ff1 r6, r3 */ - 0x9cc6ffef, /* l.addi r6, r6, -17 */ - 0xb8c60002, /* l.slli r6, r6, 2 */ - 0xe0c66800, /* l.add r6, r6, r13 */ - 0xa8a000ff, /* l.ori r5, r0, 0xff */ - 0xd4062c50, /* l.sw 0x0450(r6), r5 */ - - 0xd40d0400, /* l.sw 0x0400(r13), r0 */ - 0x03ffffff, /* l.j -1 */ - 0x15000000, /* l.nop */ -}; diff --git a/plat/allwinner/sun50i_h6/platform.mk b/plat/allwinner/sun50i_h6/platform.mk index 1c98919b1..e13e8cbd4 100644 --- a/plat/allwinner/sun50i_h6/platform.mk +++ b/plat/allwinner/sun50i_h6/platform.mk @@ -9,3 +9,6 @@ include plat/allwinner/common/allwinner-common.mk BL31_SOURCES += drivers/allwinner/axp/axp805.c \ drivers/allwinner/sunxi_rsb.c + +# Put NOBITS memory in SRAM A1, overwriting U-Boot's SPL. +SEPARATE_NOBITS_REGION := 1 diff --git a/plat/allwinner/sun50i_h6/sunxi_power.c b/plat/allwinner/sun50i_h6/sunxi_power.c index a7865a5d4..d298e6b8a 100644 --- a/plat/allwinner/sun50i_h6/sunxi_power.c +++ b/plat/allwinner/sun50i_h6/sunxi_power.c @@ -10,7 +10,9 @@ #include #include #include +#include +#include #include #include #include @@ -102,3 +104,16 @@ void sunxi_power_down(void) break; } } + +void sunxi_cpu_power_off_self(void) +{ + u_register_t mpidr = read_mpidr(); + unsigned int core = MPIDR_AFFLVL0_VAL(mpidr); + + /* Enable the CPUIDLE hardware (only really needs to be done once). */ + mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0x16aa0000); + mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0xaa160001); + + /* Trigger power off for this core. */ + mmio_write_32(SUNXI_CORE_CLOSE_REG, BIT_32(core)); +} diff --git a/plat/allwinner/sun50i_h616/include/sunxi_ccu.h b/plat/allwinner/sun50i_h616/include/sunxi_ccu.h new file mode 100644 index 000000000..85fbb9080 --- /dev/null +++ b/plat/allwinner/sun50i_h616/include/sunxi_ccu.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SUNXI_CCU_H +#define SUNXI_CCU_H + +#define SUNXI_CCU_SEC_SWITCH_REG (SUNXI_CCU_BASE + 0x0f00) + +#define SUNXI_R_PRCM_SEC_SWITCH_REG (SUNXI_R_PRCM_BASE + 0x0290) + +#endif /* SUNXI_CCU_H */ diff --git a/plat/allwinner/sun50i_h616/include/sunxi_cpucfg.h b/plat/allwinner/sun50i_h616/include/sunxi_cpucfg.h new file mode 100644 index 000000000..a63755415 --- /dev/null +++ b/plat/allwinner/sun50i_h616/include/sunxi_cpucfg.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017-2020, ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SUNXI_CPUCFG_H +#define SUNXI_CPUCFG_H + +#include + +/* 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) +#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_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_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) + +#endif /* SUNXI_CPUCFG_H */ diff --git a/plat/allwinner/sun50i_h616/include/sunxi_mmap.h b/plat/allwinner/sun50i_h616/include/sunxi_mmap.h new file mode 100644 index 000000000..3b4f4a02e --- /dev/null +++ b/plat/allwinner/sun50i_h616/include/sunxi_mmap.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SUNXI_MMAP_H +#define SUNXI_MMAP_H + +/* Memory regions */ +#define SUNXI_ROM_BASE 0x00000000 +#define SUNXI_ROM_SIZE 0x00010000 +#define SUNXI_SRAM_BASE 0x00020000 +#define SUNXI_SRAM_SIZE 0x00038000 +#define SUNXI_SRAM_A1_BASE 0x00020000 +#define SUNXI_SRAM_A1_SIZE 0x00008000 +#define SUNXI_SRAM_C_BASE 0x00028000 +#define SUNXI_SRAM_C_SIZE 0x00030000 +#define SUNXI_DEV_BASE 0x01000000 +#define SUNXI_DEV_SIZE 0x09000000 +#define SUNXI_DRAM_BASE 0x40000000 +#define SUNXI_DRAM_VIRT_BASE SUNXI_DRAM_BASE + +/* Memory-mapped devices */ +#define SUNXI_SYSCON_BASE 0x03000000 +#define SUNXI_CCU_BASE 0x03001000 +#define SUNXI_DMA_BASE 0x03002000 +#define SUNXI_SID_BASE 0x03006000 +#define SUNXI_SPC_BASE 0x03008000 +#define SUNXI_WDOG_BASE 0x030090a0 +#define SUNXI_PIO_BASE 0x0300b000 +#define SUNXI_GICD_BASE 0x03021000 +#define SUNXI_GICC_BASE 0x03022000 +#define SUNXI_UART0_BASE 0x05000000 +#define SUNXI_SPI0_BASE 0x05010000 +#define SUNXI_R_CPUCFG_BASE 0x07000400 +#define SUNXI_R_PRCM_BASE 0x07010000 +//#define SUNXI_R_WDOG_BASE 0x07020400 +#define SUNXI_R_WDOG_BASE SUNXI_WDOG_BASE +#define SUNXI_R_PIO_BASE 0x07022000 +#define SUNXI_R_UART_BASE 0x07080000 +#define SUNXI_R_I2C_BASE 0x07081400 +#define SUNXI_R_RSB_BASE 0x07083000 +#define SUNXI_CPUCFG_BASE 0x09010000 + +#endif /* SUNXI_MMAP_H */ diff --git a/plat/allwinner/sun50i_h616/include/sunxi_spc.h b/plat/allwinner/sun50i_h616/include/sunxi_spc.h new file mode 100644 index 000000000..0f5965bee --- /dev/null +++ b/plat/allwinner/sun50i_h616/include/sunxi_spc.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SUNXI_SPC_H +#define SUNXI_SPC_H + +#define SUNXI_SPC_NUM_PORTS 14 + +#define SUNXI_SPC_DECPORT_STA_REG(p) (SUNXI_SPC_BASE + 0x0000 + 0x10 * (p)) +#define SUNXI_SPC_DECPORT_SET_REG(p) (SUNXI_SPC_BASE + 0x0004 + 0x10 * (p)) +#define SUNXI_SPC_DECPORT_CLR_REG(p) (SUNXI_SPC_BASE + 0x0008 + 0x10 * (p)) + +#endif /* SUNXI_SPC_H */ diff --git a/plat/allwinner/sun50i_h616/platform.mk b/plat/allwinner/sun50i_h616/platform.mk new file mode 100644 index 000000000..fc09af7c1 --- /dev/null +++ b/plat/allwinner/sun50i_h616/platform.mk @@ -0,0 +1,24 @@ +# +# Copyright (c) 2017-2020, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# Without a management processor there is no SCPI support. +SUNXI_PSCI_USE_SCPI := 0 +SUNXI_PSCI_USE_NATIVE := 1 + +# The differences between the platforms are covered by the include files. +include plat/allwinner/common/allwinner-common.mk + +# the above could be overwritten on the command line +ifeq (${SUNXI_PSCI_USE_SCPI}, 1) + $(error "H616 does not support SCPI PSCI ops") +endif + +BL31_SOURCES += drivers/allwinner/axp/axp805.c \ + drivers/allwinner/sunxi_rsb.c \ + common/fdt_fixup.c \ + ${AW_PLAT}/${PLAT}/prepare_dtb.c + +$(eval $(call add_define,SUNXI_BL31_IN_DRAM)) diff --git a/plat/allwinner/sun50i_h616/prepare_dtb.c b/plat/allwinner/sun50i_h616/prepare_dtb.c new file mode 100644 index 000000000..e94b0b4be --- /dev/null +++ b/plat/allwinner/sun50i_h616/prepare_dtb.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021, ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include +#include + +#include + +void sunxi_prepare_dtb(void *fdt) +{ + int ret; + + if (fdt == NULL || fdt_check_header(fdt) != 0) { + return; + } + ret = fdt_open_into(fdt, fdt, 0x100000); + if (ret < 0) { + ERROR("Preparing devicetree at %p: error %d\n", fdt, ret); + return; + } + + /* 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"); + return; + } + + ret = fdt_pack(fdt); + if (ret < 0) { + ERROR("Failed to pack devicetree at %p: error %d\n", + fdt, ret); + } else { + clean_dcache_range((uintptr_t)fdt, fdt_blob_size(fdt)); + INFO("Changed devicetree to reserve BL31 memory.\n"); + } +} diff --git a/plat/allwinner/sun50i_h616/sunxi_power.c b/plat/allwinner/sun50i_h616/sunxi_power.c new file mode 100644 index 000000000..dd6ebba9a --- /dev/null +++ b/plat/allwinner/sun50i_h616/sunxi_power.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017-2020, ARM Limited. All rights reserved. + * Copyright (c) 2018, Icenowy Zheng + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define AXP305_I2C_ADDR 0x36 +#define AXP305_HW_ADDR 0x745 +#define AXP305_RT_ADDR 0x3a + +static enum pmic_type { + UNKNOWN, + AXP305, +} pmic; + +int axp_read(uint8_t reg) +{ + return rsb_read(AXP305_RT_ADDR, reg); +} + +int axp_write(uint8_t reg, uint8_t val) +{ + return rsb_write(AXP305_RT_ADDR, reg, val); +} + +static int rsb_init(void) +{ + int ret; + + ret = rsb_init_controller(); + if (ret) + return ret; + + /* Switch to the recommended 3 MHz bus clock. */ + ret = rsb_set_bus_speed(SUNXI_OSC24M_CLK_IN_HZ, 3000000); + if (ret) + return ret; + + /* Initiate an I2C transaction to switch the PMIC to RSB mode. */ + ret = rsb_set_device_mode(AXP20X_MODE_RSB << 16 | AXP20X_MODE_REG << 8); + if (ret) + return ret; + + /* Associate the 8-bit runtime address with the 12-bit bus address. */ + ret = rsb_assign_runtime_address(AXP305_HW_ADDR, AXP305_RT_ADDR); + if (ret) + return ret; + + return axp_check_id(); +} + +int sunxi_pmic_setup(uint16_t socid, const void *fdt) +{ + int ret; + + INFO("PMIC: Probing AXP305 on RSB\n"); + + ret = sunxi_init_platform_r_twi(socid, true); + if (ret) { + INFO("Could not init platform bus: %d\n", ret); + return ret; + } + + ret = rsb_init(); + if (ret) { + INFO("Could not init RSB: %d\n", ret); + return ret; + } + + pmic = AXP305; + axp_setup_regulators(fdt); + + /* Switch the PMIC back to I2C mode. */ + ret = axp_write(AXP20X_MODE_REG, AXP20X_MODE_I2C); + if (ret) + return ret; + + return 0; +} + +void sunxi_power_down(void) +{ + switch (pmic) { + case AXP305: + /* Re-initialise after rich OS might have used it. */ + sunxi_init_platform_r_twi(SUNXI_SOC_H616, true); + rsb_init(); + axp_power_off(); + break; + default: + break; + } +} + +void sunxi_cpu_power_off_self(void) +{ + u_register_t mpidr = read_mpidr(); + unsigned int core = MPIDR_AFFLVL0_VAL(mpidr); + + /* Enable the CPUIDLE hardware (only really needs to be done once). */ + mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0x16aa0000); + mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0xaa160001); + + /* Trigger power off for this core. */ + mmio_write_32(SUNXI_CORE_CLOSE_REG, BIT_32(core)); +}